JPA필수코스 N+1문제 해결하기
들어가기에 앞서
JPA를 처음 사용하면 정말 어쩔 수 없이 필수적으로 마주하는 문제가 바로 N+1문제이다. 연관관계와 무관하게 select절에 조회한 데이터만 가져오고 나머지는 프록시로 가져오는 JPA의 특성상 조회한 엔티티의 연관관계에 속해있는 엔티티를 조회하면 해당 엔티티를 조회하는 수 만큼 쿼리가 새로 나가는 현상이다.
N+1문제 발견하기
처음부터 코드를 잘 짜면 좋겠지만 이번 프로젝트 자체가 공부를 하면서 진행한 프로젝트이다보니 N+1이란 존재도 나중에 알게되었다. 동작하는 쿼리문을 하나씩 살펴보면서 문제가 발생하는 곳을 찾았고 바로 수정하였다.
해당 문제는 Board와 Image테이블에서 나타났다.
현재 게시글 엔티티인 Board와 게시글안에 저장할 수 있는 Image엔티티가 1:N관계로 이루어져 있다. 그리고 당연하게도 Board만 조회해서 게시글 리스트를 뿌릴 떄 Image를 조회하는 형식으로 사용하고 있었다.
그랬더니 게시글 10개를 뿌리는데 이미지 조회만 10번... 아 이게 바로 N+1이구나 라는 생각으로 바로 수정에 들어갔다.
N+1문제 해결하기
N+1문제를 해결하는 방법으로는 크게 3가지가 있다.
- fetch join 사용
- BatchSize 사용
- DTO로 변환해서 사용
보통 1:N관계에서 페이징을 사용할 때는 데이터가 늘어나는 현상이 있어서 페치조인을 사용하기가 힘들다. 이럴 때 주로 사용하는 것이 BatchSize이다.
DTO는 필요시에는 사용 할 수 있겠지만 지금은 DTO가 아닌 엔티티를 반환하고 있기떄문에 이것도 사용하지 않겠다.
결국 페치조인을 사용해서 문제를 해결했다. 지금 상황에서 가장 효율적이고 간단한 방법이다.
바꾸기 전의 쿼리는 그냥 메소드 호출로 가져왔다.
List<Board> findByMember_idAndDate(@Param("memberId") Long memberId, @Param("date") String date);
페치조인을 사용한 쿼리는 다음과 같다.
@Query("select b from Board b left join fetch b.images join b.member m where b.date = :date and m.id = :memberId")
List<Board> findByMember_idAndDate(@Param("memberId") Long memberId, @Param("date") String date);
이렇게 수정한 뒤 조회를 해봤을 때 깔끔하게 하나의 쿼리로 조회되는 것을 확인할 수 있었다.
'에러모음' 카테고리의 다른 글
JPA 일대다 조인 데이터 부풀려지기 방지하기 (0) | 2022.06.27 |
---|---|
스프링부트 gradle build 실패 (0) | 2022.06.15 |
[Error] Mockito를 사용한 단위테스트(form데이터 전송) (0) | 2022.05.17 |
[Error] JPQL에서 엔티티조인 후 내부의 엔티티 조회 (0) | 2022.05.03 |
[Error] TransientPropertyValueException에러.. (0) | 2022.05.02 |
댓글
이 글 공유하기
다른 글
-
JPA 일대다 조인 데이터 부풀려지기 방지하기
JPA 일대다 조인 데이터 부풀려지기 방지하기
2022.06.27 -
스프링부트 gradle build 실패
스프링부트 gradle build 실패
2022.06.15 -
[Error] Mockito를 사용한 단위테스트(form데이터 전송)
[Error] Mockito를 사용한 단위테스트(form데이터 전송)
2022.05.17 -
[Error] JPQL에서 엔티티조인 후 내부의 엔티티 조회
[Error] JPQL에서 엔티티조인 후 내부의 엔티티 조회
2022.05.03