[NestJS] 로컬인증가드에서 로그인 dto 유효성 검증하기

cabbage·2023년 11월 26일
0

NestJS

목록 보기
17/17
post-thumbnail

서론

로컬인증가드를 적용하여 로그인 API를 구현하였다. 이때 로컬인증가드를 적용한 login 라우트 핸들러는 파라미터로 @Body()를 사용하지 않아 로그인 dto에 선언한 class-validator의 유효성을 검증할 수 없게 되었다.

로컬인증가드를 적용한 login 라우트 핸들러에서도 로그인 dto에 선언한 class-validator의 유효성을 검증하고 싶었다. 구글링 결과 방법을 찾을 수 있었다.

로컬인증가드에서 로그인 dto 유효성 검증하기

로그인 dto 코드는 다음과 같다.

import { CreateUserDto } from 'src/users';

export class LogInDto extends CreateUserDto {}
  • 로그인 dto인 LogInDto는 CreateUserDto를 상속한다.
  • CreateUserDto와 동일한 프로퍼티를 사용하고 동일한 유효성 검사를 진행하고 싶어 CreateUserDto를 상속하였다.
    import { IsNotEmpty, IsString, Matches } from 'class-validator';
    
    export class CreateUserDto {
    	@IsNotEmpty({ message: 'username 필드는 필수 입력 필드입니다.' })
    	@IsString({ message: 'username 필드에 문자열을 입력해야 합니다.' })
    	username: string;
    
    	@IsNotEmpty({ message: 'password 필드는 필수 입력 필드입니다.' })
    	@IsString({ message: 'password 필드에 문자열을 입력해야 합니다.' })
    	@Matches(/^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*?_]).{8,16}$/, {
    		message:
    			'패스워드는 8~16자리이며 최소 하나 이상의 영문자, 최소 하나 이상의 숫자, 최소 하나 이상의 특수문자를 입력해야 합니다.',
    	})
    	password: string;
    }

로컬인증가드를 적용한 login 라우트 핸들러 코드는 다음과 같다.

@Post('login')
@UseGuards(LocalLoginAuthGuard)
@ResponseMessage(AuthResponse.LOGIN)
async login(
	@GetUser() user: User, //
) {
	return await this.authService.login(user);
}

이 경우 로그인 dto의 유효성을 검증하려면 로컬인증가드의 canActivate 메서드 내부에서 로그인 dto의 유효성을 검증할 수 있다. 로그인 dto의 유효성을 검증하는 로컬인증가드 코드는 다음과 같다.

import { BadRequestException, ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { plainToClass } from 'class-transformer';
import { Request } from 'express';
import { LogInDto } from '../dto';
import { validate } from 'class-validator';

@Injectable()
export class LocalLoginAuthGuard extends AuthGuard('local') {
	async canActivate(context: ExecutionContext): Promise<boolean> {
		const request = context.switchToHttp().getRequest<Request>();

		// 요청 객체의 바디를 클래스 인스턴스로 변경
		const body = plainToClass(LogInDto, request.body);

		// 요청 바디의 유효성을 검사하여 에러 리스트 가져오기
		const errors = await validate(body);

		// 에러 메세지가 추출된다면 유효성 검사 실패
		const errorMessages = errors.flatMap(({ constraints }) => Object.values(constraints));

		// 유효성 검사 실패 시 BadRequest 응답
		if (errorMessages.length > 0) {
			throw new BadRequestException(errorMessages);
		}

		return super.canActivate(context) as Promise<boolean>;
	}
}
  • class-transformerplainToClass를 사용해 요청 객체의 바디를 로그인 dto인 LogInDto 클래스의 인스턴스로 변경한다.
  • class-validatorvalidate 메서드를 사용해 위에서 얻은 LogInDto 인스턴스의 유효성을 검사한다.
  • 유효성 검사 진행 후 에러 메세지가 존재한다면 LogInDto 인스턴스의 유효성 검사가 실패했다는 의미가 된다.
  • 따라서 유효성 검사가 실패했다면 에러 메세지와 함께 BadRequestException을 던진다.

참고 링크

profile
캐비지 개발 블로그입니다. :)

0개의 댓글