[SB 3기] 코드잇 스프린트 위클리페이퍼 9주차
카테고리
프로그래밍/소프트웨어 개발
서브카테고리
웹 개발
대상자
JPA(Java Persistence API) 및 트랜잭션 관리에 관심 있는 개발자
핵심 요약
- N+1 문제 발생 원인: 연관 엔티티의
Lazy Loading
또는Eager Loading
으로 인한 추가 쿼리 발생 - 해결 방안:
@Query("SELECT m FROM Member m JOIN FETCH m.team")
또는@EntityGraph
사용 - 트랜잭션 격리성 문제:
Dirty Read
,Non-repeatable Read
,Phantom Read
발생 시READ COMMITTED
~SERIALIZABLE
격리 수준 적용
섹션별 세부 요약
1. N+1 문제 발생 원인
- 연관 엔티티 조회 시 추가 쿼리 발생:
- member.getTeam().getName()
호출 시 N
개의 쿼리 실행
- 예: 100명의 회원 조회 시 101개의 쿼리 실행
- Lazy/Eager Loading 한계:
- Lazy Loading
시 런타임에 쿼리 발생
- Eager Loading
시 컬렉션 반복 접근 시 N+1 문제 발생
2. N+1 문제 해결 방안
- JPQL
JOIN FETCH
사용:
```java
@Query("SELECT m FROM Member m JOIN FETCH m.team")
List
```
- 장점: 직관적, 복잡 쿼리 지원
- 단점: 페이징 처리 불가, 중복 데이터 발생 시 DISTINCT
필요
@EntityGraph
활용:
```java
@EntityGraph(attributePaths = {"team"})
@Override
List
```
- 장점: 코드 간결, 페이징 처리 가능
- 단점: 복잡 조인/필터링 쿼리에 적합하지 않음
3. 트랜잭션 격리성 문제
- 격리성 보장 실패 시 발생 문제:
- Dirty Read: 커밋되지 않은 데이터 읽기
- Non-repeatable Read: 동일 쿼리 결과 불일치
- Phantom Read: 동일 조건 쿼리 시 행 추가 발생
- 예시 시나리오:
- 쇼핑몰 재고 동시 주문 시 격리성 미보장 시 재고 초과 판매 발생
4. 트랜잭션 격리 수준
| 격리 수준 | 설명 | 방지되는 문제 |
|------------------|-----------------------------------|----------------------------|
| READ UNCOMMITTED | 커밋되지 않은 데이터도 읽음 | ❌ Dirty Read 허용 |
| READ COMMITTED | 커밋된 데이터만 읽음 (기본값) | ✅ Dirty Read 방지 |
| REPEATABLE READ | 동일 쿼리 결과 일관성 보장 | ✅ Non-Repeatable Read 방지 |
| SERIALIZABLE | 트랜잭션 순차 실행 보장 (성능 저하) | ✅ 모든 문제 방지 |
결론
- N+1 문제 해결:
JOIN FETCH
또는@EntityGraph
사용, 페이징 처리 필요 시@EntityGraph
선택 - 트랜잭션 격리 수준 선택:
- 성능 우선: READ COMMITTED
- 데이터 일관성 우선: REPEATABLE READ
또는 SERIALIZABLE
- 예: 재고 관리 시 REPEATABLE READ
적용하여 Phantom Read 방지