MySQL + TypeORM 로 고민하기까지의 과정
우리에게 필요한 SNS API를 구현하기에 앞서서, mongoDB에서 사용할 데이터의 스키마를 정의해야 해요.
아직 정확하게 작성하지는 못했지만 고민 포인트가 있어요
(MySQL + TypeORM 이 더 적절한 선택일수도..)
- User에 postCount, commentCount가 들어있었는데, 이걸 DB에 존재하는 User 모델에도 넣는게 맞나?
- User 라는 데이터 자체에 대한 값은 아니다보니 게시글 CRUD API를 다루다가 혹시 놓치고 User 모델에는 반영이 안된 상태로 존재한다면 데이터 무결성이 깨지는 현상이 발생할 것 같아요..
- Post, Comment에 존재하는
author
필드에 User 데이터 자체를 넣는게 맞나? - 만약 사용자가 게시글을 작성해놓고 자신의 프로필을 수정한다면 기존의 Post 모델에는 예전의 사용자 데이터가 들어가 있을 것 같아요.
RDBMS라면 당연히 원자적 데이터로만 유일하게 다뤄야겠지만,
mongodb같은 NoSQL에서는 데이터의 중복을 허용해서 넣는 경우도 있다는 것 같아서 더 찾아보고 고민해봐야 할 것 같아요.
⇒ 찾아봤는데, mongoDB는 변경될 가능성이 거의 없는 로그성 데이터에 용이해보여서 RDBMS를 사용하는 게 낫다고 판단을 했어요.
interface User { _id: string; email: string; image: string; // default: 기본 이미지 username: string; // 사용자 실명 introduce: string; // default: "안녕하세요 000입니다" slackId?: string; // default: undefined slackWorkspace?: SlackWorkspace; // default: undefined } interface Post { _id: string; author: User; title: string; // 머쓱이 제목, default: "머쓱이" content: string; // 소개글, default: "" musseukImageName: MusseukType; // 머쓱이 이미지 파일 이름, default: "musseuk_default" comments: Comment[]; } interface Comment { _id: string; author: User; content: string; // default: "" position: [number, number]; // 댓글 좌표(백분율), default: [0, 0] nickname: string; // default: "익명의 머쓱이" decorationImageName: DecorationType; // 장식 이미지 파일 이름, default: "decoration_soju1" }
ERD

모델
@Entity() export class Post { @PrimaryGeneratedColumn() id!: number; @Column({ length: 50 }) title!: string; @Column({ length: 500 }) content!: string; @Column({ length: 50, nullable: true }) imageName!: string; @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) createdAt!: Date; @ManyToOne(() => User, (user) => user.posts, { cascade: true, }) author!: User; @OneToMany(() => Comment, (comment) => comment.id) comments!: Comment[]; } @Entity() export class User { @PrimaryGeneratedColumn() id!: number; @Column({ length: 50, unique: true }) username!: string; @Column() password!: string; @Column() salt!: string; @Column({ length: 20 }) role!: string; @Column({ length: 20 }) name!: string; @Column() introduce!: string; @Column({ length: 50 }) imageName!: string; @Column({ length: 50 }) slackId!: string; @Column({ length: 20 }) slackWorkspace!: string; @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) createdAt!: Date; @OneToMany(() => Post, (post) => post.author) posts!: Post[]; } @Entity() export class Comment { @PrimaryGeneratedColumn() id!: number; @Column({ length: 50 }) author!: string; @Column({ length: 500 }) content!: string; @Column({ length: 50 }) imageName!: string; @Column() positionX!: number; @Column() positionY!: number; @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) createdAt!: Date; @ManyToOne(() => Post, (post) => post.comments, { cascade: true, }) post!: Post; }