[JPA] 외래키 주인으로 알아보는 엔티티 연관관계

2025. 9. 17. 20:54·Back-End

우리는 프로젝트를 진행하며 DB 스키마 구조에 따른 많은 연관관계를 맺는다. 일대일, 다대일, 다대다 등 현실에서 맺어지는 관계성을 DB의 연관관계로 매핑하여 데이터를 정규화해 효율적으로 관리할 수 있다.

 

자바/스프링 프로젝트를 하게 되면 Spring Data JPA를 주로 사용하는 경우에는, 엔티티레벨에서 연관관계를 매핑하고 이에 따라 단방향, 양방향 등의 구성을 하게 된다. 그런데, 책을 보거나 강의를 듣게 되면 외래키 주인이라는 키워드가 나오게 된다. 실제 프로젝트를 진행하면서 한번도 고려해보지 않던 부분들을 한 번 생각해보려 한다.

 

외래키 주인(연관관계의 주인)

JPA에서 연관관계의 주인이라고 불리는 외래키 주인은 양방향 연관관계에서 실제로 외래키를 관리하는 테이블(엔티티)를 의미한다. 

 

예를 들어 책과 작가라는 테이블의 연관관계를 매핑한다면 "1명의 작가가 여러 권의 책을 출간할 수 있다. 하지만 한 권의 책은 1명의 작가만 존재할 수 있다" 라고 생각해서 1:N의 연관관계를 설정할 수 있다.

 

이 때, DB의 관점에서 본다면 외래키는 책이라는 테이블에 `author_id`와 같은 형태로 존재해 작가 테이블을 참조하는 방식으로 설계할 것이다.

 

하지만 JPA의 관점 즉 엔티티에서 DB의 양방향 연관관계를 매핑하는 부분으로 관점을 바꿔보면 상황이 달라진다. 작가 엔티티도 자신이 작성한 책 목록을 List<Book>의 형태로 가질 수 있으며, 책 엔티티도 Author라는 형태로 작가의 정보를 가질 수 있다. 즉 객체 레벨에서는 양쪽 모두 서로를 참조할 수 있게 되는 것이다.

 

우리가 외래키를 관리하는 연관관계의 주인이 누구인지 JPA에게 명확하게 알려주지 않는다면, JPA는 어느 쪽의 변경 사항을 실제 DB의 외래키에 반영해야하는지 알 수 없게 되는 것이다. 따라서, 이를 명확하게 하기 위해 '연관관계 주인'이라는 개념을 통해, 연관관계의 주인만이 외래키의 값을 변경할 수 있고, 주인이 아닌 쪽은 변경하지는 못하며 단순히 조회하는 것만 가능해진다.

 

외래키 주인만이 외래키를 설정할 수 있다.

엔티티 레벨에서 일반적으로 외래키의 주인은 N(다)의 관계인 엔티티이지만, 1:1 연관관계에서는 직접적으로 외래 키의 주인을 지정해주어야 한다. 우리가 사용하는 `@JoinColumn()` 어노테이션은 외래키의 주인만이 사용하는 어노테이션으로 컬럼명, null 여부, unique 여부 등을 지정할 수 있다.

 

@Entity
@Getter
@Table(name = "book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private int price;
    @OneToOne
    @JoinColumn(name = "author_id")
    private Author author;
}

@Entity
@Getter
@Table(name = "author")
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToOne(mappedBy = "author")
    private Book book;
}

이렇게 1:1 연관관계에서 양방향 매핑을 진행했다. 위에서 설명한대로 외래키 주인인 Book의 경우 `@JoinColumn` 어노테이션을 가지며 외래키 주인이 아닌 Author의 경우 `mappedBy` 옵션을 통해 어떤 필드와 매핑되는지를 설정해주었다.

 

첫 번째 테스트이다.

@Test
void test1() {
        Author author = new Author();
        author.setName("테스트 작가");

        Book book = new Book();
        book.setTitle("테스트 책");
        book.setPrice(1000);
        book.setAuthor(author);

        authorRepository.save(author);
        bookRepository.save(book);
}

작가를 먼저 설정하고, 연관관계의 주인인 책이 어떤 작가와 연관되어 있는지를 설정한다. 결과는 아래와 같다.

정상적으로 외래키가 설정된 모습을 확인할 수 있다.

 

그렇다면 외래키 주인이 아닌 author에서 설정한다면?

@Test
void test2() {
        Book book = new Book();
        book.setTitle("테스트 책");
        book.setPrice(1000);

        Author author = new Author();
        author.setName("테스트 작가");
        author.setBook(book);

        authorRepository.save(author);
        bookRepository.save(book);
}

작가가 정상적으로 DB에 반영이 되어 있음에도, author_id가 설정되지 않았다. 외래키의 주인이 아니기 때문에 외래키에 관한 변경을 저장할 수 없고 단순 조회 기능만 가능하기 때문이다.

 

진짜 내가 무슨일이 있어도 외래키의 주인이 아닌 Author을 통해서만 외래키를 설정해야 한다면 다음처럼 설정해주면 된다.

@Entity
@Getter
@Table(name = "author")
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToOne(mappedBy = "author")
    private Book book;

    public void addBook(Book book) {
        this.book = book;
        book.setAuthor(this);
    }
}

이렇게 Author 내부에서 book의 setAuthor을 호출해 현재 객체를 설정해주면 된다.. 그런데 굳이 이렇게까지 해야할 상황이 존재하지는 않을 것이라 생각한다.

 

프로젝트를 진행하면서도, 외래키를 설정하기 이전에 이미 Author객체를 만들고 Book 생성자에 이를 전달하는 방식을 통해서 진행했었기 때문에 별도로 외래키 주인이 아닌 곳에서 `addBook()` 메소드와 같은 방식으로 설정할 일이 없었다. 하지만 개념을 알고 사용하는 것과 알지 못하는 것은 예상치 못한 에러 상황을 만났을 때 대처능력이 달라진다고 생각해서 한 번 정리해보게 되었다.

 

3줄 요약으로 단순하게 이야기 하면 다음과 같다.

  1. `@JoinColumn()` 어노테이션을 가진 쪽이 외래키 주인이다.
  2. `mappedBy` 옵션을 설정한 쪽은 외래키 주인이 아니다. 단순 조회용으로 양방향 설정한 것이다.
  3. 외래키 설정은 외래키 주인 엔티티에서만 가능하다.

 

 

'Back-End' 카테고리의 다른 글

비동기 메시징 방식(RabbitMQ vs Kafka)  (0) 2025.09.19
@Transactional(readOnly=true)는 왜 써야하는걸까?  (0) 2025.09.18
테스트 코드 작성 전략  (1) 2025.09.16
프로젝트 성능 개선기(nGrinder을 활용한 부하 테스트)  (0) 2025.04.02
트래픽이 많아지는 상황에서 나는 어떻게 대처할 수 있을까?  (0) 2025.02.17
'Back-End' 카테고리의 다른 글
  • 비동기 메시징 방식(RabbitMQ vs Kafka)
  • @Transactional(readOnly=true)는 왜 써야하는걸까?
  • 테스트 코드 작성 전략
  • 프로젝트 성능 개선기(nGrinder을 활용한 부하 테스트)
dev_Mins
dev_Mins
  • dev_Mins
    천천히 빠르게!
    dev_Mins
  • 전체
    오늘
    어제
    • 분류 전체보기 (49)
      • 42Seoul (2)
      • Back-End (20)
        • Spring (8)
      • Project (14)
        • PickLab (3)
      • 끄적끄적 (3)
      • Algorithm (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Kafka
    Spring Boot
    AWS
    Spring Security
    주문 서비스
    스프링 게이트웨이
    MSA Gateway
    로드밸런싱
    42서울
    JWT
    Spring Data JPA
    Servcie Discovery
    재고 서비스
    Spring Cloud
    로드밸런서
    MSA
    스프링 jwt
    스프링
    Spring
    42seoul
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
dev_Mins
[JPA] 외래키 주인으로 알아보는 엔티티 연관관계
상단으로

티스토리툴바