오늘은 NestJS를 활용하여 댓글 서비스(CommentsService
)를 구현했다. 댓글을 생성, 조회, 수정, 삭제하는 기능을 담당하며, CommentsRepository
를 통해 데이터베이스와 연동된다.
createComment
)postId
, userId
, content
를 받아 새로운 댓글을 생성한다. EmptyCommentException
발생 CommentLengthExceededException
발생 getCommentsByPostId
)postId
를 기준으로 해당 게시글의 모든 댓글을 조회한다. getCommentById
)id
를 기준으로 특정 댓글을 조회한다. CommentNotFoundException
발생 updateComment
)id
, userId
, content
를 받아 댓글을 수정한다. EmptyCommentException
발생 CommentLengthExceededException
발생 CommentNotFoundException
발생 CommentPermissionException
발생 deleteComment
)id
, userId
를 받아 댓글을 삭제한다. CommentNotFoundException
발생 CommentPermissionException
발생 postId
만 받아서 댓글을 생성하고 있는데, 나중에 Post
엔티티를 직접 참조하도록 개선해야 한다. PostRepository
를 활용하여 댓글을 작성할 게시글이 존재하는지 검증하는 로직이 필요하다. lodash
의 isEmpty()
를 활용하면 문자열 공백 체크가 간편하다. 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: '삭제되었습니다.' };
}
}