[Lv.3,4 - 팀프로젝트 : 직고래] 연관관계 설계

2020. 10. 4. 21:42일상/우아한테크코스

우아한테크코스의 레벨3,4는 팀 프로젝트를 진행하는 기간인데요,

저는 직고래라는 조직 중고 거래 서비스를 개발하고 있습니다.

이번 포스팅에서는 직고래의 설계 과정에 대해 공유해보고자 합니다.

서비스에 대한 자세한 설명은 소개사이트를 참고해주세요.

 

조직 내에서 간편하게 중고 거래를!

1. 개요

직고래 프로젝트를 경험하며 가장 어려웠던 건 아무래도 엔티티 간의 연관관계 설계였던 것 같습니다. 직고래 서비스는 회원이 조직에 가입하여 게시글을 작성하고, 가입한 조직별로 게시글을 조회할 수 있는 다양한 기능이 있습니다. 이번 포스팅에서는 초기 설계 당시 즉, 조직에 대한 도메인이 없던 당시 회원과 게시물에 대한 연관관계 설계를 어떻게 했는지, 또 조직이라는 도메인이 기획에 추가된 후 어떤 설계 과정을 거쳤는지에 대해 공유해보고자 합니다.

2. Level3 초반의 엔티티 설계

 회원(Member)  게시글(Article) 의 관계에서 필요한 요구사항은
• 게시글을 누가 작성했는지
• 회원이 작성한 게시글은 무엇인지

이며, 이를 위해서는 아래와 같은 방법이 있습니다.

1. 회원이 작성한 게시글을 안다. (일대다 단방향)

2. 게시글이 작성자를 안다. (다대일 단방향)
3. 둘 다 안다. (다대일 양방향)

이 관계들 중 어떤 관계를 택하면 좋을지 하나씩 살펴보겠습니다.


1) 회원이 작성한 게시글을 안다. 

일대다 단방향

장점
•Member가 가진 List<Article>을 통해 작성한 게시글들을 바로 알 수 있습니다. 


단점

•연관관계를 매핑한 객체(Member)가 관리하는 외래키(member_id)가 다른 테이블(Article)에 존재하여  추가적인 update 쿼리 가 발생합니다.

   • 일대다단방향 관계가 형성되면 Member테이블의 articles로 연관관계가 매핑됩니다.( @OneToMany, @JoinColumn )

    • 하지만 테이블은 다쪽 테이블(Article)에 외래키(member_id)를 가집니다.

    • 즉,  연관관계 매핑 테이블이 아닌 다른 테이블의 외래키를 관리 해야합니다.
    • 연관관계 매핑 테이블에 외래키가 있으면 엔티티의 저장과 연관관계의 처리를 insert SQL 한 번으로 끝낼 수 있지만,

    • 다른 테이블에 외래키가 있으면 연관관계 처리를 위한 update SQL을 추가로 실행해야합니다.

-- 1. member가 참조하는 articles를 저장한다. 
insert into Article (article_id, title, contents) valuses (null, ?, ?)
insert into Article (article_id, title, contents) valuses (null, ?, ?)

-- 2. member를 저장한다.
insert into Member (member_id, nickname) valuses (null, ?)

-- 3. articles의 외래키 컬럼인 member_id를 업데이트한다.
update Article set Member_Id=? where article_id = ?
update Article set Member_Id=? where article_id = ?

• 다른 방향에 대해 객체 그래프 탐색이 불가능해짐에 따라 직접 조회 메서드를 생성해야 합니다.

2) 게시글이 작성자를 안다.

 

다대일 단방향

장점
• 연관관계의 주인과 외래키를 가진 매핑테이블이 일치합니다.

    • 추가적인 쿼리가 나가지 않습니다.

•Article이 가진 Member를 통해 작성자를 바로 알 수 있습니다.

단점
• 다른 방향에 대해 객체 그래프 탐색이 불가능해짐에 따라 직접 조회 메서드를 생성해야 합니다.


3) 둘 다 안다. 

다대일 양방향


장점

• 연관관계의 주인과 외래키를 가진 매핑테이블이 일치합니다.

    • 추가적인 쿼리가 나가지 않습니다.

•객체 그래프 탐색을 통해 원하는 데이터를 쉽게 가져올 수 있습니다.

    • Member가 가진 List<Article>을 통해 작성한 게시글들을 바로 알 수 있습니다.

    • Article이 가진 Member를 통해 작성자를 바로 알 수 있습니다.


단점
• 양방향의 특성상 로직을 견고하게 작성해야 하기 때문에 실수할 여지가 있습니다.

    • 연관관계의 주인 을 정해주어야 합니다.
    • 순수한 객체 설정까지 고려해야 합니다.
        • 이를 위해서는 편의 메서드를 작성해주어야 하고
        • 편의 메서드 내에서도 관계를 제대로 설정하기 위한 로직이 추가되어야 합니다.
            • 이전 관계는 지우는 작업과 같은 관계를 맺고 끊는 작업
    • 순환 참조를 조심해야합니다. (toString(), 편의메서드 작성시) 

4) 결론

일대다 단방향에서 발생하는 추가쿼리는 데이터가 많아질수록 성능상의 문제가 발생하기 때문에 제외하겠습니다.

양방향으로 구현하여 "잘" 구현한다고 하더라도, 객체지향적으로 봤을 때 서로간의 의존은 불필요해보입니다.

따라서 다대일 단방향으로 구현 후, 반대편의 객체 탐색이 필요해질때 양방향 관계로 변경하는 것으로 결론을 내렸습니다.

 

3. Level4 추가된 엔티티에 대한 연관관계 설계

레벨4에 조직 기능이 추가되었습니다.
 게시글(Article)  조직(Organization) 의 관계에서 필요한 요구사항은
• 게시글은 하나 이상의 조직에 속해야한다.
• 게시글이 생성,삭제 되면 게시글이 속한 조직에서도 생성,삭제 되어야 한다.

• 조직마다 게시글을 볼 수 있어야 한다.
이며, 이를 위해서는 아래와 같은 방법이 있습니다.

1. 게시글이 조직을 안다. 혹은 조직이 게시글들을 안다. (다대다 단방향)
2. 둘 다 안다. (다대다 양방향)

이 관계들 중 어떤 관계를 택하면 좋을지 하나씩 살펴보겠습니다.

 

1) 게시글이 조직을 안다. 혹은 조직이 게시글들을 안다.

다대다 단방향

장점

• 자동으로 연결 테이블이 생성됩니다.

    •도메인 모델이 단순해지고, 연결 테이블을 신경쓰지 않아도 됩니다.


단점

• 자동으로 연결 테이블이 생성됩니다.

    • 자동으로 연결테이블이 생성되기 때문에 확장에 유연하지 않습니다.

        • 예를 들어 조직내 게시글들에 대한 추가 정보가 필요하게 되면 기존 연결 테이블을 사용할 수 없게됩니다. 

 

2) 둘 다 안다.

다대다 양방향


장점
• 객체 그래프 탐색이 용이합니다.


단점

• 위에서 살펴보았던 양방향의 단점(견고한 로직 필요)과 다대다의 단점이 모두 존재합니다.

 

3) 결론

위의 두 경우 모두 다대다기 때문에 자동으로 생성되는 연결테이블에 대한 단점이 존재합니다. 하지만 조직이라는 요구사항이 추가되었듯 사용자의 요구사항은 언제 어떻게 변할지 모릅니다. 때문에, 다대다 테이블의 한계를 극복하기 위해 연결테이블을 직접 만들어주는 방법을 택하였습니다. 

여기서 한 가지 더 짚고 갈 것은 직접 생성한 연결 테이블의 기본키는 Organization과 Article의 기본키로 이루어진 복합키가 아닌, 새로운 연결 테이블의 식별자를 만들어주었다는 점입니다. (복합키를 사용하여 식별 관계로 구성하게 된다면 식별자 클래스를 만들어야 하는 등의 번거로운 작업들이 발생하기 때문입니다)

연결엔티티를 통한 다대일 단방향

최종적으로 이렇게 연결엔티티를 통해 다대일 단방향의 관계로 설계하였습니다.

 

아래는 실제 연결엔티티 클래스인데요, 이를 통해 게시글과 조직 사이에 또 다른 요구사항이 추가되거나 속성이 추가되어야 할 때 칼럼을 자유롭게 추가할 수 있음을 알 수 있습니다. 

빨간줄은 assign data source해도 안없어지네요(머쓱)


이렇게 직고래의 몇가지 핵심 엔티티간의 연관관계에 대해 알아보았는데요, 혹시라도 잘못됐거나 궁금한 내용이 있다면 댓글 부탁드립니다.🙂