본문 바로가기
프로젝트/팀프로젝트 qp편

JPA와 테이블 설계

by Thumper 2024. 3. 22.

이번에는 스터디 프로젝트를 만들 때 JPA로 도메인 모델을 어떻게 구성하고 객체와 테이블을 어떻게 매핑했는지 정리하려고 한다.
모든 코드는 Github에 있으니 참고해주세요.

먼저 요구사항을 분석하고 도메인 모델과 테이블을 정리해보자.

요구사항 분석

핵심 요구사항은 다음과 같다.

  • 회원은 스터디방을 개설할수 있다.
  • 스터디방 개설할 때 여러가지 태그를 선택할 수 있다.User
  • 사용자의 구분은 "미인증 사용자", "회원가입된 사용자", "관리자"로 나뉜다.
  • UNAUTH(미인증), AUTH(인증), ADMIN(관리자)로 3가지 존재한다.
    미인증 회원은 회원가입을 통해 로그인하여 AUTH로 변경할 수 있다.
  • 회원가입할 때, 원하는 스터디 태그를 선택할 수 있다.
  • 관리자는 BAN(정지) 권한으로 회원 관리를 할 수 있다.Room
  • 스터디 방 생성/수정/삭제
  • 스터디/프로젝트 상태 표시는 OPEN(모집중), CLOSED(모집 완료)으로 구분된다. 모집 상태에 따라 status가 변경된다.
  • 스터디 시간을 등록하여, 일정 스터디 시간을 달성하면 코인을 획득할 수 있다.
  • 스터디방을 태그로 검색할 수 있다.Chat
  • 채팅창에 여러 명의 회원이 참여할 수 있다.
  • 스터디방마다 채팅창이 있으며, 스터디에 참여한 회원끼리 채팅 가능하다.Tree (이벤트)
  • 획득한 코인을 통해, 자신의 트리를 꾸밀 아이템을 획득할 수 있다.
  • 원하는 아이템으로 트리를 꾸밀 수 있다.

Tree (이벤트) 경우는 테이블이 따로 없다.

User의 studyTime 필드로 스터디 시간 정보를 받아 코인을 획득할 수 있다.

트리 부분은 프론트단에서 간단하게 만들기로 한 부분이라, 테이블을 정의하지는 않기로 했다.


요구사항을 분석해서 만든 화면 일부는 다음과 같다.

tag tag

프론트팀원분들이 만든 피그마 파일 일부를 첨부했다.

그림과 같이 스터디방 정보로 스터디 태그, 썸네일, 제목, 인원 현황 등등 데이터를 담도록 할 것이다.

테이블 설계

요구사항을 기반으로 설계한 테이블 ERD다.

그림 ERD를 분석해보자.

user_base

그림을 보면 엔티티를 상속 관계로 만들고 공통 속성은 user_base 엔티티에 두었다.
그리고 요구사항대로 user(회원가입된 사용자), admin(관리자) 자식 엔티티를 추가했다.

상속 관계를 테이블 하나에 통합하는 단일 테이블 전략을 선택했다. 따라서 user_base 테이블 하나만 사용하고 DTYPE이라는 컬럼으로 자식 엔티티를 구분했다.

users

  • 닉네임(nickname), studyTime(총 누적 공부시간), user_status(회원 상태, 관리자에 의해 차단되면 BAN처리), profile_img(프로필 사진) 정보를 가진다.
  • 참여한 스터디방(room_id)을 외래 키로 가진다.
  • roles는 JWT 인증을 위해 선언된 필드다.

user_wish_hash_tag

  • 해당 태그를 선택한 회원(user_id)을 외래 키로 가진다.
  • 태그에 대한 데이터(tag_option) 정보를 가진다.

room

  • 방을 생성한 회원의 이메일(host_email), 방제목(title), 참여 가능한 인원수 제한(expected_users), 현재 인원수(current_user_num), 스터디 타이머(study_timer), 모집상태(room_status), 방 썸네일(thumbnail) 정보를 가진다.
  • studyChatBoxId 필드를 만들어 FK는 아니지만, 값을 저장한다.

room_hash_tag

  • 태그가 있는 해당 방(room_id)을 외래 키로 가진다.
  • 태그에 대한 데이터(tag_option) 정보를 가진다.

chat_box

  • 채팅방 이름(name) 정보로 채팅창을 구분한다.

join_chat

  • 채팅창(chat_box_id)과 채팅을 작성한 회원(user_id)을 외래 키로 가진다.
  • chat_box와 chat의 연결 엔티티 역할을 한다.

chat

  • 채팅창(chat_box_id)와 채팅을 작성한 회원(user_id)을 외래 키로 가진다.
  • 채팅 메시지(message)와 작성한 날짜(send_date) 정보를 가진다.

도메인 모델 분석

요구사항을 분석해보니 회원, 관리자, 스터디방, 방태그, 유저가 선택한 태그, 그리고 채팅창에 대한 엔티티가 필요했다. 그림과 함께 분석해보자.



회원(User)과 태그(UserWishHashTag)의 관계

회원은 자신이 좋아하는 태그를 n개 이상 선택 가능하므로 회원과 태그는 일대다 관계다.
외래 키가 있는 UserWishHashTag.user가 연관관계의 주인이다. 따라서 User.userHashTags에는 @OneToMany 속성에 mappedBy를 선언해서 연관관계의 주인인 user를 지정했다.


스터디방(Room)과 회원(User)의 관계

스터디방에 여러 명의 회원이 참여할 수 있으므로 일대다 관계다.
외래 키가 있는 User.room가 연관관계 주인이다. 따라서 Room.participants에는 @OneToMany 속성에 mappedBy를 선언해서 연관관계의 주인인 room을 지정했다.


스터디방(Room)과 태그(RoomHashTag) 관계

스터디방에 여러 태그를 붙일 수 있으므로 일대다 관계다.
외래 키가 있는 RoomHashTag.room가 연관관계 주인이다. 따라서 Room.roomHashTags에는 @OneToMany 속성에 mappedBy를 선언해서 연관관계의 주인인 room을 지정했다.


채팅창(ChatBox)과 회원(User)의 관계

채팅창에 여러 명의 회원이 참여할 수 있고, 특정 회원이 여러 채팅창에 참여할 수 있으므로 다대다 관계일 수 있다.
하지만 다대다 관계를 RDB에서 사용할 수 없다. 그래서 JoinChat이라는 연결 엔티티를 만들었다.


채팅창(ChatBox)과 채팅 메시지(Chat)의 관계

채팅창에 n개 이상의 메시지를 담을 수 있으므로 다대일 단방향 관계다.
다대일은 N이 연관관계 주인이므로, 외래 키가 있는 Chat.chatBox가 연관관계 주인이다.


회원(User)과 채팅 메시지(Chat)의 관계

회원당 채팅 메시지가 정의되므로 회원과 채팅 메시지는 다대일 단방향 관계다.
다대일은 N이 연관관계 주인이므로, 외래 키가 있는 Chat.user가 연관관계 주인이다.

회고

 

팀 프로젝트를 진행하면서 객체지향적인 설계를 위해 연관관계 매핑에 대한 고민을 많이 했었다.
(현재까지는 위의 ERD, UML 그림이 최종버전이다.)

번외로, 연관관계 매핑을 설정할 때 항상 같은 고민을 한다는 것을 깨달았다.
나는 양방향 매핑과 단방향 매핑 사이에서 많은 고민을 한다.

특히 1:N 연관관계가 있는 경우, fetch join이 필요한 복잡한 조회 쿼리를 작성해야 할 때 고민이 깊어진다.
이번에는 List<> 컬렉션을 2개 이상 가진 1:N 관계에 대한 조회 쿼리를 작성해야 했는데, 여전히 복잡한 조회 쿼리 작성은 어렵다는 것을 느꼈다.

다시 한 번 삽질을 해보고, 복잡한 도메인에서 join 쿼리를 어떻게 해야 할지 정리하고 포스팅을 올리고자 한다.

댓글