티스토리 뷰

이전 게시글에서는 MSA 구조로 구축된 서비스의 구조, 그 중 Comment 서비스의 알림 수신, 발신을 중심으로 알아보았다.

이번에는 Comment 서비스의 기능 개발 부분을 정리할 것이다.

 

MSA 구조가 어렵고, 이벤트 발행 등의 문제로 조금 복잡했지만 실제 Comment 기능 개발은 지난 학기 Monolithic 구조에서와 크게 다르지 않았다.

 

1. 게시글에 달린 댓글 개수

// post에 달린 댓글 개수
async getCommentsCount(comment: RequestCommentCountDto) {
  const count = this.commentRepository
    .createQueryBuilder('comment')
    .where(`comment.post_id = ${comment.post_id}`)
    .getCount();

  return count;
}

게시글에 달린 댓글 개수를 제공하는 함수이다. 게시글을 눌렀을 때 작동될 수 있으며, 댓글을 보기 위한 게시글의 post_id를 받아와서 해당 post_id에 달린 댓글의 개수를 계산하여 리턴한다.

 

2. 게시글에 달린 모든 댓글 불러오기

async loadAllComments(post_id: string) {
  // 게시글이 존재하는 건지 확인
  // const isExistedPost;

  // // 게시글이 없을 때
  // if (isExistedPost == null) {
  //   return '없는 게시글입니다.';
  // }

  const comments = await this.commentRepository
    .createQueryBuilder('comment')
    .select('comment.comment_id')
    .addSelect('comment.user_id')
    .addSelect('comment.content')
    .addSelect('comment.created_at')
    .leftJoin('comment.post_id', 'post_id')
    .where('comment.post_id = :post_id', { post_id })
    .getMany();

  return comments;
}

게시글을 눌렀을 때, Get 요청으로 확인할 게시글의 모든 댓글을 불러오는 함수이다.

처음에는 게시글이 존재하는지 확인한 뒤, 게시글이 존재할 때만 댓글을 불러오려고 하였지만, 게시글이 없을 때의 요청은 이 요청까지 오기 전에 막히기에 주석처리한 뒤 그냥 모든 댓글을 불러오는 코드만 작성하였다.

QueryBuilder를 사용하여 모든 댓글의 comment_id, user_id, 댓글 내용, 댓글 작성 시각을 불러온다.

 

3. 로그인한 유저가 클릭한 게시글에 작성한 댓글 확인

// 로그인한 유저가 클릭한 post에 쓴 댓글을 확인
async checkUser(user_id: string, post_id: string) {
  // 내가 쓴 댓글의 commentID 찾음
  const myComments = await this.commentRepository
    .createQueryBuilder('comment')
    .select('comment_id')
    .where('user_id = :user_id', { user_id })
    .andWhere('post_id = :post_id', { post_id })
    .getMany();

  return myComments;
}

쉽게 말하면 내가 쓴 댓글을 확인하는 함수이다. 일반적으로, 내가 작성한 댓글은 수정 혹은 삭제 표시가 뜨고 수정/삭제를 할 수 있으므로 게시글마다 내가 쓴 댓글이 있는지 확인해야 한다.

그래서 QueryBuilder로 유저의 user_id와 게시글의 post_id가 둘다 존재하는 열을 찾아 comment_id만 리턴한다.

 

4. 댓글 작성 (댓글 추가)

// 새 댓글 추가
async addComment(body: RequestCreateCommentDto, user_id: string) {
  //const isExistedPost;

  const comment = this.commentRepository.create({
    post_id: body.post_id,
    user_id: user_id,
    content: body.content,
  });
  await this.commentRepository.insert(comment);
  // 이벤트 발행
  await this.snsService.publishMessage(body.post_id, 'comment_created');
}

이제 유저가 댓글을 작성하면, POST 요청으로 새 댓글을 추가하는 함수가 동작한다. 그리고 다른 서비스에 새 댓글이 작성되었다는 알림을 보낸다.

이 함수에서도 게시글이 존재하는지 확인하려 하였으나, 굳이 필요없어 주석처리한 후 작성한 댓글이 어떤 게시글인지(post_id), 작성한 유저의 user_id, 작성한 내용을 DB에 저장한다. 댓글 작성 시간은 새 댓글을 작성하면 자동으로 nestjs에서 시간을 생성하여 준다.

그리고 comment_created 이벤트를 발행한다.

 

5. 댓글 삭제

// 댓글 삭제
async deleteComment(comment_id: string) {
  const { post_id } = await this.commentRepository.findOneBy({ comment_id });
  if (!post_id) {
    return '없는 게시글입니다';
  }

  await this.commentRepository.delete({
    comment_id,
  });
  // 이벤트 발행
  await this.snsService.publishMessage(post_id, 'comment_deleted');
}

댓글 삭제도 마찬가지로 댓글이 삭제되고 나면 이벤트를 발행한다.

삭제할 댓글의 comment_id로 해당 댓글이 어떤 게시글에 달렸는지 post_id를 받아온다. 그리고 만약 그 게시글이 없다면 잘못된 접근이므로 return 한다.

잘못된 접근이 아니면, 해당 comment_id가 포함된 컬럼을 삭제하고, 'comment_deleted' 이벤트를 발행한다.

 

(6. 댓글 수정)

// 댓글 수정
async updateComment(body: RequestUpdateCommentDto, user_id: string) {
  const comment = await this.commentRepository.findOne({
    where: {
      comment_id: body.comment_id,
      user_id: user_id,
    },
  });

  // 댓글이 없으면
  if (comment == null) {
    return '없는 댓글입니다.';
  }

  comment.content = body.content;

  return await this.commentRepository.save(comment);
}

기능을 개발할 때 댓글 수정 기능까지 개발하였지만, 실제 어플리케이션에서 댓글 수정 기능이 없다고 하여 실제로 사용되지는 않는 코드이다. 하지만 이미 개발하였으므로 개발 내용에는 남겨 놓을 것이다.

댓글 수정은 수정할 댓글의 comment_id와 수정하는 사용자의 user_id로부터 수정하는 댓글을 받아오고, 만약 검색된 댓글이 없다면 없는 댓글을 수정하는 것이므로 return 한다.

잘못된 접근이 아니라면, 검색된 댓글의 내용을 새로 작성한 내용으로 바꾼 뒤, save로 바뀐 값을 저장한다.