기존에 RoleEnum을 통해 User 혹은 Admin인 경우만을 체크했다.
그런데 포스트나 댓글을 수정하는 것을 둘 중 하나로만 설정하는게 아닌 Admin이거나 내가 작성한 글(혹은 댓글)인 경우에 수정을 할 수 있도록 하려면 새로운 Guard를 구현해야 한다.
1. IsPostMineOrAdminGuard 가드 구현 및 적용
1.1. Posts Service 로직 추가하기
async isPostMine(userId: number, postId: number) {
return await this.postsRepository.exists({
where: {
id: postId,
author: {
id: userId,
}
},
relations: {
author: true,
}
});
}
Posts의 Service 에 isPostMine이라는 로직을 추가한다. userId와 postId를 매개변수로 받고 포스트 작성자가 본인인지 확인하는 로직이다.
1.2. IsPostMineOrAdminGuard Guard 구현하기
import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Injectable, UnauthorizedException } from "@nestjs/common";
import { PostsService } from "../posts.service";
import { RoleEnum } from "src/users/entity/users.entity";
@Injectable()
export class IsPostMineOrAdminGuard implements CanActivate{
constructor(
private readonly postService: PostsService,
){}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
const {user} = req;
if(!user){
throw new UnauthorizedException(
'사용자 정보를 가져올 수 없습니다.',
)
}
/**
* Admin일 경우 그냥 패스
*/
if(user.role === RoleEnum.ADMIN){
return true;
}
const postId = req.params.postId;
if(!postId){
throw new BadRequestException(
'Post ID가 파라미터로 제공 돼야합니다.',
);
}
const isOk = await this.postService.isPostMine(
user.id,
parseInt(postId),
);
if(!isOk){
throw new ForbiddenException(
'권한이 없습니다.',
);
}
return true;
}
}
IsPostMineOrAdminGuard Guard를 구현한다. 최종적으로 관리자이거나 본인이 작성한 포스트가 아니면 ForbiddenException 에러를 발생시킨다.
1.3. patchPost 에 적용하기
// 4) Patch /posts:id
// id에 해당되는 post를 변경한다.
@Patch(':postId')
@UseGuards(IsPostMineOrAdminGuard)
patchPost(
@Param('postId', ParseIntPipe) id: number,
@Body() body: UpdatePostDto,
// @Body('title') title?: string,
// @Body('content') content?: string,
) {
return this.postsService.updatePost(id, body);
}
반드시 postId로 변경해야한다. 그러면 내부적으로 req.params에 postId가 저장되게 되며, 이것을 IsPostMineOrAdminGuard Guard 에서 사용이 가능해진다.
1.4. 포스트맨 테스트
1번 아이디는 role이 User이며, 1번 아이디가 작성한 포스트로 확인할 예정이다.
문제가 없다면 1번 아이디는 본인이 쓴 게시글이라 포스트를 수정이 가능할 것이며,
2번 아이디는 role이 User이지만 본인이 아니기 때문에 ForbiddenException 을 던질 것이며,
3번 아이디는 role이 Admin이기 때문에 수정이 가능할 것이다.
110번 포스트는 1번 유저가 작성한 포스트이다.
1번 유저로 PATCH 진행 시 문제없이 잘 진행되며, Response 가 된 것을 확인할 수 있다.
GET 요청으로 확인 시 역시 잘 반영된 것을 볼 수 있다.
이번엔 2번 유저로 진행해보자.
ForbiddenException 가 발생하는 것을 볼 수 있다.
3번 유저는 Admin이다.
역시 잘 진행된다.
GET 요청에도 관리자는 잘 변경하는 것을 볼 수 있다.
2. IsCommentMineOrAdminGuard 구현 및 적용
포스트 외에도 댓글(Comment)도 이러한 적용이 필요하다.
2.1. comments Service 로직 추가하기
async isCommentMine(userId: number, commentId: number) {
return await this.commentsRepository.exist({
where: {
id: commentId,
author: {
id: userId,
}
},
relations: {
author: true,
}
});
}
2.2. IsCommentMineOrAdminGuard Guard 구현하기
import { CanActivate, ExecutionContext, ForbiddenException, Injectable, UnauthorizedException } from "@nestjs/common";
import { RoleEnum } from "src/users/entity/users.entity";
import { CommentsService } from "../comments.service";
@Injectable()
export class IsCommentMineOrAdminGuard implements CanActivate{
constructor(
private readonly commentService: CommentsService,
){
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
const {user} = req;
if(!user){
throw new UnauthorizedException(
'사용자 정보를 가져올 수 없습니다.',
);
}
if(user.role === RoleEnum.ADMIN){
return true;
}
const commentId = req.params.commentId;
const isOk = await this.commentService.isCommentMine(
user.id,
parseInt(commentId),
);
if(!isOk){
throw new ForbiddenException(
'권한이 없습니다.',
);
}
return true;
}
}
2.3. patchComment 에 적용하기
@Patch(':commentId')
@UseGuards(IsCommentMineOrAdminGuard)
patchComment(
@Param('commentId', ParseIntPipe) commentId: number,
@Body() body: UpdateCommentsDto,
) {
return this.commentsService.updateComment(body, commentId);
}
2.4. 포스트맨 테스트
2.4.1. 1번 사용자가 5번 댓글 수정 (관리자X, 본인)
1번 사용자는 본인이 쓴 5번 댓글을 자유롭게 수정이 가능하다.
2.4.2. 2번 사용자가 5번 댓글 수정 (관리자X, 타인)
2번 사용자는 ForbiddenException 이 발생한다.
2.4.3. 3번 사용자가 5번 댓글 수정 (관리자)
3번 사용자는 관리자이기 때문에 잘 수정된 것을 볼 수 있다.
'NestJS' 카테고리의 다른 글
[NestJS] Swagger 사용법 (1) | 2025.04.07 |
---|---|
[NestJS] process.env 사용 및 분기처리 (0) | 2025.04.05 |
[NestJS] RBAC (Role Base Access Controll) (0) | 2025.03.13 |
[NestJS] 모듈 네스팅 (0) | 2025.03.12 |
[NestJS] SocketIO 심화 (1) | 2025.03.12 |