❗스프링 JPA 연관관계 정리 : 연관관계 주인과 다대다 관계❗

데이터 설계 시 한 번쯤은 고민한다는 단방향 연관관계와 양방향 연관관계를 정리해 보려고 한다.

연관관계를 정의할 때, 크게 세 가지를 생각한다.

  1. 방향 : 단방향, 양방향 (객체 참조)
  2. 연관 관계의 주인 : 양방향일 때, 연관 관계에서 관리 주체
  3. 다중성 : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)

이번 글에서는 다대일 관계에서의 연관관계 주인과 다대다 관계를 설명하고자 한다.

✨ 단방향 연관관계

: 객체 관계에서 한쪽만 참조하는 것

✨ 양방향 연관관계

: 객체 관계에서 서로 참조하는 것

  • 엄밀히 말하면, 데이터베이스에서 참조는 방향이 없기 때문에, 서로 단방향 참조를 하는 것을 양방향 참조라고 한다.

✨ 연관관계 주인

JPA 연관 관계에 대해 공부하다 보면, 다대일 관계에서 ‘다’ 쪽에 연관관계의 주인을 잡아야 한다고 하는데, 그 이유가 무엇일까?

생각해 보면 당연한 소리이다. 데이터베이스는 fk를 통해 테이블을 관리하기 때문이다.

다대일 관계에서의 연관관계 - 예시

예시를 하나 생각해 보자. 하나의 게시물에 댓글이 여러 개 달리는 로직을 구현한다고 한다.

Commtent 테이블과 Board 테이블은 다대일 연관관계를 가질 것이고, 만약 Board (다대일에서 일쪽) 에 연관관계의 주인을 둔다고 가정하면, Board 엔티티의 속성이 바뀌는 Comment에 따라 중복되는 Board 값이 쌓일 것이다.

엔티티는 객체와 달리 리스트와 같은 컬렉션을 가질 수 없다.

따라서, 하나하나 풀어내야 하는데, Board는 하나이고 Comment는 여러 개이므로 이 Comment를 Board에 나타내기 위해서는 (연관관계 주인이 Board이므로 Board 테이블에 나타낸다) Board id, title, content … 와 같은 수많은 속성들이 여러 개이고 Comment id(fk)만 다른 값들이 Comment 수대로 생성될 것이다.

Board Entity

board_id title content create_at comment_id
1 제목 내용 2024.05.15~ 1
1 제목 내용 2024.05.15~ 2
1 제목 내용 2024.05.15~ 3
1 제목 내용 2024.05.15~ 4
1 제목 내용 2024.05.15~ 5

Comment Entity

comment_id content create_at
1 내용1 2024.05.15~
2 내용2 2024.05.15~
3 내용3 2024.05.15~
4 내용4 2024.05.15~
5 내용5 2024.05.15~

이렇게 된다면 끔찍한 일…

또한, 트랜잭션의 원칙 중 하나인 데이터베이스 하나의 컬럼은 하나의 값을 가져야 한다는 원자성에 어긋나게 된다.

만약 반대로 Comment 쪽에 연관관계 주인을 설정한다면?

Comment는 Board에 대한 fk 값을 각각 가지고 있으므로 Comment 값이 겹치지 않게 될 것이다.

Board Entity

board_id title content create_at
1 제목 내용 2024.05.15~

Comment Entity

comment_id content create_at board_id
1 내용1 2024.05.15~ 1
2 내용2 2024.05.15~ 1
3 내용3 2024.05.15~ 1
4 내용4 2024.05.15~ 1
5 내용5 2024.05.15~ 1

Board는 연관관계의 주인이 아니므로 comment에 대한 fk를 갖고 있지 않다. 따라서, 더 효율적인 처리가 가능한 것이다.

✨ 다대다 관계

다대다 관계도 마찬가지로, 중간 테이블을 만들어 값을 풀어내기 위해서는 중간 테이블이 “다”로, 연관관계의 주인이 되어야 한다. (여기서는 중간 테이블을 생성해주는 애노테이션을 쓰지 않는다고 가정한다.)

물론 다대다 관계는 실무에서 사용을 권장하지 않는다. (되도록 피하는 편이 좋다.)

Category와 Item이 다대다 관계이고, CategoryItem이라는 중간 테이블로 풀어내야 한다고 가정한다.

위의 내용과 마찬가지이므로, 자세한 설명은 생략한다. 중간 테이블이 N(다)이고, Category, Item이 1일 때 테이블을 보면 다음과 같다.

pk와 fk가 중요한 내용이므로 나머지 속성은 생략한다.

Category Entity

category_id
1
2
3

Item Entity

item_id
1
2
3
4

CategoryItem이 “다” 이므로 Category, Item에 대한 연관관계의 주인이다. 따라서, CategoryItem이 Category, Item에 대한 fk를 가지게 된다.

CategoryItem Entity

category_item_id category_id item_id
1 1 1
2 1 2
3 1 5
4 2 1
5 2 2

category id가 1인 값을 불러올 수도 있고, item_id가 1인 값을 쿼리를 날려 불러올 수도 있는 것이다.

그러므로 다대다 관계는 지양해야 하지만, 만약 중간 테이블로 풀어내야 한다면 무조건 중간 테이블이 “다”여야 하고, fk 값을 가져야 이러한 관계를 풀어낼 수 있다.

✨ 참고 자료

  • https://jeong-pro.tistory.com/231
  • https://hoestory.tistory.com/28

카테고리:

업데이트: