[Spring] JPA 관계 설정(1:N/N:1)
2025. 11. 5. 08:22ㆍDev./Spring
728x90
반응형
JPA 관계 설정
ERD에서 DB간 관계를 설정했다면 그 관계를 자바 객체 수준에서 반영하는 과정을 의미한다. 즉 ERD 테이블 관계를 코드로 옮겨온다 라고 이해하면 될 것 같다.
| ERD | JPA 관계 설정 | |
| 표현 대상 | 데이터베이스 | 자바 객체 |
| 단위 | 테이블 | 엔티티 클래스 |
| 관계 표현 | PK/FK | 어노테이션 (@OneToMany, @ManyToOne) |
| 주 언어 | SQL, 다이어그램 | Java |
| 역할 | 설계 | 구현 |
| 결과 | DB 스키마 | 코드 로직에서의 연결 |
JPA 관계 설정 핵심 키워드 정리
1. @OneToMany / @ManyToOne — 가장 기본적인 관계 표현
의미
- 1:N 관계 (예: Todo 1개 → Comment 여러 개)
- N:1 관계 (예: Comment 여러 개 → Todo 1개)
키워드 포인트
- 외래키(FK) 는 항상 N 쪽(다수 쪽) 에 존재한다.
→ 그래서 @ManyToOne 이 연관관계의 주인 - 반대로 @OneToMany(mappedBy = "todo") 는 단순히 읽기용
2. mappedBy — 연관관계의 주인을 지정
의미
- 양방향 관계에서 외래키를 실제로 관리하는 주체를 지정한다.
- mappedBy 는 “나는 주인이 아니야”라는 뜻.
설정 안하면?
- 양쪽이 다 주인으로 인식되어 불필요한 조인 테이블이 생김. 즉, @JoinTable 이 자동 생성되어 예상치 못한 구조가 만들어짐
→ mappedBy 를 정확히 써야 단방향+양방향 혼동을 막을 수 있음.
3.@JoinColumn — 외래키(FK) 컬럼 이름 지정
- DB에 실제로 어떤 이름으로 외래키를 저장할지 지정
4. cascade — 연관된 엔티티까지 함께 처리
- 부모 엔티티의 상태 변화가 자식 엔티티에도 전파
속성
- PERSIST : 부모 저장시 자식도 같이 저장
- REMOVE : 부모 삭제시 자식도 삭제
- ALL : 모든 상태 변화 전파( 저장, 삭제, 병합 ..)
5. orphanRemoval = true — 부모로부터 떨어진 자식 자동 삭제
- 부모의 컬렉션(List)에서 제거된 자식 엔티티를 DB에서도 자동 삭제
6. fetch = FetchType.LAZY / EAGER — 지연로딩과 즉시로딩
- 연관된 객체를 언제 가져올지 결정하는 전략
옵션
- LAZY : 실제 접근할 때 쿼리 실행 ( 지연 로딩 )
- EAGER : 바로 join 해서 가져옴 ( 즉시 로딩 )
⚠️ EAGER는 불필요한 join으로 성능 저하를 유발하기 때문에 기본은 보통 LAZY로 둔다. @ManyToOne은 기본값이 EAGER 이므로 LAZY로 명시해주자
7. 연관관계의 주인
- 외래키(FK)를 가진 엔티티가 연관관계의 주인
- mappedBy가 없는 쪽이 주인
목표
Todo 1개에 여러 개의 Comment가 달리는 구조 구현
- Todo (1) : 여러 Comment를 가짐
- Comment (N) : 하나의 Todo에 속함
Entity 설계
Todo.java
...
public class Todo extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
...
// Todo 하나에 여러 comment들이 연결됨
@OneToMany(mappedBy = "todo", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
}
- mappedBy = "todo"
→ Comment의 필드명(todo)과 일치해야 함 - cascade = CascadeType.ALL
→ Todo 저장/삭제 시 Comment도 같이 처리 - orphanRemoval = true
→ Todo에서 제거된 Comment는 DB에서도 삭제
Comment.java
...
public class Comment extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
// 여러 댓글(N)이 하나의 Todo(1)에 속함
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "todo_id", nullable = false)
private Todo todo;
}
- @ManyToOne : 여러 댓글이 하나의 Todo를 참조
- fetch = FetchType.LAZY : 기본적인 댓글만 조회 시 Todo는 즉시 불러오지 않음 (지연 로딩)
- @JoinColumn(name = "todo_id") : FK 이름 지정
관계 요약
| 방향 | 관계 | 설명 |
| Todo → Comment | 1:N | 하나의 일정에 여러 댓글 연결 |
| Comment → Todo | N:1 | 하나의 댓글은 특정 일정에 속함 |
| FK 위치 | Comment 테이블 | todo_id 컬럼 생성 |
| 연관관계 주인 | Comment | 실제 FK를 가짐 |
728x90
반응형
'Dev. > Spring' 카테고리의 다른 글
| [Spring] JPA 영속성 컨텍스트 (0) | 2025.11.10 |
|---|---|
| [Spring] JPA N+1 문제 (0) | 2025.11.07 |
| [Spring] 수정 사항 발생시 자동 리빌드 Auto Reload (0) | 2025.11.04 |
| [내배캠] 한달 회고 (0) | 2025.10.31 |