JPA N+1 문제, 왜 생기고 어떻게 줄일까: fetch join과 EntityGraph 선택 기준까지 빠른 답 EAGER 는 N+1의 해결책이 아니라 연관 조회 시점을 앞당기는 설정에 가깝다. JPQL이나 Spring Data JPA 조회 메서드가 조인을 자동 보장하지는 않는다. LAZY 는 문제를 없애는 방식이 아니라 미루는 방식이다. 목록을 읽은 뒤 연관 필드를 순회하는 순간 N+1이 다시 나타날 수 있다. 목록 조회 최적화는 fetch join , @EntityGraph , 배치 로딩( @BatchSize , hibernate.default_batch_fetch_size )을 조회 형태와 페이징 요구사항에 맞춰 나눠 쓰는 편이 낫다. 최적화 여부는 SQL 로그, 바인드 값, 쿼리 수, 실행 계획까지 같이 봐야 판단할 수 있다. 목차 시간 흐름으로 이해하기 흐름으로 보기 패치가 아니라 fetch 전략이다 N+1이 생기는 구조를 쿼리 관점에서 보기 LAZY와 EAGER는 왜 둘 다 N+1에서 안전하지 않은가 findAll 호출 뒤에 실제 SQL이 어떻게 늘어나는가 실행 계획과 인덱스로 보면 왜 더 느려지는가 fetch join, EntityGraph, 배치 로딩은 어떻게 나눠 쓰나 설정과 검증 예시 자주 부딪히는 함정과 현재 기준 버전 차이 시간 흐름으로 이해하기 T1 목록 조회 시작 → T2 루트 쿼리 실행 → T3 연관 로딩 대기 → T4 연관 필드 접근 → T5 추가 SQL 반복 Post 목록 API는 대개 루트 엔티티만 읽는 SQL로 시작한다. 문제는 그다음 시점이다. DTO 매핑이나 JSON 직렬화에서 post.getAuthor().getName() 같은 코드가 실행되면, 그 순간 연관 엔티티 초기화가 시작되고 결과 건수만큼 비슷한 SQL이 반복될 수 있다. 흐름으로 보기 흐름 다이어그램 N+1은 "첫 쿼리 하나가 느린 문제"라기보다 "첫 쿼리 뒤에 반복 SQL이 따라붙는 구조"에 가...