Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
Tags
- 메모리구조
- optional
- 스파르타사전캠프
- Til
- 계층형
- 스파르타코딩클럽 #개발자 #백엔드
- 스파르타코딩클럽
- 네트워크
- 내일배움캠프
- 패키지구조
- 깃헙
- Spring
- 데이터모델링
- HTTP
- 스터디카페
- 레퍼클래스
- springboot
- 자바
- 스파르타코딩클럽 #사전캠프
- 도메인별
- Java
- 스프링부트
- 함바그
- SQLD
- til #데이터베이스 #sql
- 키오스크
- 프레임워크
- 사전캠프
- 정규화
- 캡슐화
Archives
- Today
- Total
개발일지
게시물 페이징 조회시 n+1 본문
1️⃣ 문제 상황: 게시판 페이징 조회 시 N+1 문제
엔티티 구조
- Board (게시글) ↔ Member (작성자) : @ManyToOne(fetch = LAZY)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
private Member member;
- 게시글을 조회할 때 Board 객체만 가져오고 Member는 LAZY 로딩 설정
- 따라서 게시글 목록을 조회하면서 board.getMember().getName() 같은 호출이 발생하면 Member를 DB에서 추가로 조회하는 쿼리가 반복적으로 실행
실제 로그 예시 (N+1 문제 발생)
select * from board limit 10; -- 1번 쿼리 (게시글 목록)
select * from member where member_id=?; -- 10번 쿼리 (각 게시글 작성자 조회)
select * from member where member_id=?;
...
- 게시글 10개 조회 시, Member 조회 쿼리가 10번 반복 → N+1 문제
2️⃣ 문제 발생 원인
- @ManyToOne(fetch = LAZY) → 기본적으로 연관 엔티티를 즉시 가져오지 않음
- PostResponse DTO 생성 시 board.getMember().getName() 호출 → Member가 아직 로딩되지 않아 추가 쿼리 발생
즉, Board는 한 번 조회했지만 Member는 게시글 개수만큼 추가 조회되는 구조가 문제였습니다.
3️⃣ 해결 방법
(1) Fetch Join 사용
- JPQL에서 Board와 Member를 한 번에 조회하도록 JOIN FETCH 사용
@Query("SELECT b FROM Board b JOIN FETCH b.member ORDER BY b.createdAt DESC") List<Board> findAllWithMember();
- 이렇게 하면 Board와 연관된 Member 데이터를 한 번의 쿼리로 가져올 수 있음
(2) 페이징 처리
- JPA에서는 fetch join과 Pageable을 함께 쓰면 count 쿼리가 필요
- 따라서 데이터 조회 쿼리와 count 쿼리를 분리
@Query(value = "SELECT b FROM Board b JOIN FETCH b.member ORDER BY b.createdAt DESC",
countQuery = "SELECT count(b.boardId) FROM Board b")
List<Board> findAllWithMemberPaged(Pageable pageable);
- 데이터 조회 쿼리: 게시글 + 작성자 한 번에 조회
- count 쿼리: 페이지네이션용 게시글 수만 계산
(3) 서비스에서 DTO 변환 시 추가 쿼리 없음
- 이제 board.getMember().getName() 호출해도 이미 JOIN으로 가져왔기 때문에 추가 쿼리 발생하지 않음
4️⃣ 결과
- 기존: 게시글 10개 조회 → Member 10개 추가 조회 → 총 11번 쿼리 (N+1 문제)
- 개선: 게시글 10개 조회 + Member JOIN → 1번 쿼리 + count 1번 → 총 2번 쿼리 ✅
- 로그 예시:
select b1_0.board_id, b1_0.title, ..., m1_0.member_id, m1_0.name, ... from board b1_0 join member m1_0 on m1_0.member_id = b1_0.member_id order by b1_0.created_at desc; select count(*) from board b1_0;
즉, N+1 문제가 완전히 해결됨.
select
b1_0.board_id,
b1_0.category,
b1_0.comment_count,
b1_0.content,
b1_0.created_at,
b1_0.member_id,
m1_0.member_id,
m1_0.birthdate,
m1_0.content,
m1_0.created_at,
m1_0.email,
m1_0.experience_level,
m1_0.is_deleted,
m1_0.name,
m1_0.password,
m1_0.region,
m1_0.updated_at,
b1_0.title,
b1_0.updated_at,
b1_0.view_count
from
board b1_0
join
member m1_0
on m1_0.member_id=b1_0.member_id
order by
b1_0.created_at desc
select
count(*)
from
board b1_0