Skip to content

[BE] @OptionalGuard 데코레이터

강창한 edited this page Mar 12, 2024 · 2 revisions

작성자 : 창한

🤷‍♂️ 왜 필요한가요?

서비스 중 동일한 요청에 로그인 여부에 따른 분기처리를 하고 싶어요.

ex) 모임 리스트 반환 API → 로그인한 사용자에게는 내가 속한 모임을 추가적으로 제공

@UseGuards()를 사용하면 비회원 접근이 불가능하고, 안붙이면 로그인 정보를 가져올 수가 없어요

🤷‍♂️ 어떻게 구현하나요?

  • NestJS의 Reflector를 활용하여 class의 MetaData에 OptionalGuard 라는 정보를 추가하는 데코레이터를 만듭니다.

    // decorator.ts
    import { SetMetadata } from '@nestjs/common';
    
    export const OPTIONAL_GUARD = 'OptionalGuard';
    export const OptionalGuard = () => {
      return SetMetadata(OPTIONAL_GUARD, true);
    };
  • UseGuard의 TokenAuthGuard 의 메소드를 상속 구현(override)을 추가합니다.

    • 이를 사용하기 위해서 생성할 때 Reflector 를 주입받도록 코드를 수정합니다.

      // auth.guard.ts
      @Injectable()
      export class TokenAuthGuard extends AuthGuard('jwt') {
        constructor(private reflector: Reflector) {
          super();
        }
        ...
      }
    • 앞서 주입받은 OptionalGuard 라는 정보를 토대로 아래와 같이 분기하여 에러 처리를 합니다.

      // auth.guard.ts
      export class TokenAuthGuard extends AuthGuard('jwt') {
      	...
      	handleRequest(err: any, user: any, info: any, context: ExecutionContext) {
      	  const optional = this.reflector.getAllAndOverride<boolean>(OPTIONAL_GUARD, [
      	    context.getHandler(),
      	    context.getClass(),
      	  ]);
      	
      	  if (optional && !user) {
      	    return null;
      	  }
      	
      	  if (err || !user) {
      	    throw err || new UnauthorizedException();
      	  }
      	  return user;
      	}
      }
    • 최종 코드

      // auth.guard.ts
      
      import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
      import { Reflector } from '@nestjs/core';
      import { AuthGuard } from '@nestjs/passport';
      import { OPTIONAL_GUARD } from '@src/decorator';
      
      @Injectable()
      export class TokenAuthGuard extends AuthGuard('jwt') {
        constructor(private reflector: Reflector) {
          super();
        }
      
        handleRequest(err: any, user: any, info: any, context: ExecutionContext) {
          const optional = this.reflector.getAllAndOverride<boolean>(OPTIONAL_GUARD, [
            context.getHandler(),
            context.getClass(),
          ]);
      
          if (optional && !user) {
            return null;
          }
      
          if (err || !user) {
            throw err || new UnauthorizedException();
          }
          return user;
        }
      }

🤷‍♂️ 어떻게 사용하나요?

UseGuards를 사용할 때 데코레이터를 함께 붙여줘요.(순서무관)

@OptionalGuard()
@UseGuards(TokenAuthGuard)

⇒ 어떻게 순서 무관하게 가능한 지, 컴파일 단계의 지식까지는 추가 학습이 필요합니다..!(아직 잘 몰라요..)

📷 예제

// GET /groups
@OptionalGuard()
@UseGuards(TokenAuthGuard)
@Get()
async getAllGroups(@Req() { user }, @Query() findOptions, paginationOptions) {
  console.log('user: ', user);
}
  • JWT 토큰이 있을 때
    • 요청

image

- 결과

image (1)

  • JWT 토큰이 없을 때
    • 요청

image (2)

- 결과

image (3)

Reference

decorator
optional_authentication_in_nestjs-stackoverflow
Reflector-NestJS
Reflect-MDN
nestjs-custom-decorator-toss

MetaData
중복코드 따운 ! 재사용성 비상 ! mapped-type나와 !

Lock Festival 🔒

Rules

개발일지

Description

학습 노트

회의록

사전 회의
1주차 회의록
2주차 회의록
3주차 회의록
4주차 회의록
5주차 회의록
6주차 회의록

데일리 스크럼

1주차
2주차
3주차
4주차
5주차
6주차

회고록

1주차 회고록
2주차 회고록
3주차 회고록
4주차 회고록
5주차 회고록
6주차 회고록

스프린트

멘토링 일지

Clone this wiki locally