0. 댓글 엔티티 생성 및 존재 하는 게시물인지 확인하는 미들웨어
@Entity()
export class CommentsModel extends BaseModel {
@ManyToOne(() => UsersModel, (user) => user.postComments)
author: UsersModel;
@ManyToOne(() => PostModel, (post) => post.comments)
post: PostModel;
@Column()
@IsString()
comment: string;
@Column({
default: 0,
})
@IsNumber()
likeCount: number;
}
댓글 엔티티 생성
post-exists.middleware.ts
@Injectable()
export class PostExistsMiddelware implements NestMiddleware {
constructor(private readonly postService: PostsService) {}
async use(req: Request, res: Response, next: NextFunction) {
const postId = req.params.postId;
console.log(postId);
if (":postId" === postId) {
throw new BadRequestException("Post ID 파라미터는 필수입니다.");
}
const exists = await this.postService.checkPostExistsById(parseInt(postId));
if (!exists) {
throw new BadRequestException("Post가 존재하지 않습니다.");
}
next();
}
}
comment.moduel.ts
export class CommentsModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(PostExistsMiddelware).forRoutes(CommentsController);
}
comment 컨트롤러 전체에 미들웨어 적용
1. 댓글생성
create-comments.dto.ts
export class CreateCommentsDto extends PickType(CommentsModel, ["comment"]) {}
comment.controller.ts
@Post()
@UseGuards(AccessTokenGuard)
@UseInterceptors(TransactionInterceptor)
async postComment(
@Param("postId", ParseIntPipe) postId: number,
@Body() body: CreateCommentsDto,
@CurrentUser() user: UsersModel,
@QueryRunnerDecorator() qr: QueryRunner
) {
const resp = await this.commentsService.createComment(
body,
postId,
user,
qr
);
await this.postsService.incrementCommentCount(postId, qr);
return resp;
}
comment.service.ts
async createComment(
dto: CreateCommentsDto,
postId: number,
author: UsersModel,
qr?: QueryRunner
) {
const repository = this.getRepository(qr);
return repository.save({
...dto,
post: {
id: postId,
},
author,
});
}
댓글 생성
post.service.ts
async incrementCommentCount(postId: number, qr?: QueryRunner) {
const repository = this.getRepository(qr);
await repository.increment(
{
id: postId,
},
"commentCount",
1
);
}
댓글 생성시 post테이블의 댓글 갯수를 하나 올려준다.
댓글이 잘 생성되며 DB에도 잘 저장이 된다.
2. 댓글 조회
paginate-comments.dto.ts
export class PaginateCommentsDto extends BasePaginationDto {}
comment.contoroller.ts
@Get()
getComments(
@Param("postId", ParseIntPipe) postId: number,
@Query() query: PaginateCommentsDto
) {
return this.commentsService.paginteComments(query, postId);
}
comment.service.ts
paginteComments(dto: PaginateCommentsDto, postId: number) {
return this.commonService.paginate(
dto,
this.commentsRepository,
{
relations: {
author: true,
},
select: {
author: {
id: true,
nickName: true,
},
},
where: {
post: {
id: postId,
},
},
},
`posts/${postId}/comments`
);
}
추가 조회 옵션으로 사용자의 정보를 가져오되 id와 nickName만 가져오게 했다.
페이지 네이션이 적용되어 오름 차순으로 44번 게시글에 있는 댓글들을 잘 가져온다 !
comment.contoroller.ts
@Get(":commentId")
getComment(@Param("commentId", ParseIntPipe) commentId: number) {
return this.commentsService.getCommentById(commentId);
}
comment.service.ts
async getCommentById(id: number) {
const comment = await this.commentsRepository.findOne({
relations: {
author: true,
},
select: {
author: {
id: true,
nickName: true,
},
},
where: {
id,
},
});
if (!comment) {
throw new BadRequestException(`id: ${id} Comment는 존재하지 않습니다.`);
}
return comment;
}
개별 댓글 또한 잘 조회 된다 !
3. 댓글 수정
update-comments.dto.ts
export class UpdateCommentsDto extends PartialType(CreateCommentsDto) {}
is-comment-mine-or-admin.guard.ts
@Injectable()
export class IsCommentMineOrAdminGuard implements CanActivate {
constructor(
private readonly commentService: CommentsService,
private readonly authService: AuthService,
private readonly usersService: UserService
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
const rawToken = req.headers["authorization"];
if (!rawToken) {
throw new UnauthorizedException("토큰이 없습니다!");
}
const token = this.authService.extractTokenFromHeader(rawToken, true);
const decoded = await this.authService.verifyToken(token);
const user = await this.usersService.getUserByEmail(decoded.email);
if (!user) {
throw new UnauthorizedException("사용자 정보를 가져올 수 없습니다.");
}
const commentId = req.params.commentId;
const isOk = await this.commentService.isCommentMine(
user.id,
parseInt(commentId)
);
if (!isOk) {
throw new ForbiddenException("권한이 없습니다.");
}
return true;
}
}
댓글 수정 삭제시 사용할 자신이 생성한 데이터 인지 확인하는 가드
comment.controller.ts
@Patch(":commentId")
@UseGuards(IsCommentMineOrAdminGuard)
async patchComment(
@Param("commentId", ParseIntPipe) commentId: number,
@Body() body: UpdateCommentsDto
) {
return this.commentsService.updateComment(body, commentId);
}
comment.service.ts
async updateComment(dto: UpdateCommentsDto, commentId: number) {
const comment = await this.commentsRepository.findOne({
where: {
id: commentId,
},
});
if (!comment) {
throw new BadRequestException("존재하지 않는 댓글입니다.");
}
const prevComment = await this.commentsRepository.preload({
id: commentId,
...dto,
});
const newComment = await this.commentsRepository.save(prevComment);
return newComment;
}
위 계정이 생성한 댓글을 다른 사용자가 수정하려 해보자.
위 작성자와 다른 계정으로 수정시도.
그럼 권한이 없다고 잘 에러가 나오며 아래와 같이 DB의 데이터가 수정되지 않는다 !
다시 작성자가 댓글 수정 시도
작성한 사용자가 수정 시 수정이 잘된다 ~
3. 댓글 삭제
comment.controller.ts
@Delete(":commentId")
@UseGuards(AccessTokenGuard)
@UseInterceptors(TransactionInterceptor)
async deleteComment(
@Param("commentId", ParseIntPipe) commentId: number,
@Param("postId", ParseIntPipe) postId: number,
@QueryRunnerDecorator() qr: QueryRunner
) {
const resp = await this.commentsService.deleteComment(commentId, qr);
await this.postsService.decrementCommentCount(postId, qr);
return resp;
}
comment.service.ts
async deleteComment(id: number, qr?: QueryRunner) {
const repository = this.getRepository(qr);
const comment = await repository.findOne({
where: {
id,
},
});
if (!comment) {
throw new BadRequestException("존재하지 않는 댓글입니다.");
}
await repository.delete(id);
return id;
}
post.service.ts
async decrementCommentCount(postId: number, qr?: QueryRunner) {
const repository = this.getRepository(qr);
await repository.decrement(
{
id: postId,
},
"commentCount",
1
);
}
moca 계정이 댓글 생성
위 작성자와 다른 계정으로 삭제시도.
삭제가 되지 않았다 !
다시 작성자가 댓글 삭제 시도
댓글이 잘 삭제 된다 !
'캡스톤 설계 [건물별 소통 플랫폼 BBC]' 카테고리의 다른 글
채팅 기능 구현하기 (0) | 2024.03.26 |
---|---|
인터셉터과 트랜잭션을 활용한 게시물 작성 [파일 업로드] (0) | 2024.03.25 |
페이지네이션 일반화 하기 (2) | 2024.03.24 |
게시물 페이지 네이션 (0) | 2024.03.24 |
유효성 체크 [DTO && Class Validator/Transformer] + 게시물 수정+비밀번호 안보이게하기 (0) | 2024.03.08 |