Featured image of post [MovieLens] 2. DB 설계

[MovieLens] 2. DB 설계

연구용 데이터셋으로 프로젝트 만들기

앞서 진행한 데이터셋 구조 확인을 바탕으로, 간단하게 데이터베이스 설계를 해봤습니다.

TAGS, RATINGS 테이블은 user_id, movie_id 컬럼을 활용한 관계 테이블인데 USERS 테이블에 관련된 데이터들을 별도로 제공하지 않았기 때문에 있다고 가정하고 진행했습니다.

erDiagram
    MOVIES {
        int movie_id PK
        string title
        string genres
        int rating_count
        bigint rating_total
    }
    
    RATINGS {
        int user_id PK,FK
        int movie_id PK,FK
        int rating
        timestamp created_at
    }
    
    REVIEWS {
        int review_id PK
        int user_id FK
        int movie_id FK
        string content
        timestamp created_at
        timestamp updated_at
    }
    
    TAGS {
        string name PK
        timestamp created_at
    }

    REVIEWS_TAGS {
        int user_id PK,FK
        int movie_id PK,FK
        int tag_name PK,FK
    }

    USERS {
        int id PK
    }

    MOVIES ||--o{ RATINGS: ""
    RATINGS }o--|| USERS: ""
    
    MOVIES ||--o{ REVIEWS: ""
    REVIEWS }o--|| USERS: ""
    
    REVIEWS ||--o{ REVIEWS_TAGS: ""
    REVIEWS_TAGS }o--|| TAGS: ""

기존 데이터를 기반으로 일부 컬럼들과 테이블을 추가하였습니다.

자세한 내용은 테이블 별로 설명해보겠습니다.

MOVIES

erDiagram
    MOVIES {
        int movie_id PK
        string title
        string genres
        int rating_count
        bigint rating_total
    }

기존 제공된 movie_id, title, genres는 그대로 활용하고, 평균 평점 계산에 활용될 rating_count, rating_total 컬럼을 추가했습니다.

genres 컬럼은 장르들이 | 문자로 구분되어 문자열 형태로 저장되는 형식이었는데, 처음 RDBMS를 이용해 만드는 방식에서는 검색 조건등의 활용을 배제하고, 이후 검색 엔진 같은 다른 방식으로 제공해 볼 예정입니다.

rating_count, rating_total 컬럼은 조회 후 서버나 클라이언트 측에서 직접 계산하는 방식을 사용할 수 있고, MySQL, PostgreSQL등과 같이 함수 기반 인덱스 기능을 제공하는 RDBMS를 활용한다면 조건을 활용한 조회 성능에도 이슈가 발생하지 않을 것이라 생각되어 추가하였습니다.

rating_count, rating_total 컬럼은 사용자가 평점을 추가하거나 수정할 때 변경되는 값이므로 평점 등록, 수정, 삭제 처리시 해당 컬럼 처리 로직 구현에 주의가 필요하겠습니다.

RATINGS

erDiagram
    RATINGS {
        int user_id PK,FK
        int movie_id PK,FK
        int rating
        timestamp create_ts
    }

기존 제공된 user_id, movie_id, create_ts는 그대로 활용하고, rating 컬럼의 데이터 타입을 변경하였습니다.

rating 같은 경우 기존에는 0.5 간격 0~5 까지의 소수를 저장하고 있었지만, MOVIES 테이블의 rating_total 처리와 평균 평점 계산 처리가 필요하므로, 개인 최고점을 10점으로 변경하고 서비스 로직에서 평점을 계산하는 방식을 고려하는게 더 합리적이라 판단하여 정수형 데이터타입을 선택하였습니다.

이외에도 rating_id와 같은 별도 PK를 만드는 것을 고려해봤지만, rating_id 컬럼이 다른 테이블에서 활용되거나 PK가 변경거나 user_id, movie_id 없이 단독으로 할용되는 경우는 없을 것이라 판단되어 별도로 추가하지는 않았습니다.

TAGS, REVIEWS, REVIEWS_TAGS

기본 제공된 데이터에서 TAGS는 전체 2,328,315건 중 유니크한 tag는 153,950개 였습니다.

erDiagram
    TAGS {
        int user_id PK,FK
        int movie_id PK,FK
        int tag PK,FK
        timestamp create_ts
    }

위와 같은 구조를 확인할 수 있었는데, 일반적으로 이러한 형태의 태그는 글과 함께 사용되는 해시태그 같은 기능이어서 어떻게 구조를 가져가야할지 고민이 들었습니다.

제 추측으로는 이 데이터의 목적은 추천 모델 개발이므로 사용자와 영화의 관계, 사용자가 자주 활용한 태그, 특정 영화에 많이 활용된 태그 등 user_id, movie_id를 기준으로 활용하는 것이 더 적합했기 때문이라고 생각되었습니다.

이 부분들을 고려하여 구성한 테이블 구조는 아래와 같습니다.

erDiagram
    TAGS {
        string name PK
        timestamp create_ts
    }
    
    REVIEWS_TAGS {
        int user_id PK,FK
        int movie_id PK,FK
        int tag_name PK,FK
    }

    REVIEWS {
        int review_id PK
        int user_id FK
        int movie_id FK
        string content
        timestamp create_ts
        timestamp update_ts
    }


    TAGS ||--o{ REVIEWS_TAGS: ""
    REVIEWS_TAGS }o--|| REVIEWS: ""

기존 TAGSREVIEWS_TAGS로 바꿔 연결 테이블로 구성했습니다.

TAGS 테이블은 tag만을 가지게 구성하고, 사용자가 영화에 후기를 남기는 기능을 고려하여 REVIEWS 테이블을 추가하였습니다.

TAGS 테이블은 앞서 언급했던 데이터 중복 뿐만 아니라 특정 태그를 활용한 게시글 검색등 활용될 여지가 많기 때문에 추가하였습니다.

사용 횟수 같은 컬럼들을 추가하는 것도 고려하였으나, 현 상황에서는 (tag_name) 인덱스 추가 정도로 충분히 빠른 조회를 수행할 수 있기 때문에 게시글 작성, 수정, 삭제 등의 로직 처리에 변수들을 배제하는것이 좋겠다고 판단하였습니다.

REVIEWS 테이블은 비즈니스 요구사항(베스트 후기 등)에 따라 단독으로 활용될 여지가 많다고 생각되어 RATINGS 테이블과 다르게 review_id를 추가하였습니다.

REVIEWS_TAGS 테이블은 기존 외래키인 user_id, movie_idreview_id로 변경하지 않고 사용했는데, 위에서 언급한대로 사용자와 영화의 관계를 쉽게 이용하려면 기존 방식이 더 적합하고, REVIEWS와 관련된 처리가 필요하다면 인덱스를 활용하여 충분히 쉽게 처리할 수 있다고 판단되었습니다.

끝으로

데이터셋 구조 확인에 이어 간단히 데이터베이스 구조를 구상해봤습니다.

만들어 놓고 보니 뭔가 찜찜한 기분이 계속 남네요..

제 역량이 부족해서 어떤 문제들이 발생하게 될지 예상할 수는 없지만 앞으로 발견되는 부족한 부분들은 프로젝트를 진행하면서 조금씩 수정해나가겠습니다.

끝까지 읽어주셔서 감사합니다 :)