[wanted-pre-onboarding] Assignment1

·2021년 11월 3일
0

Github Repo

https://github.com/wanted-wecode-subjects/aimmo-subject-teamB

개요

  • 회원, 게시글, 댓글 API 만들기

기간

  • 2021.11.01 - 2021.11.03

개발 환경

  • TypeScript
  • NestJS
  • MongoDB

사용 라이브러리

  • bcrypt
  • passport
  • mongoose
  • class-validator

구현 사항

  • 회원 로그인 구현
  • 게시글 CRUD
  • 게시글 pagination
  • 게시글 조회수 카운트
  • 게시글 검색
  • 게시글 카테고리
  • 댓글 CRUD
  • 대댓글(depth: 1)
  • 대댓글 pagination

MongoDB

MongoDB는 NoSQL 중 가장 많이 쓰이는 데이터베이스입니다.

NoSQL

NoSQL은 Not Only SQL의 약자로 RDBMS와는 달리 고정된 스키마나 복잡한 JOIN이 존재하지 않습니다.
또한 key-value 쌍으로 데이터를 표현합니다.

{
    "_id": ObjectId("5099803df3f4948bd2f98391"),
    "username": "velopert",
    "name": { first: "M.J.", last: "Kim" }
}

NoSQL & RDBMS

NoSQL을 RDBMS와 비교하여 볼 때 다음과 같이 정리할 수 있습니다.

RDBMSNoSQL
DatabaseDatabase
TableCollection
ColumnField, Key
RowDocument

_id

_id는 12bytes의 hexadecimal 값으로 각 document의 유일성을 제공합니다.

동적 schema

Document는 동적 schema를 갖고 있습니다.
즉, 같은 Collection의 다른 Document 사이에 다른 key를 가질 수 있습니다.

Data Modelling

schema 디자인 시 고려 사항

  • 사용자 요구에 따라 schema를 디자인한다.
  • 객체를 함께 사용한다면(예: 게시글, 댓글) 하나의 document에 합쳐서 사용한다. 그렇지 않으면 따로 사용한다.
  • 읽을 때 join 하는 것이 아니라 데이터를 작성할 때 join 한다.

예제

다음과 같은 요구사항이 있다고 가정합니다.

  • 게시글에는 title, content, author가 존재한다.
  • 각 게시글은 0개 이상의 태그를 가지고 있을 수 있다.
  • 게시글엔 댓글을 달 수 있다. 댓글은 author, content, 작성시간을 가지고 있다.

이를 RDBMS로 디자인하면 다음과 같이 3개의 테이블을 생성해야 합니다.

반면 NoSQL로 디자인하면 다음과 같이 Document를 작성할 수 있습니다.
즉, 하나의 Document에 세 데이터를 넣을 수 있습니다.

{
  "_id":"POST_ID",
  "title":"POST_TITLE",
  "content":"POST_CONTENT",
  "username":"POST_WRITER",
  "tags":[
    TAG1,
    TAG2,
    TAG3
  ],
  "time":"POST_TIME",
  "comments":[
    {
      "username":"COMMENT_WRITER",
      "mesage":"COMMENT_MESSAGE",
      "time":"COMMENT_TIME"
    },
    {
      "username":"COMMENT_WRITER",
      "mesage":"COMMENT_MESSAGE",
      "time":"COMMENT_TIME"
    }
  ]
}

출처: https://velopert.com/436

대댓글

MongoDB self ref을 사용하여 대댓글을 구현하였습니다.
댓글의 schema는 다음과 같습니다.

Comment {
  content: string;
  author: string;
  depth: number;
  parentCommentId: string;
  comments: Comment[];
  created_at: Date;
  updated_at: Date;
  postId: string;
}

댓글의 경우 depth가 0이 되고, parentCommentId에는 빈 값이 들어갑니다.
그리고 대댓글은 depth가 1이 되며, parentCommentId에는 부모 댓글의 _id가 들어갑니다.

이 과정에서 comments의 comment가 자기 참조를 할 수 있도록 @Prop 데코레이터를 @Prop({type: [{type: mongoose.Schema.Types.ObjectId, ref: 'Comment'}]}) 와 같이 적용하였습니다.

예시

댓글이 존재하는 상황에서 대댓글이 등록되면 다음과 같이 조회가 됩니다.

{
    "_id": "618176d0bf01ef0f96c163bb",
    "postId": "postId",
    "updated_at": "2021-11-02T17:35:12.347Z",
    "created_at": "2021-11-02T17:35:12.347Z",
    "comments": [
        "618176fabf01ef0f96c163c2",
    ],
    "depth": 0,
    "author": "testuser",
    "content": "댓글",
    "__v": 3
}

회고

NoSQL을 처음 써보게 되어 초기에 진행이 더뎠습니다.
또한, 초기에 ORM으로 사용하려던 TypeORM이 MongoDB와는 호환이 잘 안되는 이슈가 있어 진행에 어려움을 겪었습니다.
예를 들어, 데이터 조회시 typeorm mongodb cannot read property 'prototype' of undefined 같은 에러가 발생하는 경우가 있습니다. 이는 버전에 대한 문제로 인해 발생하는 이슈이며, 이 때문에 mongoose를 대체로 사용하였습니다.
(관련 글: TypeORM + MongoDB 적용 및 사용하지 않게 된 이유)

NoSQL로 스키마를 작성하고, 기능을 구현하였으나, NoSQL스럽게 스키마를 작성하고 코드를 구현하였는지도 약간 의문스럽습니다.
예를 들어, comment의 postId 같은 필드가 foreign key 처럼 사용된 것 같아 꺼림칙합니다.

한가지 다행이라고 생각되는 점은 이후에 NoSQL을 사용할 때 오늘만큼을 헤매지 않을 것 같다는 점입니다.
추후 제가 만들고자 하는 애플리케이션의 DB로 NoSQL을 사용한다거나 관련된 내용을 마주했을 때 좀 더 가벼운 마음으로 개발을 진행할 수 있을 것 같습니다.

0개의 댓글