perf(account): 내역 목록 태그 조회 N+1 → 배치 단일 쿼리로 개선

list() 에서 항목마다 findTagNamesByEntryId() 를 개별 호출하던 N+1 을
findTagsByEntryIds() 배치 쿼리 1회로 대체.
항목 수가 많을 때 타임아웃/과부하 방지.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-07-02 01:47:08 +09:00
co-authored by Claude Sonnet 4.6
parent 947c7a1f68
commit 190fbe835f
3 changed files with 28 additions and 3 deletions
@@ -53,9 +53,19 @@ public class AccountService {
public List<AccountEntryResponse> list(Long memberId, Integer year, Integer month,
String type, String category, Long walletId, String keyword, Long tagId) {
return mapper.findByMember(memberId, year, month,
blankToNull(type), blankToNull(category), walletId, blankToNull(keyword), tagId).stream()
.map(this::toResponse)
List<AccountEntry> entries = mapper.findByMember(memberId, year, month,
blankToNull(type), blankToNull(category), walletId, blankToNull(keyword), tagId);
if (entries.isEmpty()) return List.of();
// 태그 N+1 방지: 한 번에 모든 entry 의 태그를 조회 후 Map 으로 그룹화
List<Long> ids = entries.stream().map(AccountEntry::getId).toList();
Map<Long, List<String>> tagsByEntry = new HashMap<>();
for (Map<String, Object> row : mapper.findTagsByEntryIds(ids)) {
Long eid = ((Number) row.get("entryId")).longValue();
String name = (String) row.get("tagName");
tagsByEntry.computeIfAbsent(eid, k -> new ArrayList<>()).add(name);
}
return entries.stream()
.map(e -> AccountEntryResponse.from(e, tagsByEntry.getOrDefault(e.getId(), List.of())))
.toList();
}