오늘은 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: '삭제되었습니다.' };
}
}