내가 레거시를 낳았다…
현재 코드는 유지보수 관점을 떠나서, 네이밍, 가독성 측면에서도 너무 안좋은 코드임을 인지하고 있습니다.
제가 생각한 로직을 공유하고, 인지하고 있는 문제점, 그리고 윌리엄이 남겨주신 피드백의 대한 생각 및 공유로 작성해보겠습니다. 리팩터 후에 메모도 따로 남길게요!
💧 로직

우선 일일 상세내역을 조회하기 위해서
저희가 필요한 테이블은 expenditure와 income입니다.
- 제가 여기서 겪었던 문제는 income과 expenditure에 대해서 페이징 처리가 아니라
일일 상세내역에 대한 페이징을 작성해야한다는 것
이었어요.- 즉, 사용자가 2022-08월에 작성한
expenditure와 income의 날짜의 합집합에 해당하는 일일 상세 내역을 보여주어야합니다.
- 예를 들어 사용자가 지출을 20~11 10개, 수입을 15에서 6일 작성하였고, page=1&size=10으로 페이징 요청을하였다면 최신날짜
20~11일에 대한 상세내역을 보여주어야합니다.
- 고민
- 이때 어
차피 한달을 10일씩 자르면 페이징이 최대 4개(31일) 보통 3개(30,29,28)로 짤리니까 그냥 고정된 날짜로 짜를까했는데!!!
, 이경우 유저가 30~21일 중 30일만 작성했다면… 30일에 데이터만 볼 수 있어요.. 그리고 페이징의 개념과 맞지 않는다고 생각했습니다. - 날짜를 합집합하려고 했는데…
querydsl과 jpa는 union을 지원하지 않습니다 …
- 저는 상세내역을 페이징하기 위해 사용자가
2022-08월에 작성한 expenditure와 income의 모든 날짜를 불러온 후 합집합하였습니다.
- 그후 프론트에서 요청한 페이징과 오프셋을 사용하여 필요한 날짜 데이터들을 추출하였습니다.
private List<LocalDate> getPageDate(PageCustomRequest pageCustomRequest, Long userId, LocalDate date) { long offset = pageCustomRequest.getOffset(); int size = pageCustomRequest.getSize(); int year = date.getYear(); int month = date.getMonthValue(); List<LocalDateTime> expenditureTimes = queryFactory.select(expenditure.registerDate) .from(expenditure) .where( expenditure.user.id.eq(userId), expenditure.registerDate.year().eq(year), expenditure.registerDate.month().eq(month) ) .fetch(); List<LocalDateTime> incomeTimes = queryFactory.select(income.registerDate) .from(income) .where( income.user.id.eq(userId), income.registerDate.year().eq(year), income.registerDate.month().eq(month) ) .fetch(); List<LocalDate> expenditureDate = expenditureTimes.stream() .map(LocalDateTime::toLocalDate) .collect(Collectors.toList()); List<LocalDate> incomeDate = incomeTimes.stream() .map(LocalDateTime::toLocalDate) .collect(Collectors.toList()); expenditureDate.addAll(incomeDate); List<LocalDate> dates = expenditureDate.stream() .distinct() .collect(Collectors.toList()); Collections.sort(dates, (d1, d2) -> d1.isAfter(d2) ? -1 : 1); int end = (int)offset + size > dates.size() ? dates.size() : (int)offset + size; return dates.subList(((int)offset), end); }
- 제가 생각하는 해결 방안은 지금처럼 코드에서 좀더 깔끔하게 개선하려고 노력한다.
union이 지원안되니까 native쿼리로 불러와서 정렬한다.
- 네이티브 쿼리는 db vendor 종속적이어서 반대하실 수도 있는데 이부분 고려해보시면 좋을 것같아요, JPA가 추상화가 잘 되어있다고 하지만, JPA도 저는 어차피 벤더 종속적이라고 생각합니다..
- 가장 최악 - 개선 무조건…
public PageCustomImpl<FindDayAccountResponse> findDailyAccount(Long userId, PageCustomRequest pageRequest, LocalDate date) { List<LocalDate> dates = getPageDate(pageRequest, userId, date); List<FindDayAccountResponse> responses = new ArrayList<>(); dates.iterator().forEachRemaining( d -> responses.add(new FindDayAccountResponse(d, getDayIncomeAmount(d), getDayExpenditureAmount(d), getDayDetails(userId, d))) ); Collections.sort(responses, (o1, o2) -> o2.getRegisterDate().getDayOfMonth() - o1.getRegisterDate().getDayOfMonth()); return new PageCustomImpl<>(pageRequest.getPage(), pageRequest, responses); }
- 지금 iterator를 돌려서 getDayDetails가 돌아가고 있습니다.
- 20~11일의 날짜가 있으면 각자의 데이터마다 쿼리를 쏘는거죠.
- 이부분 쿼리 개선해서 between 날짜 이용해서 한 번에 데이터를 쏘도록 개선할 예정입니다.
👍 윌리엄 리뷰 + 사랑해여
- 지금 계층을 보면, service는 정말 단순히 데이터 서빙만 하고 있습니다.
그건 둘째치고 repository에서 service로 넘겨주는 dto를 controller로 반환하는 문제져.
- 이렇게라도 구현한 이유
- 현재 화면에 맞춘 쿼리 작성입니다.
- 원래 사용자 정의 커스텀으로 QueryDsl을 사용하는 class를 만듭니다.
- MemberRepositoryImpl이 SimpleJpaRepository 구현체의 역할을 하는 거죠?
- 근데 현재 AccountBookQueryRepository는 Jpa interface와 묶지 않았습니다 저는
- 쿼리가 화면 종속적이라고 생각했습니다.
- 쿼리를 재사용하기 어렵다고 생각했고, 특정 엔티티 하나의 종속되어 있는 repository가 아니라고 판단했습니다.
- 개선의 의지가 없는이유
- 추후 서비스에서 여러번 끌고 가서 사용할 가능성이 적다고 생각했습니다.
- 현재 우리의 기한으로 오버엔지니어링이라고 생각합니다.
