일대일 관계, 일대다 관계에 대해서는 자세히 다룬 적 있지만, 한번도 다대다 관계에 대해 자세히 설명한 적은 없습니다. 이번 포스팅에선, 다대다 매핑을 어떻게 처리하는지 알아보도록 하겠습니다.
두 개의 테이블이 서로의 행에 대해서 여러 개로 연관되어 있는 상태를 다대다 또는 M:N 관계라고 한다. 구체적인 예시로, 대학생이 강의를 듣는 상황을 생각해볼 수 있다. 한명의 학생은 여러 개의 강의를 들을 수 있고, 하나의 강의는 여러 명의 학생을 수용할 수 있다. 이 때, 학생과 강의의 관계가 바로 다대다 관계이다. 다대다 관계에 대한 설명을 지금껏 미뤄둔 이유는 일대일 매핑 또는 일대다 매핑과는 조금 다른 특징을 갖기 때문이다.
다대다 관계가 조금 다른 특징을 가지고 있다고 말하는 이유는 2개의 테이블로 관계를 매핑할 수 없기 때문이다. 그 이유는 조금만 생각해봐도 쉽게 알 수 있다. 만약 학생 테이블의 기본키를 강의 테이블에서 관리하고, 강의 테이블의 기본키를 학생 테이블에서 관리하면 어떻게 될까?
변현섭 학생과 김유리 학생 모두 알고리즘 설계 과목을 수강한다. 그러다보니 알고리즘 설계라는 강의가 강의 테이블에서 중복이 발생한다. 또한 변현섭 학생이 강의를 3개나 듣기 때문에 학생 테이블에서도 중복이 발생하고 있다. 다시 말해 중복 데이터를 최소화할 수 있다는 관계형 데이터베이스의 장점을 전혀 활용하지 못하고 있는 것이다. 그럼 어떻게 해결할 수 있을까?
정답은 두 테이블 사이의 연결 테이블을 두어 총 3가지 테이블을 이용하는 것이다.
이제 더 이상 학생 테이블에서 강의ID를 관리할 필요가 없고, 반대로 강의 테이블에서도 학생ID를 관리할 필요가 없다. 다만, 학생 테이블은 연결 테이블을 일대다로 참조하고, 강의 테이블도 연결 테이블을 일대다로 참조한다. 즉, 우리가 이전에 배웠던 일대다 매핑 방식을 그대로 사용하여 다대다 관계 매핑을 만들 수 있다는 것이다.
그렇다면 학생 테이블과 강의 테이블의 관계에서 누가 부모이고 누가 주인일까? 결론부터 이야기하면, 다대다 관계는 논리적인 관계일 뿐, 실제 구현단계에서는 사용되지 않기 때문에 주종관계와 부모관계를 따질 필요가 없다. 다대다 관계는 연결 테이블을 이용해 일대다 관계로 풀어내기 때문에 주종관계와 부모관계는 일대다 매핑에서와 동일하게 적용된다.
다시 말해, 학생 테이블과 연결 테이블의 관계에서는 연결테이블이 주인이 되고, 학생 테이블이 부모가 된다. 마찬가지로 강의 테이블과 연결 테이블의 관계에서는 연결테이블이 주인이 되고, 강의 테이블이 부모가 된다. 그리고 이 때, 학생 테이블과 강의 테이블의 관계는 굳이 따질 이유가 없다.
이론적인 내용을 이해했고, 일대다 매핑을 구현할 수 있다면, 다대다 매핑을 구현하는 일은 그렇게 어려운 일이 아니다. 간략하게만 구현 방식을 설명하고 포스팅을 마치도록 하겠다. 사실 다대다 매핑을 구현할 일이 그렇게 많지는 않은데, 그나마 프로젝트를 진행하면서 다대다 매핑을 구현할 일이 생긴다면, 아마 가장 대표적인 예시가 유저와 채팅방의 관계이지 않을까 싶다.
한 명의 유저는 여러 개의 채팅방을 가질 수 있고, 하나의 채팅방은 여러 명의 유저를 수용할 수 있다(그룹 채팅 기능을 제공하는 경우). 채팅서버를 구현하는 방법에 대해서는 다른 포스팅에서 다루기로 하고, 오늘은 관계 매핑에 대해서만 주의 깊게 살펴보기로 하자.
실시간 채팅을 구현하는 방법은 아래의 링크를 참조하기 바란다.
>> 실시간 채팅 구현하기
일대다 매핑을 하던 방식 그대로 연결테이블을 매핑해주면 된다.
동일한 방식으로 매핑을 진행한다.
연결 테이블은 다대일 매핑 방식으로 User와 ChatRoom을 매핑하면 된다.
※ 연결테이블 네이밍 규칙
연결테이블의 이름은 일반적으로 연결하고자 하는 두 엔티티의 이름을 연결하여 짓는 것이 관례이다. 또한 사람과 사물에 대한 연결테이블인 경우, 사람을 먼저 쓴다. 반드시 지켜야 하는 것은 아니지만, 네이밍 규칙을 따르는 편이 좋다.
설정을 완료하면, 다대다 매핑이 완성된다. 이제 한 명의 유저가 여러 개의 채팅방을 가질 수 있고, 하나의 채팅방에 여러 명의 유저를 수용할 수 있다.
참고로, 유저 엔티티와 채팅방 엔티티에서는 연결 테이블에 대한 정보를 직접 관리하지 않는다. 즉, 유저와 채팅방 테이블에는 연결 테이블에 대한 필드가 존재하지 않는다는 것이다. 이는 유저 테이블에 게시글을 관리하는 필드가 존재하지 않는 것과 같은 이유이다.