로컬인증가드를 적용하여 로그인 API를 구현하였다. 이때 로컬인증가드를 적용한 login 라우트 핸들러는 파라미터로 @Body()를 사용하지 않아 로그인 dto에 선언한 class-validator의 유효성을 검증할 수 없게 되었다.
로컬인증가드를 적용한 login 라우트 핸들러에서도 로그인 dto에 선언한 class-validator의 유효성을 검증하고 싶었다. 구글링 결과 방법을 찾을 수 있었다.
로그인 dto 코드는 다음과 같다.
import { CreateUserDto } from 'src/users';
export class LogInDto extends 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-transformer
의 plainToClass
를 사용해 요청 객체의 바디를 로그인 dto인 LogInDto 클래스의 인스턴스로 변경한다.class-validator
의 validate
메서드를 사용해 위에서 얻은 LogInDto 인스턴스의 유효성을 검사한다.