2025년 2월 17일

김동환·2025년 2월 17일
0

TIL (Today I Learned) - NestJS 댓글 서비스 구현

1. 댓글 서비스(CommentsService) 구현

오늘은 NestJS를 활용하여 댓글 서비스(CommentsService)를 구현했다. 댓글을 생성, 조회, 수정, 삭제하는 기능을 담당하며, CommentsRepository를 통해 데이터베이스와 연동된다.

2. 핵심 기능 및 예외 처리

댓글 생성 (createComment)

  • postId, userId, content를 받아 새로운 댓글을 생성한다.
  • 예외 처리:
    • 댓글 내용이 비어 있으면 EmptyCommentException 발생
    • 댓글 길이가 50자를 초과하면 CommentLengthExceededException 발생

특정 게시글의 댓글 목록 조회 (getCommentsByPostId)

  • postId를 기준으로 해당 게시글의 모든 댓글을 조회한다.

단일 댓글 조회 (getCommentById)

  • id를 기준으로 특정 댓글을 조회한다.
  • 댓글이 존재하지 않으면 CommentNotFoundException 발생

댓글 수정 (updateComment)

  • id, userId, content를 받아 댓글을 수정한다.
  • 예외 처리:
    • 댓글 내용이 비어 있으면 EmptyCommentException 발생
    • 댓글 길이가 50자를 초과하면 CommentLengthExceededException 발생
    • 댓글이 존재하지 않으면 CommentNotFoundException 발생
    • 작성자가 아닌 경우 CommentPermissionException 발생

댓글 삭제 (deleteComment)

  • id, userId를 받아 댓글을 삭제한다.
  • 예외 처리:
    • 댓글이 존재하지 않으면 CommentNotFoundException 발생
    • 작성자가 아닌 경우 CommentPermissionException 발생

3. 리팩토링할 부분

  • 현재 postId만 받아서 댓글을 생성하고 있는데, 나중에 Post 엔티티를 직접 참조하도록 개선해야 한다.
  • PostRepository를 활용하여 댓글을 작성할 게시글이 존재하는지 검증하는 로직이 필요하다.

4. 배운 점

  • lodashisEmpty()를 활용하면 문자열 공백 체크가 간편하다.
  • 예외 처리를 철저하게 구현하면 API의 안정성이 올라간다.
  • Repository 패턴을 활용하면 데이터베이스 접근 로직을 서비스 레이어에서 분리할 수 있어 유지보수가 용이하다.

comments.service.ts

import { Injectable } from '@nestjs/common';
import _ from 'lodash';
import { CommentsRepository } from './comments.repository';

// import { Post } from 'src/posts/entities/post.entity';
// import { PostRepository } from '../../posts/repository/post.repository';

import {
  CommentNotFoundException,
  CommentPermissionException,
  EmptyCommentException,
  CommentLengthExceededException,
  // PostNotFoundException,
} from 'src/common/exceptions/comments.exception';

@Injectable()
export class CommentsService {
  constructor(
    private readonly commentsRepository: CommentsRepository,
    // private readonly postRepository: PostRepository, // Post 존재 여부 확인용
  ) {}

  async createComment(postId: number, userId: number, content: string) {
    if (_.isEmpty(content.trim())) throw new EmptyCommentException();
    if (content.length > 50) throw new CommentLengthExceededException();

    // const post = await this.postRepository.findById(postId);
    // if (!post) throw new PostNotFoundException();

    const comment = await this.commentsRepository.createComment(
      { id: postId } as any, // Post 객체 대신 ID만 넘김 post로 바꿔야됨
      userId,
      content,
    );

    return {
      id: comment.id,
      content: comment.content,
      createdAt: comment.createdAt,
      updatedAt: comment.updatedAt,
    };
  }

  async getCommentsByPostId(postId: number) {
    const comments = await this.commentsRepository.findByPostId(postId);

    return comments.map((comment) => ({
      id: comment.id,
      content: comment.content,
      createdAt: comment.createdAt,
      updatedAt: comment.updatedAt,
    }));
  }

  async getCommentById(id: number) {
    const comment = await this.commentsRepository.findById(id);
    if (!comment) throw new CommentNotFoundException();

    return {
      id: comment.id,
      content: comment.content,
      createdAt: comment.createdAt,
      updatedAt: comment.updatedAt,
    };
  }

  async updateComment(id: number, userId: number, content: string) {
    if (_.isEmpty(content.trim())) throw new EmptyCommentException();
    if (content.length > 50) throw new CommentLengthExceededException();

    const comment = await this.commentsRepository.findById(id);
    if (!comment) throw new CommentNotFoundException();
    if (comment.user.id !== userId) throw new CommentPermissionException();

    comment.content = content;
    const updatedComment = await this.commentsRepository.updateComment(comment);

    return {
      id: updatedComment.id,
      content: updatedComment.content,
      createdAt: updatedComment.createdAt,
      updatedAt: updatedComment.updatedAt,
    };
  }

  async deleteComment(id: number, userId: number) {
    const comment = await this.commentsRepository.findById(id);
    if (!comment) throw new CommentNotFoundException();
    if (comment.user.id !== userId) throw new CommentPermissionException();

    await this.commentsRepository.deleteComment(comment);
    return { id, message: '삭제되었습니다.' };
  }
}
profile
Node.js 7기

0개의 댓글

관련 채용 정보