(TIL) 20220630, JPA의 연관관계 매핑(OneToMany, ManyToOne)

2022. 7. 2. 02:00TIL(Today I learned)

반응형

🏴󠁩󠁤󠁪󠁷󠁿Facts(한 것) & Findings(배운 것)


오늘은 트레바리 면접을 봤다.

 

면접에서 객체지향, 클린 아키텍처, 도메인 주도 설계 등 다양한 얘기를 했다.

 

결과 여하에 무관하게 아주 재밌는 면접이었다.

 

전형적인 CS 질문과는 거리가 있으면서도 설계에 관한 얘기를 많이 했고,

 

계층형 아키텍처에 관한 얘기, 헥사고날 아키텍처에 관한 얘기도 했다.

 

내 실력이 부족해서 완벽하게 답한 것 같진 않지만 좋은 경험이었다.

 


아침에 트리플 과제를 완성해서 제출했다.

 

과제의 분량이 많지는 않았는데, 중간중간 바쁜 일이 생겨서 3일이 지나고서야 제대로 시작했으니,

 

3-4일 만에 완성한 꼴이 됐다.

 

그래도 결과는 꽤 만족스럽다.

 

과제를 진행하면서 오히려 더 공부가 된 것 같은데

 

JPA 매핑부터, 인덱스, DB 설정 등 다양하게 진행했으니...

 

혹시나 결과물이 궁금하신 분은 아래 깃으로 들어가면 된다.

 

 

 

GitHub - mikekang47/triple_submission

Contribute to mikekang47/triple_submission development by creating an account on GitHub.

github.com


단일 도메인 혹은 여러 도메인을 사용하더라도 연관관계가 없는 도메인으로 DB를 접근하는 프로젝트만 한 사람의 경우

 

도메인 간 연관관계를 처리하는 데 있어서 JPA를 어떻게 활용해야 할지 모르는 분들이 많은 것 같다.(나도 잘 몰랐다.)

 

그래서 도메인 간 DB 연관관계에 대해서 정리해보려고 한다.

 

 

현재 대부분의 저장소는 RDB를 사용하고 있고, RDB는 Relational이라는 이름에 맞게 테이블 간의 연관관계로 이루어져 있다.

 

그럼 왜 연관관계로 이루어져 있을까? 그냥 한 테이블에 다 저장하면 되지?

 

 

한 테이블에 저장하게 되면 덩치가 커지게 되고, 덩치가 커진다는 것은 데이터의 용량이 커진다는 것이다.

 

데이터의 용량이 크다는 것은 결국 저장소에 많은 공간을 필요로 하게 되고, 이는 비용을 유발한다.

 

 

하지만 이것보다 더 중요한 것이 있다.

 

바로 중복이다.

 

코드를 작성해본 사람들은 알 것이다. 중복이 얼마나 무서운 것이며, 피하고 싶은 것인지.

 

 

한 테이블에 데이터를 밀어 넣게 되면 필연적으로 중복이 발생하고 이 중복을 제거하기 위해서 우리는 RDB를 사용하게 된다.

 

 

 

그리고 도메인의 연관관계로 바로 이어지기에는 조금 괴리감이 있지만 중간 과정은 따로 서술하도록 하고 

 

매핑부터 알아보려 한다.

 

 

먼저 객체 연관관계에는 크게 4가지가 있다.

 

OneToOne, OneToMany, ManyToOne, ManyToMany

 

주로

 

1:1, 1:N, N:1, N:M으로 표기하곤 한다. 

 

이 글에서는 일대다, 다대일까지만 알아보려 한다.

 

 

먼저 OneToOne을 예시를 통해서 알아보자.

 

나는 좀 특별한 예시를 쓰고 싶어서 학교와 교장 선생님을 예시로 들겠다.

 

한 학교에는 한 명의 교장 선생님만 있다. (교장 선생님이 2명 이상인 학교가 있다면.... 난 본적이 없다.)

 

그리고 교장선생님은 한 학교에만 속한다. (A 학교의 교장이면서 동시에 B학교의 교장일 수 없는 것이다.)

 

이것이 1:1 일대일 관계이다.

 

 

다음은 1:N, 일대다 관계이다.

 

 

일대다는 주로 팀과 맴버로 많이 설명하는데, 비슷한 예시로 반과 학생으로 예시를 들어볼까 한다.

 

 

한 반에는 여러명의 학생이 있을 수 있다. 

 

 

학생은 한 반에만 속한다.

 

 

이를 반의 입장에서 생각하면 한 반에 여러명의 학생이 있으므로, 1:N이다.

 

 

그럼 그 반대는 어떨까? 

 

여러 학생은 한 반에 속하기 때문에 N: 1이다. 즉 다대일 관계인 것이다.

 

 

일대다의 반대는 항상 다대일이고, 일대일의 반대는 항상 일대일이다.

 

 

일대다와 대다일이 가장 많이 쓰이기 때문에 코드를 중심으로 살펴보자.

 

@AllArgsContructor
@NoArgsContructor
@Entity
class Student {
    @Id 
    @GeneratedValue
    private Long id;
    
    private Class class_;
    
}
@AllArgsContructor
@NoArgsContructor
@Entity
class Class {

    @Id
    @GeneratedValue
    private Long id;
    
    private List<Student> students = new ArrayList<>();
    
 }

(Getter와 Setter는 생략했다.)

 

이렇게 학생 클래스와 반 클래스를 생성했다.

 

반은 여러명의 학생을 가질 수 있기 때문에 맴버 변수로 List형태의 학생을 가진다.

 

그리고 학생은 한 반에만 속할 수 있기 때문에 맴버 변수로 반을 가진다.

 

그럼 여기서 필연적으로 FK가 발생하는데, 이 FK를 누가 관리를 하는가?

 

 

FK는 반드시 주인 클래스가 관리한다. (이 이야기는 다음에....)

 

그럼 어떤 객체가 주인 클래스일까?

 

 

항상 1: N, N:1에서 N쪽이 주인 클래스이다.

 

 

그럼 반과 학생 중에서 어떤 곳이 N인가?

 

여러명의 학생이 반에 속함으로 학생이 N이다.

 

그렇기 때문에 FK를 학생 클래스에서 관리해야한다.

 

학생 클래스 입장에서는 ManyToOne이고, 반 입장에서는 OneToMany이기 때문에 각각 어노테이션을 붙여준다.

 

그리고 주인 클래스가 아닌 클래스에 mappedBy를 붙여줌으로써 주인 클래스가 아님을 알려야한다.

 

@AllArgsContructor
@NoArgsContructor
@Entity
class Student {
    @Id 
    @GeneratedValue
    private Long id;
    
    @ManyToOne
    private Class class_;
    
}
@AllArgsContructor
@NoArgsContructor
@Entity
class Class {

    @Id
    @GeneratedValue
    private Long id;
    
    @OneToMany(mappedBy = "class_")
    private List<Student> students = new ArrayList<>();
    
 }

 

이렇게 말이다.

 

이렇게 하면 절반이상 성공한 것이다.

 

추가적으로 데이터 즉시로딩이냐 지연로딩이냐 등을 설정해야하지만 

 

기본적으로 큰틀은 변하지 않는 다는 것을 알고 공부하면 좋을 것 같다.

 

🏴󠁩󠁤󠁪󠁷󠁿Affirmation(자기선언)


 

🏴󠁩󠁤󠁪󠁷󠁿여담


 

반응형