문제 상황
관리자가 유저요청기록 페이지에 들어가면 전체 데이터를 가져와서 화면에 보여주게 된다.
ApiRequesHistory에는 요청 기록이 담겨있고, Member와 1:1 관계를 가져서 누가 어떤 요청을 날렸고 어떤 응답을 받았는지 기록하게 된다.
조회 쿼리를 살펴보면
이렇게 쿼리가 날아가는데 실제 응답 DTO에서는 Member에 관한 데이터는 email 말고 사용하지 않는다.
즉 9개의 데이터 중 1개만 필요한 상황인데 DB에서 과하게 부르고 있는 문제가 있다. 데이터가 하나라면 상관없지만 더미데이터를 75만 개 넣어뒀으니 불필요한 데이터 호출이 배로 늘어나고 있는 상황.
해결 방법
그래서 DB에 받는 데이터를 DTO에 직접 담아주려고 한다.
아래거 현재 작성된 쿼리문이다.
List<ApiRequestHistory> results = queryFactory
.selectFrom(apiRequestHistory)
.leftJoin(apiRequestHistory.member, member).fetchJoin()
.orderBy(apiRequestHistory.requestDate.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
Entity로 직접 가져와서 서비스에서 DTO로 전부 변환시킨다.
먼저 selectFrom을 나눠서 select에는 DTO를 뽑게, From에는 Entity를 선택하도록 구분시켜야 한다.
List<ApiRequestHistoryResponseDTO> results = queryFactory
.select(Projections.constructor(ApiRequestHistoryResponseDTO.class,
apiRequestHistory.member.id,
apiRequestHistory.requestDate,
member.email,
apiRequestHistory.methodType,
apiRequestHistory.requestContent,
apiRequestHistory.responseContent))
.from(apiRequestHistory)
.leftJoin(apiRequestHistory.member, member)
.orderBy(apiRequestHistory.requestDate.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
여기서 문제가 됐었던 부분이 QueryDsl에서 DTO로 데이터를 조회하는 경우 fetchJoin이 불가능하다고 했다.
https://www.youtube.com/watch?v=zMAX7g6rO_Y&t=591s
자세한 영상은 위 영상을 참고하면 된다.
그래서 나도 fetchJoin을 삭제하고 그냥 leftJoin으로 진행했다.
그리고 Projection.field를 사용하면 DTO의 필드와 DB의 컬럼을 일치시켜 매핑하는 방법이다. 즉 DB에서 가져오는 컬럼 이름과 DTO의 필드 이름이 정확하게 매칭되어야 한다. 기본생성자가 필요하다.
Projection.contructor는 DTO의 생성자를 통해 필드를 매핑하는 방식이다. 필드 이름과는 상관없이 생성자의 매개변수 순서와 타입이 일치해야 한다. 그리고 전체 생성자가 필요하다.
왼쪽이 전체 Entity를 통한 결과, 오른쪽이 DTO로 데이터를 조회한 결과다. 우선 실제 필요한 데이터만 조회기때문에 쿼리의 양이 확 줄어든 것을 확인할 수 있다.
'프로젝트 > RESTAPI 추천 서비스' 카테고리의 다른 글
프로젝트 코드리뷰 (1) (0) | 2024.06.03 |
---|---|
N+1 해결 과정에서 마주친 고민 (Feat, 추가 쿼리 or 페치조인) (0) | 2024.05.31 |
이메일 전송을 비동기로 처리하기 (Feat, @Async) (0) | 2024.05.30 |
탈퇴하면 해당 유저를 어떻게 처리해야 할까? (0) | 2024.05.28 |