N+1 문제란 무엇인가
N+1 문제는 ORM(객체 관계 매핑)을 사용할 때 흔히 발생하는 성능 저하 이슈입니다. 하나의 쿼리로 N개의 데이터를 조회한 뒤, 그 각각의 데이터에 연관된 정보를 가져오기 위해 추가로 N번의 쿼리가 실행되는 현상을 말합니다. 결과적으로 원래 1번이면 끝날 수 있었던 조회가 1 + N번의 쿼리로 늘어나면서 데이터베이스에 불필요한 부하를 주게 됩니다.
왜 발생하는가: 동작 원리와 예시
예를 들어 게시글(Post)과 작성자(Author)가 1:1 관계라고 가정해봅시다. 게시글 10개를 조회한 뒤 각 게시글의 작성자 이름을 출력하는 코드를 작성하면, JPA나 Hibernate 같은 ORM은 기본적으로 연관 데이터를 지연 로딩(Lazy Loading)합니다. 즉 게시글 목록을 가져오는 쿼리 1번이 실행된 후, 화면에서 작성자 정보에 접근할 때마다 별도의 쿼리가 추가로 발생합니다.
비유하자면, 식당에서 손님 10팀의 주문을 한 번에 받아왔지만(1번의 쿼리), 각 팀이 무엇을 마실지는 따로따로 물어보러 가는 것과 같습니다(10번의 추가 쿼리). 손님이 100팀이라면 종업원은 101번을 움직여야 하는 셈이죠. 데이터가 많아질수록 이 비효율은 기하급수적으로 커집니다.
실무에서 왜 반드시 알아야 하는가
N+1 문제는 개발 환경의 소규모 테스트 데이터에서는 잘 드러나지 않다가, 운영 환경에서 데이터가 많아지면 갑작스러운 응답 지연이나 DB 부하로 나타나는 경우가 많습니다. 실무에서는 다음과 같은 방법으로 이를 예방합니다.
fetch join을 사용해 연관 엔티티를 한 번의 쿼리로 함께 조회한다.- Batch Size 설정으로 연관 데이터를 묶어서 조회한다(
hibernate.default_batch_fetch_size). - 필요한 경우 DTO 조회나 QueryDSL을 활용해 처음부터 필요한 데이터만 최적화된 쿼리로 가져온다.
- 실행되는 SQL 로그를 항상 확인하는 습관을 들인다.
ORM은 개발 생산성을 크게 높여주지만, 내부에서 어떤 쿼리가 실제로 실행되는지 이해하지 못하면 성능 문제를 뒤늦게 발견하게 됩니다. N+1 문제는 그 대표적인 사례로, ORM을 사용하는 개발자라면 반드시 개념을 이해하고 대응 전략을 갖추고 있어야 합니다.