01. JPA N+1 문제
1. N+1 문제란?
연관 관계가 설정된 엔티티를 조회할 때, 의도하지 않은 추가 쿼리가 조회된 데이터 개수(N)만큼 발생하는 현상을 뜻한다. 예를 들어, 10개의 게시글을 조회한 뒤 각 게시글의 작성자를 출력하고자 할 때, 게시글을 가져오는 쿼리 1번 외에 작성자를 찾기 위한 쿼리가 10번 더 실행되는 것이 대표적인 N+1 문제라고 할 수 있다.
이러한 N+1 문제는 JPA가 JPQL을 실행할 때, 연관 관계 데이터를 고려하지 않고 주 객체를 우선 조회하기 때문에 발생한다. 즉, JPA는 먼저 연관된 데이터를 프록시(가짜 객체)로 채워 주 객체를 조회한 후에, 실제 연관 데이터가 사용되는 시점에 각각 추가 쿼리를 실행한다. 이러한 동작은 결과적으로 한 번의 쿼리로 처리할 수 있는 작업을 데이터 수만큼 반복하게 되어 시스템 성능에 부담을 준다.
2. 해결 방안
| 해결 방안 | |
| Fetch Join | 연관된 엔티티를 한 번의 쿼리로 가져오는 것 |
| @EntityGraph | 애노테이션을 통해 특정 쿼리에서만 함께 조회할 연관 관계 설정 |
| Batch Size 설정 | 연관 객체를 조회할 때, IN 절을 사용하여 설정한 크기만큼 묶어서 조회 |
02. 트랜잭션의 격리성(Isolation)
트랜잭션의 격리성이란 하나의 트랜잭션이 동시에 실행되는 다른 트랜잭션에게 영향을 미치거나 받지 않고, 독립적으로 실행되는 특성을 의미한다. 즉, 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 차단하는 성질을 뜻한다. 이러한 격리성이 보장되지 않을 경우, 데이터가 꼬이는 이상 현상이 발생할 수 있다.
1. 격리성 미보장 문제
| 이상 현상 | |
| 더러운 읽기 (Dirty Read) |
다른 사람이 아직 커밋하지 않은 트랜잭션의 데이터를 읽는 현상 ⇒ 해당 트랜잭션이 롤백될 경우, 잘못된 데이터를 읽은 것이 됨 |
| 반복 불가능한 읽기 (Non-Repeatable Read) |
한 트랜잭션 내에서 같은 데이터를 두 번 조회했을 때, 그 값이 다른 현상 ⇒ 중간에 다른 사람이 트랜잭션의 값을 수정함 |
| 유령 읽기 (Phantom Read) |
한 트랜잭션 내에서 일정 범위의 데이터를 두 번 조회했을 때, 첫 번째에 없던 레코드가 나타나는 현상 ⇒ 중간에 다른 사람이 트랜잭션에 데이터를 삽입함 |
2. 트랜잭션 격리 수준
트랜잭션 격리 수준(Isoloation Level)은 ANIS가 위와 같은 격리성 미보장 문제를 해결하기 위해 정의한 것으로, 여러 사람이 동시에 데이터를 건드릴 때 서로의 작업 내용을 얼마나 볼 수 있게 할 것인가를 결정한 규칙을 의미한다. 즉, 데이터의 정확성을 지키는 것과 시스템의 속도를 높이는 것 사이의 균형을 맞추기 위해 정의한 규칙을 뜻한다.
| 격리 수준 (Isolation Level) |
적용 DB | 발생 가능 문제 | |
| READ UNCOMMITTED | 커밋되지 않은 데이터도 읽음 | Dirty, Non-Repeatable, Phantom | |
| READ COMMITTED | 커밋된 데이터만 읽음 |
Oracle / Postgre | Non-Repeatable, Phantom |
| REPEATABLE READ | 내 트랜잭션 시작 전 커밋된 데이터만 읽음 |
MySQL | Phantom Read (일부 발생) |
| SERIALIZABLE | 트랜잭션을 순차적으로 실행 | 없음 |
'[Codeit] > Spring 백엔드 10기 위클리페이퍼' 카테고리의 다른 글
| [11] Container 와 Doker, 그리고 Orchestration (0) | 2026.04.06 |
|---|---|
| [10] 계층별 입력값 검증과 Mockito (0) | 2026.04.06 |
| [08] DDL과 DML, 그리고 역정규화 (0) | 2026.04.05 |
| [07] 웹 API의 발전 과정과 HTTP 요청의 흐름 (0) | 2026.02.26 |
| [06] AOP와 MVC (0) | 2026.02.26 |