복합 키(Composite Key)란?

  • 하나의 테이블이 2개 이상의 컬럼을 조합해 PK(기본 키)를 구성하는 경우
  • 흔히 다대다(N:M) 관계를 풀어내는 조인 테이블에서 자주 사용

 

예시 테이블 구조

CREATE TABLE order_items (
    order_id BIGINT,
    product_id BIGINT,
    quantity INT,
    PRIMARY KEY (order_id, product_id) # order_id + product_id 가 복합 PK
);
  • 하나의 주문(Order)에는 여러 상품(Product)이 포함될 수 있음
  • 하나의 상품은 여러 주문에 포함될 수 있음
  • 이 경우 OrderItem 테이블은 order_id 와 product_id 를 묶어서 복합 키로 사용

 

JPA에서 복합 키를 정의하는 방법

대표적으로 @IdClass 방식과 @EmbeddedId 방식이 있다.

 

@IdClass 방식

  • 복합 키를 외부 식별자 클래스로 정의하고, 엔티티 내부에서 각 키 필드를 개별적으로 선언하는 방식
  • 이 방식은 구조가 단순한 조인 테이블에 특히 유용함. 예를 들어 학생(Student)과 수업(Course)의 다대다 관계를 풀어내는 중간 테이블(StudentCourse)처럼, 복합 키만 정의하면 되는 테이블에서는 빠르고 간단하게 구현할 수 있음
  • 식별자 클래스와 엔티티 클래스 양쪽에 동일한 필드를 선언해야 함 → 코드 중복
  • 레거시 시스템을 다루거나 기존 데이터베이스 모델이 POJO 스타일로 구성된 경우
  • 필드의 개별 접근이 자주 필요하거나, 별도의 연관관계 없이 식별자만으로 조회하는 경우

 

식별자 클래스 생성
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

import jakarta.persistence.Embeddable;
import java.io.Serializable;

@Getter 
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
// @Embeddable 안 씀 !
public class OrderItemId implements Serializable {

    private Long orderId;
    private Long productId;
}

 

Entity 클래스
import jakarta.persistence.*;

@Entity
@IdClass(OrderItemId.class)
public class OrderItem {

    @Id
    private Long orderId;

    @Id
    private Long productId;

    private int quantity;
	
     /*
        연관관계 매핑(예: Order, Product) 추가 시
        @ManyToOne
    	@JoinColumn(name = "order_id", insertable = false, updatable = false)
    	private Order order;

    	@ManyToOne
    	@JoinColumn(name = "product_id", insertable = false, updatable = false)
    	private Product product;
    */
    
    // 기본 생성자, getter/setter 생략
}

@EmbeddedId 방식

  • 복합 키를 하나의 값 객체로 만들어, 해당 객체를 엔티티의 식별자로 사용하는 방식
  • 코드 중복이 없고 구조적으로 더 깔끔함
  • 객체지향적인 설계를 선호하는 경우
  • 엔티티와 연관관계 매핑이 필요한 경우 (@EmbeddedId + @MapsId) 
  • 복합 키 자체가 하나의 도메인 객체로 의미가 있을 경우 적합

 

복합 키 클래스 정의
import jakarta.persistence.Embeddable;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@Embeddable  // @EmbeddedId 방식일 때는 꼭 필요!
public class OrderItemId implements Serializable {

    private Long orderId;
    private Long productId;
}

 

Entity 클래스
import jakarta.persistence.*;

@Entity
public class OrderItem {

    @EmbeddedId
    private OrderItemId id;

    private int quantity;
	
    /*
        연관관계 매핑(예: Order, Product) 추가 시
        @ManyToOne
        @MapsId("orderId")
        @JoinColumn(name = "order_id")
        private Order order;

        @ManyToOne
        @MapsId("productId")
        @JoinColumn(name = "product_id")
        private Product product;
    */
   
    // 기본 생성자, getter/setter 생략
}

'📚 Study' 카테고리의 다른 글

인증과 인가, 그리고 JWT  (1) 2025.08.05