JPA 연관관계 매핑 (1)

김소연·2026년 4월 20일

연관관계

연관관계는 엔티티(테이블) 간의 관계를 의미합니다.

  • 예시
    • 학생과 학급: 한 학급에는 여러 학생이 있고, 한 학생은 하나의 학급에 속합니다
    • 주문과 상품: 한 주문에는 여러 상품이 있을 수 있습니다
    • 회원과 팀: 한 팀에는 여러 회원이 있고, 한 회원은 하나의 팀에 속합니다

Primary Key와 Foreign Key

데이터베이스에서 테이블 간의 관계를 설정하고 데이터의 무결성을 보장하는 가장 핵심적인 두 요소는 기본 키(Primary Key, PK)와 외래 키(Foreign Key, FK)입니다.

  • Primary Key

    기본 키는 테이블에 있는 각 행(row)을 유일하게 식별할 수 있는 고유한 값입니다.
    우리 모두가 고유한 주민등록번호를 갖고 있는 것과 같습니다.

    특징설명예시
    유일성테이블 내 모든 행은 서로 다른 키 값을 가져야 한다.주민등록번호
    NOT NULL기본 키 값은 절대 비어있을 수 없다.주민등록번호가 없는 대한민국 사람은 없다.
    불변성기본 키 값은 변경되면 안 된다.주민등록번호가 바뀌면 안 된다.
  • Foreign Key

    외래 키는 한 테이블의 컬럼이 다른 테이블의 기본 키를 참조하는 것을 말합니다. 즉, 테이블 간의 관계를 맺어주는 연결 고리 역할을 합니다.

    특징설명예시
    관계 설정두 테이블을 연결하여 의미 있는 데이터 관계를 만든다.Team과 Member는 관계가 있다.
    중복 허용외래 키 값은 기본 키와 달리 중복될 수 있다.하나의 Team은 여러 명의 Member를 가질 수 있다.
    NULL 허용외래 키 값은 비어 있을 수 있다.Team이 없는 Member가 있을 수 있다.

단방향 vs 양방향

가장 먼저 이해해야 할 것은 관계의 '방향'과 '주인'입니다. 방향과 주인은 연관관계의 기본 규칙입니다.

1. 단방향 관계

한쪽 객체에서만 다른 쪽 객체를 참조할 수 있는 관계입니다.

  • 💡 Member 자신이 속한 Team을 알지만, Team은 자신에게 속한 Member들을 모르는 관계입니다.

    @Getter
    @Entity
    @Table(name = "members")
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Member {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String username;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "team_id")
        private Team team;
        // ...
    }
    @Getter
    @Entity
    @Table(name = "teams")
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Team {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        // ...
    }

    이 경우 Member 엔티티만 조회하면 team_id FK를 통해 Team을 알 수 있습니다.

    이를 코드로 풀어보면 아래와 같습니다.

    member.getTeam();

    Member 테이블은 Team team을 필드로 가지고 있으므로 member.getTeam();이 가능합니다. 하지만 Team 테이블은 Member와 관련한 필드를 가지고 있지 않으므로, Team 엔티티 객체로 Member 조회는 불가능합니다.


2. 양방향 관계

양쪽 객체가 서로를 참조하는 관계입니다.

  • 💡 MemberTeam을 알고, Team 역시 자신에게 속한 Member 목록을 아는 관계입니다.

    @Getter
    @Entity
    @Table(name = "members")
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Member {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String username;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "team_id")
        private Team team;
        // ...
    }
    @Getter
    @Entity
    @Table(name = "teams")
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Team {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
    
        @OneToMany(mappedBy = "team")
        private List<Member> members = new ArrayList<>();
        // ...
    }

    이 경우 Member 엔티티를 조회하면 team_id FK를 통해 Team을 알 수 있고, 또한 Team 엔티티를 조회하여 members 필드를 통해 어떤 Member들이 팀에 속해있는지 알 수 있습니다.

    이를 코드로 풀어보면 아래와 같습니다.

    // 이전과 동일
    member.getTeam();

    Member 테이블은 Team team을 필드로 가지고 있으므로 여전히 member.getTeam();이 가능합니다.

    // Member 리스트를 조회할 수 있다.
    team.getMembers();

    Team 테이블은 Member와 관련한 필드로 private List<Member> members = new ArrayList<>(); 를 갖고 있으므로 Team 엔티티 객체로 Member 조회가 가능합니다.


📌 연관관계의 주인

단방향 관계에서의 연관관계의 주인은 경우의 수가 하나밖에 없습니다. 다른 쪽을 필드로 갖고 있는 엔티티 클래스가 하나 뿐이기 때문입니다. 위의 경우, Member 엔티티 클래스에

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;

이렇게 Team 필드가 정의되어있기에 Member가 연관관계의 주인입니다.

양방향 관계에서는 두 객체(Member, Team)가 서로를 참조합니다. 이때 JPA는 "둘 중 누가 DB의 외래 키(FK)를 관리할지" 정해야 하는데, 이 관리 권한을 가진 쪽을 연관관계의 주인이라고 합니다.

  • 주인의 규칙:
    외래 키(FK)를 가진 테이블과 매핑된 엔티티가 주인입니다.
  • 주인이 아닌 쪽:
    mappedBy 속성을 사용해 "나는 주인이 아니며, 주인의 어떤 필드와 연결되어 있는지"를 명시합니다. 이쪽에서의 관계 설정은 DB에 반영되지 않고, 오직 조회만 가능합니다.

쉽게 말하자면, @ManyToOne이 있는 쪽이 연관관계의 주인입니다.

0개의 댓글