[Gosrock/Nestjs] Guard 사용중인 Controller 내부 특정 메소드에 모든 접근 허가하기 (NoAuth)
고스락 티켓 예매 페이지 22th 의 일부인
@NoAuth 데코레이터 구현에 관한 글입니다
우선 이 글은 Nestjs 에서
AuthGuard 등의 Custom Guard 구현에 대해 알고 있다고 가정하고 작성했습니다
📝 Reference
Guards - https://jakekwak.gitbook.io/nestjs/overview/guards
🔍 문제 상황
우선 고스락 티켓 프로젝트에서는 AccessTokenGuard 라는 커스텀 가드를 구현했고
이는 Role 기반으로 엑세스 토큰에서 유저와 그 권한을 뽑아와서
해당 요청 메소드에 접근 권한이 있는지 확인하고 접근 인가 / 불가 처리를 하는 것이다
자세한 것은 고티켓 팀장님의 포스팅을 참고하십쇼
https://devnm.tistory.com/16?category=1258201
📝 tickets.controller.ts
@ApiTags('tickets')
@ApiBearerAuth('accessToken')
@Controller('tickets')
@UseGuards(AccessTokenGuard)
export class TicketsController {
constructor(private ticketService: TicketsService) {}
...
...
}
내가 맡았던 구현 부분 중 티켓 컨트롤러의 예시
@UseGuards 데코레이터를 통해 구현해둔 AccessTokenGuard 를 사용하도록 처리했다
문제는 이렇게 할 경우 중간에 특정 메소드만 권한을 부여하기가 어렵다는 것이다
사실 어렵지는 않지만
전역 가드를 떼어낸 후에 요청에 권한이 필요한 각 메소드마다 다시 가드를 붙이는 식으로
코드 구조가 쓸데없이 난잡해지고 귀찮아진다
개발자가 제일 싫어하는 구조? 라고 해야되나
그래서 나온 해결 책은 구조는 그대로 두되
새로운 데코레이터를 만드는 것이다
📌 @NoAuth
📝 NoAuth.guard.ts
import { SetMetadata } from '@nestjs/common';
export const NoAuth = () => SetMetadata('no-auth', true);
데코레이터를 하나 만드는 방법은 어렵지 않다
인자로 받은 애들을 특정 이름의 메타데이터로 변환 시켜주면 된다
이제 특정 메소드에 @NoAuth() 를 붙여주게 되면
해당 메소드의 메타데이터로 'no-auth': true 라는 값이 추가된다
🚀 코드 수정
📝 AccessToken.guard.ts
@Injectable()
export class AccessTokenGuard implements CanActivate {
constructor(private authService: AuthService, private reflector: Reflector) {}
canActivate(
context: ExecutionContext
): boolean | Promise<boolean> | Observable<boolean> {
//@NoAuth 사용시 해당 부분에서 AccessTokenGuard 사용 해제시킴
const noAuth = this.reflector.get<boolean>('no-auth', context.getHandler());
if (noAuth) {
return true;
}
const request = context.switchToHttp().getRequest();
return this.validateRequest(request, context);
}
private async validateRequest(request: Request, context: ExecutionContext) {
...
...
...
}
이제 프로젝트에서 사용중인 AuthGuard 라던지,,,
여기서는 AccessTokenGuard 같이 내가 사용중인 가드의 코드를 약간만 수정하면 된다
Nestjs 에서 커스텀 가드는 반드시 CanActivate 를 구현해야 한다
가드는 특정 요청을 가로챈 후에
CanActivate 의 값이 true 이면 요청을 승인하고 그렇지 않으면 거부한다
이제 ExecutionContext 에서 getHandler 로
AccessTokenGuard를 사용중인 컨트롤러에서 메소드의 참조값을 가져온다
이후 리플렉터로 여기서 우리가 구현했던 메타데이터인 'no-auth' 값을 뽑아온다
getHandler 로 가져온 메소드가 @NoAuth() 데코레이터를 사용중이라면
'no-auth': true 일 것이고 그렇지 않다면 undefined, 즉 false 이다
'no-auth': true 이면 가드를 즉시 통과시키고
그렇지 않다면 원래처럼 가드에서 인증을 처리하면 된다
🚀 NoAuth 적용
📝 tickets.controller.ts
...
@ApiOperation({
summary: '[랜딩페이지] 티켓 개수를 반환한다'
})
@ApiResponse({
status: 200,
description: '요청 성공시',
type: TicketCountDto
})
@NoAuth()
@Get('/count')
async getTicketCount() {
const count = await this.ticketService.countTicket();
return { count: count };
}
...
@NoAuth() 를 필요로 하는 메소드에 적용시켰다
이 메소드는 GET /count 요청을 날렸을 때
DB 내부에 몇 장의 티켓이 있는지 반환해주는 메소드이다
랜딩페이지에서 티켓이 몇 개 주문됐는지 보여줄 필요가 있는데
로그인하지 않은 사용자도 이를 볼 수 있어야 하기 때문에 구현하게 되었다
바로 이 부분을 위해서...
구현은 알고보면 매우 간단하지만
Nestjs 의 가드와 데코레이터의 동작 원리를 이해하지 못했다면 상당히 복잡해 보일 것이다
아무쪼록 더 까먹기 전에
얼른 구현하면서 공부했던 것들을 정리해야겠다
사이트가 궁금하다면~~~