JPA

[JPA] 복합키 매핑 방법 정리 (EmbeddedId vs IdClass)

yemong 2026. 3. 16. 19:42
@Embedded란 무엇인가?
엔티티 내부에서 값 타입을 포함시켜 여러 필드를 하나의 객체로 묶어 사용하는 JPA 매핑 방식이다.
즉, 여러 컬럼을 하나의 객체로 묶어서 객체지향적으로 표현할 수 있도록 도와준다.

 

사용하는 이유

별도의 테이블을 생성하지 않고 엔티티 컬럼으로 매핑된다. 객체 지향적인 설계를 위해서 이러한 방식을 사용한다.

 

예를들면 아래의 코드와 같이 사용할 수 있다.

@Entity
public class User {

    @Id
    private Long id;

    @Embedded
    private Address address;
}
@Embeddable
public class Address {

    private String city;
    private String street;
    private String zipcode;
}

 

@Embeddable은?
여러 필드를 하나의 값 타입(Value Type)으로 묶어 엔티티에 포함시킬 수 있도록 하는 타입이다.

 


JPA에서 복합키를 설정하는 방법으로는 @EmbeddedId 방식과 @IdClass 방식, 총 두가지가 존재한다.

@EmbeddedId 방식은?
- 복합키에 해당하는 필드를 하나의 객체로 묶고,
해당 객체를 @EmbeddedId로 설정하여 복합키로 사용한다.
- @EmbeddedId를 사용할 때는 복합키 클래스에 반드시 @Embeddable을 선언해야 한다.
- 복합키 클래스는 equals와 hashCode를 반드시 구현해야 한다.

장점

- 복합키를 하나의 객체로 관리할 수 있다

단점

- PK 필드가 객체 내부에 존재하기 때문에, 엔티티에서 PK 필드를 바로 확인하기 어렵다

 

@Entity
@Table
public class Member {

    @EmbeddedId
    private MemberPK memberPK;

    @Column(name="phone")
    private String phone;

    @Column(name="address")
    private String address;
}

 

 

@Embeddable
public class MemberPK implements Serializable{

    @Column(name="member_no")
    private int memberNo;

    @Column(name="member_id")
    private String memberId;

    public MemberPK() {
    }

    public MemberPK(int memberNo, String memberId) {
        this.memberNo = memberNo;
        this.memberId = memberId;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        MemberPK memberPK = (MemberPK) o;
        return memberNo == memberPK.memberNo && Objects.equals(memberId, memberPK.memberId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(memberNo, memberId);
    }
}

테스트 코드를 통해 확인해보자.

    @Test
    public void 임베디드_아이디를_사용한_복합키_테이블_매핑_테스트() {
        Member member = new Member();
        member.setMemberPK(new MemberPK(1, "user01"));
        member.setPhone("010-1234-5678");
        member.setAddress("서울시 종로구");

        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        entityManager.persist(member);

        transaction.commit();

        /* 설명. 복합키는 하나의 타입(MemberPK)이어야 하며 
         * PK 비교 시 동등 비교가 가능(equals/hashCode overriding)해야 한다. 
         */
        Member foundMember = entityManager.find(Member.class, member.getMemberPK());
        Assertions.assertEquals(member.getMemberPK(), foundMember.getMemberPK());
    }

해당 코드는 @EmbeddedId를 사용한 복합키 매핑하고, 엔티티를 저장한 뒤 조회하는 테스트 코드이다.

 

@EmbeddedId 방식에서는 PK가 하나의 객체로 관리된다.

따라서 엔티티 조회 시에도 복합키 객체를 그대로 전달해야 한다.

 

그렇기 때문에 동등비교가 가능하도록 equals와 hashCode를 overriding해야한다.

 

@IdClass 방식은?
- 복합키에 해당하는 필드에는 각각 @Id를 설정해야 한다.
- @IdClass(MemberPK.class) 처럼 어떤 클래스가 복합키 타입인지 지정해야 한다.
- 복합키 클래스에서는 equals와 hashCode를 반드시 overriding 해야 한다.

 

장점

- PK 필드를 엔티티에서 바로 확인할 수 있어 가독성이 좋다.

- 복합키 객체를 거치지 않고 엔티티 필드를 바로 조회할 수 있어, JPQL 작성이 비교적 간단하다.

 

단점

- 복합키 필드를 엔티티와 PK 클래스 두 곳에 관리해야해서 중복 코드가 발생한다.

- 유지보수 시, 수정해야할 부분이 늘어난다 (PK 구조가 변경되면 엔티티와 PK 클래스 모두 수정해야 한다.)

@Entity
@Table
@IdClass(MemberPK.class)
public class Member {

    @Id
    @Column(name = "member_no")
    private int memberNo;

    @Id
    @Column(name = "member_id")
    private String memberId;

    @Column(name = "phone")
    private  String phone;

    @Column(name = "address")
    private String address;
}
public class MemberPK {
    private int memberNo;
    private String memberId;

    public MemberPK() {}

    public MemberPK(int memberNo, String memberId) {
        this.memberNo = memberNo;
        this.memberId = memberId;
    }

    @Override
    public int hashCode() {...}

    @Override
    public boolean equals(Object obj) {...}
}

 

테스트 코드로 확인해보자.

    @Test
    public void enum타입_매핑_테스트() {
        Member member = new Member();
        member.setMemberNo(1);
        member.setMemberId("user01");
        member.setPhone("010-1234-5678");
        member.setAddress("서울시 서초구");

        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        entityManager.persist(member);

        transaction.commit();

        Member foundMember = entityManager.find(Member.class, new MemberPK(1, "user01"));
        Assertions.assertEquals(member, foundMember);/* 동일 비교 */
    }

 

해당 코드는 @IdClass를 이용해 복합키를 매핑하고, 엔티티를 저장한 뒤 조회하는 테스트 코드이다.

 

엔티티 조회 시에는 복합키 클래스(MemberPK)를 이용해
PK 값을 전달하여 조회할 수 있다.

또한 복합키 클래스는 동일한 PK인지 비교할 수 있도록
equals와 hashCode를 반드시 오버라이딩해야 한다.
.

'JPA' 카테고리의 다른 글

[JPA] JPA의 장단점  (0) 2026.03.15