Projects/[Final] Shopping Mall Project

[QueryDsl] 기간별 판매 통계 조회 메서드 수정하기 - 2부

montmer27 2026. 4. 28. 18:18

[관련 커밋]

https://github.com/all-in-market/mvp-api-server/commit/e8450c8a71b9dfcf9b931b2157f5e6f66409b5a9

 

fix: 판매 통계 집계 시 Tuple에서 DTO projection으로 전환 · all-in-market/mvp-api-server@e8450c8

+ this(sellerId, null, null, totalOrders, totalItems, totalSales, totalRefunds, refundAmount, netSales);

github.com

[지난 이야기]

판매자가 자신의 통계를 기간별로 조회하는 메서드 sellerDailyStatisticsRepository.findRangedStatistics()가 Querydsl 5.x 버전에서 잠재적 NPE 구조를 일으켜 문제가 되었던 DTO 프로젝션 대신 Tuple을 받아오는 방식으로 수정했다.

하지만 튜터님 피드백에서 Tuple을 받아오는 방식이 위험하다는 평을 받아, 다른 방법을 고민해야 하는 상황이다.

https://montmer27.tistory.com/232

 

[QueryDsl] 기간별 판매 통계 조회 메서드 테스트 코드 작성 및 수정하기

무엇을, 왜 했나?판매자가 자신의 통계를 기간별로 조회하는 메서드의 테스트 코드를 작성하는 과정에서 기존 코드에서 QueryDsl 버전에 맞지 않는 구조적 문제를 발견했고, 기존 코드도 함께 수

montmer27.tistory.com

 

[새로운 문제상황]

조건: Tuple 사용은 절대 지양해야 한다.

[목표]

Tuple을 사용하지 않으면서도 집계 기간 파라미터(from, to)의 null값을 허용하는 조회 메서드를 구현해야 한다.

[행동 계획]

1. DTO 프로젝션을 사용하되, 집계 기간 파라미터를 넣지 않아도 되는 DTO가 필요하다

2. 현재 DTO로 사용하고 있는 DailyStatisticsResponse는 LocalDate from과 to를 반드시 요구하므로 다른 DTO가 필요하다.

3. LocalDate from과 to를 받지 않아도 되는 DailyStatisticsResponse의 생성자를 오버로딩하자.

[결과]

아래와 같이 DTO의 생성자를 오버로딩했다.

public DailyStatisticsResponse(Long sellerId, Integer totalOrders, Integer totalItems,
                                   BigDecimal totalSales, Integer totalRefunds,
                                   BigDecimal refundAmount, BigDecimal netSales) {
        this(sellerId, null, null, totalOrders, totalItems, totalSales, totalRefunds, refundAmount, netSales);
    }

그 결과 코드가 줄어들었다(54줄 -> 48줄)

[Before]

[After]

[느낀 점]

Tuple 사용이 너무 간단하고 편리했는데 역시 타입 안전성이라는 문제가 있었다.
DTO 구조상 Projections.constructor() 실행 시 Expressions.constant()에 nullable 필드를 넣어줘야만 했어서 Tuple로 이를 우회하려 했었는데, 단순히 문제가 되는 필드를 받지 않는 DTO를 새로 만들어 주는 것만으로 해결해줄 수 있었다.
DTO를 새로 만드는 게 타입 안전성을 위험에 빠트리는 것보다 낫다는 것을 깨달았다.