[TIL]심화 학습 개인과제 회고

YJin·2025년 4월 18일

[내배캠 Spring 6기_TIL]

목록 보기
22/56

회고

Lv2. N+1 문제 해결

기존 코드

@Query("SELECT t FROM Todo t LEFT JOIN FETCH t.user u ORDER BY t.modifiedAt DESC")
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);
  • LEFT JOIN FETCH 를 통해 N+1 문제 해결

수정

@EntityGraph(attributePaths = "user")
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);
  • @EntityGraph를 사용하여 N+1 해결

FETCH JOIN vs @EntityGraph

FetchType.EAGER 로 조회 시 조회된 데이터(N)만큼 연관 관계에 대한 쿼리가 추가 발생하는 문제를 N+1 문제라고 함.

이를 FetchType.LAZY로 변경하여 해당 연관 관계가 필요한 시점에 조회하는 것으로 회피할 수 있지만 여전히 조회 시점에 N+1 조회 쿼리가 추가적으로 발생.

N+1 문제를 해결하기 위해서는?
1️⃣FETCH JOIN과 2️⃣@EntityGraph 방법을 사용할 수 있다.

1️⃣ FETCH JOIN

지연로딩(LAZY)가 아닌 즉시로딩(EAGER)처럼 한번에 조회하여 프록시가 아닌 실제 엔티티 참조 가능.

  • 한꺼번에 가져오므로 JPA의 Paging API (Pageable) 사용 불가
  • Native Query로 작성 시 하드 코딩 단점 존재 (QueryDSL로 해결 가능)
  • ~:N 관계가 여러개 일시 사용 불가
  • 기본적으로 Inner join 이므로 연관 관계가 null일 시 조회 대상에서 제외 (null-safe 하지 않음)
    • LEFT JOIN FETCH로 명시하면 해결 가능하지만 여전히 하드 코딩의 단점이 존재

  • 페이징 문제 존재
    • DB가 아닌 메모리에서 처리됨 (JOIN으로 쿼리가 날아가기 때문)

2️⃣ @EntityGraph

  • 기본적으로 Inner Join을 하는 Fetch join과 다르게 Outer Join 방식으로 동작. (null-safe)
  • 어노테이션 선언 후 attributePath 설정으로 간단하게 사용 가능
  • 여러개의 ~:N 관계도 처리 가능
  • 페이징 문제 존재
    • DB가 아닌 메모리에서 처리됨 (EntityGraph와 같이 JOIN으로 쿼리가 날아가기 때문)

페이징 문제

Fetch Join이나 @EntityGraph나 기본적으로는 JPQL으로 JOIN하여 데이터를 가져오는 것이기 때문에 LIMIT, OFFSET을 사용하는 페이징 사용 시 경고가 뜬다
DB에서 처리가 되지 않고 모든 데이터를 메모리에 불러와서 메모리에서 페이징을 처리한다.

Team IDMember ID
110
111
220
330
331
332
440
550
551

이 경우 메모리 부족 문제가 발생할 가능성이 큼! 그렇다면?

@BatchSize로 해결

일대다 관계에서 페치 조인을 했을 때 데이터 뻥튀기가 발생하는 것은 하이버네이트5 버전에서 객체-RDB의 간극 때문에 발생했던 문제인데, 하이버네이트6 이후부터는 JPA 쿼리에 distinct가 자동으로 적용됨

도전 과제

Lv1 인터셉터 or AOP

인터셉터 https://mangkyu.tistory.com/173

마치며

이번 과제는 내가 처음부터 코드를 작성하는 것이 아닌, 이미 만들어진 다른 사람의 코드를 보고 개선점/오류를 찾는 것이어서 문제 접근 방식이 달랐던 것 같다.

  • 다른 사람의 코드이다 보니 .. 뭐라는지 모르겠다 . 주석을 더 달아줬으면 한다.
    • 이 부분은 다른 사람 코드 리뷰를 더 많이해서 코드 보는 눈을 기르고 싶다.
  • 지난번 팀 프로젝트에서 코드 리뷰를 했을 때처럼 왜 이렇게 작성했을까? < 이 생각을 제일 많이 함.
  • 5번 항목이 흥미로웠다 (과제에선 못했다.) 해설 세션에서 다른 사람들의 개선 방안을 보면서 이렇게 생각을 할수도 있구나 함. 확실히 개발자는 설득과 주장이 중요한 것 같다. 왜 그 코드를 짰는지 왜 그렇게 개선했는지 다른 사람을 설득하는 것이 중요한 것 같다. 절대적으로 좋은 코드는 없고 사람마다 생각이 다를 수 있는거니 항상 코드를 짤 때 여러번 생각하면서 짜야겠다.

참고

N+1 - Fetch join vs @EntityGraph

profile
백엔드 개발도 락이다

0개의 댓글