2025년 1월 31일

김동환·2025년 1월 31일

📌 오늘의 학습 (TIL) - CommentsService 구현

🔹 CommentsService 개요

CommentsServiceNestJSTypeORM을 활용하여 댓글을 생성, 조회, 수정, 삭제하는 기능을 제공하는 서비스입니다.


🔹 주요 기능

1️⃣ 댓글 생성 (createComment)

async createComment(cardId: number, userId: number, content: string)
  • cardId, userId, content를 입력받아 새로운 댓글을 생성
  • this.commentRepository.create()를 사용해 새로운 댓글 객체를 만들고 save()를 통해 DB에 저장

2️⃣ 특정 카드의 댓글 조회 (getCommentByCardId)

async getCommentByCardId(cardId: number)
  • findBy()를 사용하여 특정 카드에 속한 모든 댓글을 조회

3️⃣ 특정 댓글 조회 (getCommentById)

async getCommentById(id: number)
  • findOneBy()를 이용해 특정 ID의 댓글을 조회
  • 댓글이 존재하지 않을 경우 NotFoundException 예외 발생

4️⃣ 댓글 수정 (updateComment)

async updateComment(id: number, userId: number, content: string)
  • verifyComment()를 호출하여 댓글이 존재하는지, 사용자가 작성자인지 검증
  • 검증 후 update()를 이용해 댓글 내용 수정
  • 변경된 댓글을 findOneBy()로 다시 조회하여 반환

5️⃣ 댓글 삭제 (deleteComment)

async deleteComment(id: number, userId: number)
  • verifyComment()를 호출하여 삭제 권한을 확인
  • delete()를 사용하여 DB에서 댓글 삭제 후 메시지 반환

🔹 댓글 검증 로직 (verifyComment)

private async verifyComment(id: number, userId: number)
  • 특정 댓글이 존재하는지 확인
  • 댓글 작성자와 userId가 일치하지 않으면 NotFoundException 예외 발생

🔹 배운 점

1️⃣ NestJS의 InjectableInjectRepository

  • Injectable()을 사용해 서비스 클래스를 주입 가능하게 만듦
  • InjectRepository(Comment)를 활용해 commentRepository를 TypeORM과 연결

2️⃣ TypeORM의 CRUD 메서드 활용

  • create(), save(), findBy(), findOneBy(), update(), delete() 등의 ORM 메서드를 활용하여 DB 조작

3️⃣ Lodash를 활용한 데이터 검증 (_.isNil())

  • _.isNil(comment)를 사용해 null 또는 undefined 여부를 확인하여 예외 처리

🔹 개선할 점

  • getCommentByCardId()에서 댓글이 없을 경우 예외를 던지는 로직 추가 가능
  • updateComment()deleteComment()에서 verifyComment()를 호출하므로, 중복되는 조회 쿼리를 줄일 방법 고려 가능

NestJS + TypeORM을 활용한 댓글 서비스 구현을 통해 CRUD 로직을 정리할 수 있었다!


comments.service.ts

import { Repository } from 'typeorm';
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import _ from 'lodash';

import { Comment } from './entities/comment.entity';

@Injectable()
export class CommentsService {
 constructor(
   @InjectRepository(Comment)
   private commentRepository: Repository<Comment>,
 ) {}

 async createComment(cardId: number, userId: number, content: string) {
   const newComment = this.commentRepository.create({
     cardId,
     userId,
     content,
   });
   return await this.commentRepository.save(newComment);
 }

 async getCommentByCardId(cardId: number) {
   return await this.commentRepository.findBy({
     cardId: cardId,
   });
 }

 async getCommentById(id: number) {
   const comment = await this.commentRepository.findOneBy({ id });
   if (_.isNil(comment)) {
     throw new NotFoundException('댓글을 찾을 수 없습니다.');
   }
   return comment;
 }

 async updateComment(id: number, userId: number, content: string) {
   await this.verifyComment(id, userId);
   await this.commentRepository.update({ id }, { content });
   return await this.commentRepository.findOneBy({ id });
 }

 async deleteComment(id: number, userId: number) {
   await this.verifyComment(id, userId);
   await this.commentRepository.delete({ id });
   return { id, message: '삭제되었습니다.' };
 }

 private async verifyComment(id: number, userId: number) {
   const comment = await this.commentRepository.findOneBy({
     id,
   });

   if (_.isNil(comment) || comment.userId !== userId) {
     throw new NotFoundException(
       '댓글을 찾을 수 없거나 수정/삭제할 권한이 없습니다.',
     );
   }
 }
}
profile
Node.js 7기

0개의 댓글