[JPA] Entity 다대다 매핑 어떻게 매핑할까? (복합키 or 단일 PK + FK)

이민호·2025년 4월 7일
post-thumbnail

배경

프로젝트를 진행하거나 학교 과제를 진행면서, ERD와 엔티티 설계시 다대다 관계를 설계하는데 항상 어려움이 있었다.

이번 프로젝트에서 다대다 매핑을 단일PK + FK로 결정했던 근거에 대해 생각을 공유해보겠다.


이번 프로젝트의 요구사항은 Github Commit들과 그 부모 Commit들의 관계를 나타내는데 어떤식으로 ERD와 Entity를 설계할 것이냐가 관건이었다.
항상 복합키를 구성해서 설계했었는데 이번 기회에 내 나름 근거를 들어 설계를 해봤고, 그에 대한 의견을 공유 해보겠다.


단일KEY 방식이냐,, 복합키 방식이냐,,,

우선!! 다대다 관계를 설정하는데에는 크게 두 가지 방식이 있다.

단일 PK + FK 방식은 쉬우니 복합키 방식에 대해 파악해보자.

1. 복합키 방식

복합키 방식은 테이블의 연관관계에서 Foreign Key 여러 개를 하나의 PK로 구성하는 방식이다.

✔특징 1. 이는 관계 자체가 PK가 된다.

이말은 즉, 우리 비즈니스 로직 자체를 구성하는 두 개의 Column (commit_id, parent_id) 자체가 PK가 된다는 말이다. 부모- 자식 관계 처럼 자연스럽게 UNIQUE하게 구성이 된다.

✔특징 2. 데이터 관리가 어렵다.

주로 PK를 이용해서 데이터를 찾는 DB 특성상, 우리는 commit_id, parent_id 컬럼에 대한 2개의 값을 같이 알아야 조인, 삽입, 업데이트가 가능하다는 말이다. 이는 Join , Insert 문의 복잡도를 증가 시킬 수 있다.

✔특징 3. 인덱스 구성.

MYSQL에서는 PK는 PK로 구성된 인덱스를 무조건 구성한다! 따라서 (commit_id, parent_id) 복합 컬럼으로 구성된 인덱스가 만들어 진다는 것이다. -> 이는 조건 검색 (WHERE commit_id = ? AND parent_id = ?) 이나 조인 시 필터링 성능이 향상된다.

단일 PK방식 + FK는 PK는 의미없는 값으로 두고, FK는 복합 컬럼으로 구성하는 방식이다.

단일 PK 방식을 고른 이유?

우선 제일 큰 이유는 우리의 요구사항에서 인덱싱이 필요가 없었기 때문이다.

복합키를 구성한다는 것은 그만큼 복잡성을 감안한다는 것인데,
사실 Commit_parents 테이블은 단순히 커밋들의 관계를 나타낼 뿐이다(울의 요구사항에서) join이나, 특정 튜플을 골라 선택할 경우가 거의 없다.

어짜피 부모, 자식 각각의 정보만 필요하고, 부모 자식은 결국에 Commits 테이블의 하나의 튜플을 구성하므로, 단일 PK와 연관관계를 통해 역으로 접근하면 된다.
데이터를 가져오기 위해 Join하는 것은 너무 비효율 적이라고 생각했다.

복합키 방식은 비즈니스 로직의 컬럼을 하나의 PK로 구성하기 때문에 확장성이 낮다고 생각했다.

예를 들어,
한 커밋 - 부모 조합이 여러번 생길 수도 있다. 만약 그 테이블에 상태(status), 작성자 (user_id)가 추가로 생겼다고 해보자.

커밋 - 부모 조합은 같지만 다른 컬럼의 데이터가 다를 수도 있다.
이런 경우 복합키 방식은 표현하기 힘들다. 이런 경우를 대비해 단일 PK Entity에서는 컬럼에 unique제약을 걸어준다.

package LogITBackend.LogIT.domain;

import LogITBackend.LogIT.domain.common.BaseEntity;
import jakarta.persistence.*;

@Entity
@Table(name = "commit_parents",
        uniqueConstraints = @UniqueConstraint(columnNames = {"commit_id", "parent_id"}))
public class CommitParent extends BaseEntity {

    @Id // 단일 id 방식
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "commit_id")
    private Commit commit;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Commit parent;

}

결론

복합키 방식은 단순한 관계를 나타낼 때, 정말 그 관계 하나밖에 없을때 나타내면 좋을 것 같고, JPA에서는 단일 KEY를 두고 FK를 여러개 두는게 낫다는 결론을 냈다.

profile
효율적으로 살게요.

0개의 댓글