본문 바로가기
WEB programming/back-end

jpa n+1 학습 후 실습 (1)

by 가으더 2025. 10. 29.
728x90

JPA N+1

상황

1. Post(게시글) 엔티티가 있고, 그 안에 author(작성자)로 User를 참조.(N:1 관계)

2. postRepository.findAll()로 게시글 목록 10개를 가져옴.

3. 그 후, 각 Post에서 post.getAuthor().getName() 같은 걸 화면에 보여주려고 접근

쿼리

1. 게시글 목록 가져오는 쿼리 1번

SELECT * FROM posts;

 

2. 첫 번째 글의 author를 가져오는 쿼리 1번

SELECT * FROM users WHERE id = ?;

3. 두 번째 글의 author를 가져오는 쿼리 또 1번

4. 세 번째 글의 author를 가져오는 쿼리 또 1번
… 이렇게 N번

 

결과적으로 : 목록 한 번(N개) 가져왔는데, 연관된 데이터를 N번 추가로 찍어서 총 N+1번의 쿼리" → N+1 문제

N+1이 나타나는 곳

postService.findAll() → posts 전체 가져오는 1번 쿼리

Response에서 post.getAuthor().getName() -> post마다 author를 Lazy 로딩하면서 쿼리 추가 → N번

= N + 1

실제로 확인해보기

/api/posts 수행 후 아래와 같은 sql 쿼리문을 확인할 수 있다.

1) 첫 쿼리 : posts 전체 가져옴

select
    p1_0.id,
    p1_0.author_id,
    p1_0.content,
    p1_0.created_at,
    p1_0.title,
    p1_0.updated_at 
from
    posts p1_0

2) 같은 형태의 쿼리가 여러번 반복 실행

select
    u1_0.id,
    u1_0.created_at,
    u1_0.email,
    u1_0.name,
    u1_0.status,
    u1_0.updated_at 
from
    users u1_0 
where
    u1_0.id=?

문제 발생 원인

JPA 기본 fetch 전략 : FetchType.LAZY (필요할 때만 불러오기) 

"필요할 때" 반복문이 돌면서 여러 번 필요해지므로 N + 1 문제가 발생.

해결

record 형태로 바꾸기

record : 모든 필드를 private final로 만듦, 생성자, getter, equals(), hashCode(), toString() 전부 만들어줌. -> immuatable 데이터 묶음으로 만들어줌.

결과

600줄 이상이던 쿼리가 아래 사진의 크기로 대폭 감소함을 알 수 있다.

 

추가 학습

사실 Fetch Join도 많이 쓰는데, 시간 관계상 여기서 한번 끊고 다음에 해보려고 한다. 그리고 하나만 하기엔 아쉬우니까 Page 처리도 같이 포함해서 해보려고 한다.

'WEB programming > back-end' 카테고리의 다른 글

[UMC] 1주차 정리 & 느낀점  (1) 2022.03.21