본 게시물은 이전에 운영하던 velog에서 작성됨 (2023년 5월 3일 작성)
TalkGG 프로젝트 진행하면서 테이블 설계가 중간에 바뀌었었다.
게시물 테이블과 관련된 이슈였고 게시물과 관련된 테이블들이 비효율적으로 설계된 것 같아 테이블을 통합하는 작업이었다.
하면서 느낀 건데 데이터 중복이 많은 것도 문제인데 마냥 테이블을 잘게 분해시키는 것도
큰 문제가 될 수 있을 것 같다.
상황에 따라 분해도 하고 통합도 하고 유연하게 대처하는 것이 옳은 것 같다.
🐹 1차 설계
- 우선 게시물 테이블을 만들었는데 게시물은 자유, 유머, 대회소식으로 총 3개의 카테고리로 나누어진다.
- 게시물 카테고리 별로 게시물의 구조나 양식이나 저장하는 데이터 컬럼이 달라질 수 있지 않을까 하는 생각이 들어 카테고리마다 테이블을 만들었다.
- 자유, 유머, 대회소식 게시물 테이블을 만들었고, 각각의 테이블마다 댓글 테이블, 대댓글 테이블도 만들었고, 이에 더해서 게시물마다 좋아요 테이블도 만들었다. 이렇게 만드니 테이블이 15개가 되어 있었다.
지금 보니까 관계 설정은 참 잘한 것 같다.
🐸 1차 설계의 문제점?
일단 위 설계를 기반으로 프로젝트를 진행했었는데, 진행을 하면 할수록 뭔가 계속 꼬이는 느낌이 들기 시작했다.
- 게시물 카테고리마다 API를 만들어야 하나?
- 게시물 카테고리마다 컨트롤러를 분리해야 하나?
- 기능이 거의 똑같은 API이기 때문에 같은 비즈니스 로직이 반복될 가능성이 있는데, 이 로직들을 어떻게 재사용해야하지?
- TypeORM의 경우 쿼리 빌더를 사용할 때 엔티티를 명시해줘야해서 메소드를 재사용하기가 까다로운데 이런 경우 어떻게 재사용해야하지?
- 게시물 검색 API처럼 여러 테이블을 조인해야할 상황이 발생할 수 있는데, 이렇게 게시물 카테고리 별로 테이블을 쪼개버리면 조인 성능 저하는 어떻게 해결해야하지?
- 외래키가 연결된 경우 무결성 검사를 반복하게 되는데, 여기서 발생하는 오버헤드가 서비스에 큰 영향을 미칠까?
이런 저런 고민들로 머리를 싸매던 중 테이블을 통합하는 것이 더 효율적이라는 판단을 해서 2차 설계를 진행했다.
❄️ 2차 설계
- 2차 설계의 목적은 카테고리별로 분리된 테이블을 통합시키는 것이었다.
- 게시물 테이블을 하나의 테이블로 통합시켰다.
- 좋아요 테이블은 게시물 테이블의 컬럼으로 통합시켰다.
- 댓글과 대댓글 테이블은 댓글이라는 하나의 테이블로 통합하고 댓글 순서와 댓글 그룹 컬럼을 추가해서 댓글과 대댓글을 구분하도록 하였다.
- 댓글, 대댓글 통합에 대한 아이디어는 여기서 얻었다
OKKY - MYSQL 대댓글 기능
구글에 계층형 쿼리에 대해서 검색해보면ㄴ 0 ㄴ1 ㄴ 2 ㄴ 3이런식으로 무한 대댓글 기능을 설명하더라구요근데 저는ㄴ 0 ㄴ 1 ㄴ 1 ㄴ 1이렇게 한 댓
okky.kr
🐰 2차 설계의 문제점?
이번에는 댓글과 대댓글 데이터를 관리하면서 꼬이게 되었다.
우선 새로 추가된 컬럼들은 다음과 같은 역할을 하는데,
- sequence 컬럼은 댓글의 순서에 대한 정보인데, 댓글일 경우 컬럼 값은 0이 되고, 대댓글일 경우 순서대로 1 이상의 값이 담긴다.
- group의 경우 댓글과 대댓글이 어떤 댓글에 속해있는 지에 대한 정보이다. 댓글일 경우 댓글 자기 자신의 pk 값이 들어가고 대댓글일 경우 대댓글을 달려고 하는 댓글의 pk가 들어간다.
- id 컬럼과 group 컬럼으로 셀프 조인하여 댓글과 대댓글을 반환하는 방식이다.
group 컬럼의 경우 같은 테이블의 id 컬럼을 참조하는 외래키인데, 여기서 문제가 발생한 것이다. 댓글을 작성할 경우 group에는 해당 댓글 데이터의 id 값이 입력되야한다. 하지만 group이 참조하려는 id 값이 아직 테이블에 존재하지 않기 때문에 에러가 발생한다.
지금 생각해보면 참조 관계를 끊어주면 해결될 수 있는 문제인 것 같은데, 이때 당시에는 왜 이렇게 관계 설정에 집착했던 것일까..
아무튼 이때의 나는 다시 댓글 테이블을 댓글과 대댓글 테이블로 분리하는 판단을 하였다..
🐨 N차 설계
2차 설계 이후에는 크게 다음과 같은 변경이 있었다.
- 댓글 테이블을 댓글 테이블과 대댓글 테이블로 분리
- OAuth 인증 도입으로 이메일, 비밀번호 컬럼 삭제 및 OAuth 데이터 관련 컬럼 생성
- 게시판 조회수 컬럼이 있었는데, Redis에서 관리하도록 분리
- 게시판 좋아요 컬럼이 있었는데, Redis에서 관리하도록 분리
- 등등
결과적으로 현재 테이블 구조는
요렇게 되었다.
⚡ 테이블은 그때 그때 유연하게
프로젝트 하면서 알게 된건데, 외래키 무결성 검사로 인해 성능 저하가 발생하여 규모가 큰 RDB의 경우 외래키를 설정하지 않는 경우가 많다고 한다.
이처럼 테이블 설계에는 답이 없는 것 같다. 그때 그때 환경에 맞게 설계하면 되는 거시다!
게시물 테이블을 통합하면서 파티셔닝도 하고 싶었는데, 외래키가 존재하는 테이블은 파티셔닝을 할 수 없다고 한다. 그래서 나중에 외래키 관계를 다 끊어 버리고 파티셔닝을 해서 조회 성능이 얼마나 개선되는 지 확인해봐야겠다.