Today I Learned
#연관관계 매핑 실수와 영속성 컨텍스트의 역할
JPA를 사용할 때 자주 마주치는 연관관계 매핑의 실수들과 JPA의 핵심 개념인 영속성 컨텍스트의 동작 원리에 대해 정리해봤다.
연관관계 매핑 실수 유형과 실무 팁
1. 양방향 매핑 시 무한 루프 발생
양방향 연관관계를 설정할 때, `toString()`, `equals()`, `hashCode()`에서 순환 참조가 발생해 무한 루프에 빠질 수 있다.
- 해결 방법: `@ToString(exclude = ...), `@JsonIgnore`, `@EqualsAndHashCode(exclude - ...) 등으로 회피
※실무에서는 양방향 매핑을 꼭 필요한 경우에만 사용하고, 순환 참조를 피하기 위한 설정을 반드시 해줘야 함.
2. mappedBy 설정 오류
`mappedBy`는 연관관계의 주인이 아님을 명시하는 어노테이션이다.
실수로 잘못 설정하면 외래 키가 중복 생성되거나, 관계가 제대로 매핑되지 않을 수 있다.
※연관관계의 주인은 외래 키를 가진 쪽이라는 점을 명확히 이해하고 설정해야 함.
3. 컬렉션 초기화 실수
`List`, `Set` 필드 초기화를 하지 않으면 `NullPointerException` 발생 가능
- 해결 방법: `new ArrayList<>()`, `new HashSet<>()` 등으로 초기화
※Entity 생성 시점에 컬렉션 필드를 반드시 초기화하는 습관을 들이면 좋다.
4. Cascade 설정 남용
`CascadeType.ALL`을 무분별하게 사용하면 의도치 않은 저장/삭제가 발생할 수 있다.
※실무에서는 `CascadeType.PERSIST`, `MERGE`만 선택적으로 사용하는 것이 안전합니다.
5. 지연 로딩(LAZY) vs 즉시 로딩(EAGER)
`LAZY`: 실제로 사용할 때 로딩 → 성능 최적화
`EAGER`: 즉시 로딩 → N+1 문제 발생 가능
※실무에서는 대부분 `LAZY`를 기본으로 설정하고, 필요한 경우에만 `EAGER`를 고려함.
6. LazyInitializationException 발생 원인
LAZY 설정된 연관 엔티티를 트랜잭션 밖에서 접근할 경우 발생
- 해결 방법: fetch join, DTO 변환, OpenEntityManagerInView 설정 등
영속성 컨텍스트의 동작 원리
1. 영속 상태란?
`EntityManager.persist()`를 통해 객체가 영속성 컨텍스트에 저장된 상태
이후 변경된 필드는 트랜잭션 커밋 시 자동으로 DB에 반영됨
2. 쓰기 지연 (Write-behind)
`persist()` 호출 시 즉시 DB에 반영되지 않음
트랜잭션 커밋 시점에 한 번에 쿼리 실행 → 성능 최적화
3. 1차 캐시
동일한 엔티티를 여러 번 조회해도 DB에 재요청하지 않음
`find()` 호출 시 먼저 캐시에서 조회
4. 변경 감지 (Dirty Checking)
영속 상태의 객체가 변경되면 트랜잭션 커밋 시점에 자동으로 `UPDATE` 쿼리 실행
5. flush() 와 clear() 실습
`flush()`: 영속성 컨텍스트의 변경 내용을 DB에 반영
`clear()`: 영속성 컨텍스트 초기화 → 1차 캐시 제거
실습을 통해 SQL 로그를 확인하며 쓰기 지연이 어떻게 작동하는지 알수 있었다.
실습 예시
@Entity
public class Post {
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
}
`mappedBy` = "post"로 연관관계 주인을 명확히 설정
`comments` 컬렉션은 `new ArrayList<>()`로 초기화
`CascadeType.ALL`은 실습에서는 사용했지만, 실무에서는 신중하게 선택
#마치며
이번 학습은 JPA가 왜 그렇게 설계되었는지, 어떤 실수를 피해야 하는지, 그리고 성능과 안정성을 어떻게 확보할 수 있는지를 실무 관점에서 이해하는 시간이었다.
영속성 컨텍스트의 동작 원리를 알고 나니, JPA가 단순한 ORM이 아니라 정교한 트랜잭션 관리 도구라는 걸 실감하게 되었다.