[Spring] 연관관계

2025. 8. 29. 15:01·Spring

들어가며

엔티티들은 대부분 다른 엔티티와 연관관계가 있다. 예를 들어 주문 엔티티는 어떤 상품을 주문했는지 알기 위해 상품 엔티티와 연관관계가 있고, 상품 엔티티는 또 다른 엔티티와 관계가 있다. 그런데 객체는 참조(주소)를 통해 관계를 맺고 테이블은 외래키를 사용해서 관계를 맺는다. 이 둘은 완전히 다른 특징을 가진다. 그래서 우리는 이 객체 연관관계와 테이블 연관관계를 매핑해줘야한다. 객체의 참조와 테이블의 외래키를 매핑하는 것이 이번 포스트의 목표인 것이다. 

 

개념

  • 방향 : `양방향`, `단방향` 이 있다. 예를 들어 회원과 팀이 관계가 있을 때, 회원 → 팀 또는 팀 → 회원 둘 중 한 쪽만 참조하는 것을 '단방향' 관계라고 하고, 회원 → 팀, 팀 → 회원 양쪽 모두 서로 참조하는 것을 양방향 관계라 한다. 방향은 객체 관계에서만 존재하고, 테이블에서는 항상 양방향이다. 
  • 다중성 : `다대다`, `다대일`, `일대다`, `일대일` 가 있다. 예를 들어, 회원과 팀이 관계가 있을 때 여러 회원은 한 팀에 속하므로 회원가 팀은 다대일 관계이다. 반대로 한 팀에 여러 회원이 소속될 수 있으므로 팀과 회원은 일대다 관계이다. 
  • 연관관계의 주인 : 객체를 양방향 연관관계로 만들면, 연관관계의 주인을 정해야한다. 

 

참조한다 = 그 객체 안에 상대를 가리키는 필드가 있다는 뜻

'자식이 부모를 참조한다.' = 자식 객체가 부모 필드를 가진다. = 외래키가 자식 테이블에 있다. 

'부모가 자식을 참조한다.' = 부모 객체가 자식 컬렉션을 갖는다. = 객체에서 목록으로 접근하기 편하게 해준다. 


단방향 연관관계 

다대일의 관계부터 살펴보자. 회원과 팀은 다대일 연관관계를 가지고 있다. 

// 회원 엔티티
@Entity
public class Member {

    @Id
    @Column(name="member_id")
    private Long id;
    
    private String username;
    
    // 연관관계 매핑 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="team_id")
    private Team team;
 }
 
 // 팀 엔티티 
 @Entity 
 public class Team {
 
     @Id
     @Column(name="team_id")
     private Long id;
     
     private String name;
 }
  • `@ManyToOne` : 다대일 관계라는 매핑 정보 
  • `FetchType.LAZY` : JPA의 기본 값은 EAGER 이지만, 실전에서는 지연 로딩을 명시하는 것을 권장 
  • `@JoinColumn(name="team_id")` :  조인 칼럼은 외래키를 매핑할 때 사용, name 속성에는 매핑할 외래 키 이름을 작성

일대다 단뱡향 매핑

일대다 단뱡향은 거의 사용하지 않는다. 일대다 단방향 (`@OneToMany`)를 쓰면 자식 테이블에 외래키가 없어서 중간에 조인 테이블이 생기고, 그 때문에 insert나 join이 한 번씩 더 늘어서 비효율적이다. 

 

그래서 보통은 자식쪽에 외래키를 두고, `@ManyToOne` 단방향으로 간단하게 설계한다. 그리고 부모에서 자식으로 자주 찾게 되면 아래와 같이 `mappedBy` 를 사용해 양방향으로 추가한다. 


양방향 연관관계 

앞에서 단방향 연관관계에서는 회원에서 팀으로만 접근하는 다대일 단방향 매핑을 해보았는데, 이번에는 반대 방향인 팀에서 회원으로 접근하는 관계를 추가해보자. 이렇게 되면, 회원에서 팀으로도 접근이 가능하고 반대방향인 팀에서 회원으로도 접근이 가능하다. 

// 회원 엔티티
@Entity
public class Member {

    @Id
    @Column(name="member_id")
    private Long id;
    
    private String username;
    
    // 연관관계 매핑 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="team_id")
    private Team team;
 }
 
 // 팀 엔티티 
 @Entity 
 public class Team {
 
     @Id
     @Column(name="team_id")
     private Long id;
     
     private String name;
     
     @OneToMany(mappedBy = "team")
     private List<Member> members = new ArrayList<Member>();
 }
  •  컬렉션인 `List<Member> members` 추가
  • `@OneToMany` 매핑 정보와 `mappedBy`속성에는 반대쪽 매핑의 필드 이름 값을 사용

양방향 매핑의 규칙 : 연관관계의 주인 

양방향 연관관계 매핑 시에는 두 연관관계 중 하나를 연관관계의 주인으로 정해야 한다. 연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리할 수 있다. 반면, 주인이 아닌 쪽에는 읽기만 할 수 있다. 어떤 연관관계의 주인으로 정할지는 `mappedBy` 속성을 사용하면 된다. 

 

→ 연관관계의 주인은 '외래키'가 있는 곳 으로 한다. `@JoinColumn` 이 있는 쪽을 주인으로 정한다. 


다대다 연관관계는 사용하지 않는다. 

`@ManyToMany`는 사용하지 않는다. 대신 중간에 조인 엔티티를 둬서 설계한다. 예를 들어, 회원과 팀이 다대다 관계에 있다면, `Member_Team` 과 같은 엔티티를 하나 만든다. 이렇게 역할이나 가입일 같은 추가 칼럼을 자연스럽게 담을 수 있고, 조회나 쓰기 과정도 쉬워진다. 

@Entity
public class MemberTeam {
    @Id 
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "member_id")
    private Member member;

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

    private Role role;        
    private LocalDateTime joinedAt;
}

// Member에 추가 
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<MemberTeam> memberTeamList = new ArrayList<>();

// Team에 추가 
@OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
private List<MemberTeam> memberTeamList = new ArrayList<>();

 

'Spring' 카테고리의 다른 글

[Spring] Setter 사용을 지양해야하는 이유  (0) 2025.08.29
[Spring] DTO class를 record로 사용하는 이유  (0) 2025.08.29
[Spring] PSA (Portable Service Abstraction)  (0) 2025.08.27
[Spring] @Transactional 개념과 사용  (0) 2025.08.19
[Spring] 프록시(Proxy)  (0) 2025.08.13
'Spring' 카테고리의 다른 글
  • [Spring] Setter 사용을 지양해야하는 이유
  • [Spring] DTO class를 record로 사용하는 이유
  • [Spring] PSA (Portable Service Abstraction)
  • [Spring] @Transactional 개념과 사용
erika0915
erika0915
백엔드 개발자가 되고 싶어요 .
  • erika0915
    erikoding
    erika0915
  • 전체
    오늘
    어제
    • 분류 전체보기 (78)
      • 프로젝트 (13)
        • 끼니콩 (3)
        • 덕메랑 (3)
        • handDoc (7)
        • Haeil (0)
      • Java (9)
        • 클린코더스 (0)
      • Spring (30)
      • Redis (3)
      • CS (7)
        • 운영체제 (3)
        • 컴퓨터구조 (0)
        • 네트워크 (4)
      • DevOps (2)
      • 코딩테스트 (0)
      • Tech (14)
        • TDD (1)
        • 정리 (5)
        • 우테코 (0)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

    스프링
    springboot
    운영체제
    스프링부트
    지라
    Spring
    MongoDB
    몽고디비
    AI
    jira
    TDD
    OS
    파인튜닝
    자바
    레디스
    STT
    깃
    네트워크
    git
    깃허브
    CoolSMS
    java
    docker
    redis
    github
    도커
    Network
    코드레빗
    promtail
    coderabbit
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
erika0915
[Spring] 연관관계

티스토리툴바