diff --git a/src/app.controller.ts b/src/app.controller.ts index 67d3b98..28b8632 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -8,6 +8,7 @@ import { } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ThrottlerException } from '@nestjs/throttler'; +import { AuthErrorDefine } from './auth/Errors/AuthErrorDefine'; import { ErrorResponse } from './common/decorators/ErrorResponse.decorator'; import { CustomValidationError } from './common/errors/ValidtionError'; @@ -19,63 +20,26 @@ export class CommonController { model: BadRequestException, exampleDescription: '400번 BadRequestException', exampleTitle: '400 번의 BadRequestException', - exampleMessageInfo: '에러메시지 형식이 옵니다.' + message: '에러메시지 형식이 옵니다.' }, { model: CustomValidationError, exampleDescription: '400번 ValidationError', exampleTitle: '400번 ValidationError', - exampleMessageInfo: { + message: { '검증오류가난 필드': ['어떤에러', '어떤에러2'], '검증오류가난 필드2': ['어떤에러3', '어떤에러4'] } } ]) @ErrorResponse(HttpStatus.UNAUTHORIZED, [ - { - model: UnauthorizedException, - exampleDescription: '헤더 Bearer 형식을 지키지 않고 잘못 요청 보냈을 때', - exampleTitle: '어세스토큰-잘못된 헤더 요청', - exampleMessageInfo: '잘못된 헤더 요청' - }, - { - model: UnauthorizedException, - exampleDescription: - '손상되거나 Bearer 형식은 맞췄는데 토큰이 이상한 토큰일 때', - exampleTitle: '어세스토큰-잘못된 토큰', - exampleMessageInfo: '잘못된 토큰' - }, - { - model: UnauthorizedException, - exampleDescription: '기한이 지난 토큰일때', - exampleTitle: '어세스토큰-기한만료', - exampleMessageInfo: '기한만료' - }, - { - model: UnauthorizedException, - exampleDescription: - '어세스토큰은 살아있지만 db에서 유저가 삭제되었을때 (테스트할때 발생할수있는 오류)', - exampleTitle: '어세스토큰-유저없음', - exampleMessageInfo: '없는 유저입니다.' - } - ]) - @ErrorResponse(HttpStatus.FORBIDDEN, [ - { - model: ForbiddenException, - exampleDescription: '일반 유저가 관리자의 메소드에 접근 하려고 할때', - exampleTitle: '관리자메소드에 유저가 접근', - exampleMessageInfo: '권한이 없습니다.' - } - ]) - @ErrorResponse(HttpStatus.TOO_MANY_REQUESTS, [ - { - model: ThrottlerException, - exampleDescription: - '과도한 요청을 보낼시에 ( 인증문자 요청 , 관리자 슬랙 인증 요청', - exampleTitle: '과도한 요청', - exampleMessageInfo: 'ThrottlerException: Too Many Requests' - } + AuthErrorDefine['Auth-1000'], + AuthErrorDefine['Auth-1001'], + AuthErrorDefine['Auth-1002'], + AuthErrorDefine['Auth-1003'] ]) + @ErrorResponse(HttpStatus.FORBIDDEN, [AuthErrorDefine['Auth-3000']]) + @ErrorResponse(HttpStatus.TOO_MANY_REQUESTS, [AuthErrorDefine['Auth-9000']]) @ApiOperation({ summary: '공통 오류에 대해서 정의 합니다.' }) @Get() commen() {} diff --git a/src/auth/Errors/AuthErrorDefine.ts b/src/auth/Errors/AuthErrorDefine.ts new file mode 100644 index 0000000..66cbe52 --- /dev/null +++ b/src/auth/Errors/AuthErrorDefine.ts @@ -0,0 +1,136 @@ +import { + BadRequestException, + ForbiddenException, + InternalServerErrorException, + UnauthorizedException +} from '@nestjs/common'; +import { ThrottlerException } from '@nestjs/throttler'; +import { ErrorResponseOption } from 'src/common/decorators/ErrorResponse.decorator'; +type Keys = + | 'Auth-1000' + | 'Auth-1001' + | 'Auth-1002' + | 'Auth-1003' + | 'Auth-3000' + | 'Auth-9000' + | 'Auth-5000' + | 'Auth-0000' + | 'Auth-0001' + | 'Auth-0002' + | 'Auth-0003' + | 'Auth-0004' + | 'Auth-0005'; + +export const AuthErrorDefine: Record< + Keys, + ErrorResponseOption & { code: string } +> = { + //400 + 'Auth-0000': { + model: BadRequestException, + exampleDescription: '3분짜리 인증번호 기한만료시에 발생하는 오류', + exampleTitle: '인증번호-기한만료', + message: '인증번호가 기한만료 되었습니다.', + code: 'Auth-0000' + }, + 'Auth-0001': { + model: BadRequestException, + exampleDescription: '인증번호가 일치하지 않으면 발생하는 오류', + exampleTitle: '인증번호-불일치', + message: '인증번호가 일치하지 않습니다.', + code: 'Auth-0001' + }, + 'Auth-0002': { + model: BadRequestException, + exampleDescription: '중복해서 회원가입을 시도하면 막습니다.', + exampleTitle: '중복회원가입요청', + message: '이미 회원가입한 유저입니다.', + code: 'Auth-0002' + }, + + 'Auth-0003': { + model: BadRequestException, + exampleDescription: '슬랙에 등록되지않은 유저일때 발생하는 오류', + exampleTitle: '정보오류-유저정보,슬랙정보없음', + message: '가입한 유저나 어드민 유저가 아닙니다.', + code: 'Auth-0003' + }, + 'Auth-0004': { + model: BadRequestException, + exampleDescription: '받은 슬랙이메일이 올바르지않을경우', + exampleTitle: '정보오류-슬랙정보없음', + message: '가입한 슬랙 이메일을 올바르게 입력해 주세요', + code: 'Auth-0004' + }, + + 'Auth-0005': { + model: BadRequestException, + exampleDescription: '가입한 유저나 어드민 유저가 아닐때', + exampleTitle: '인증번호-검증이후', + message: '가입한 유저나 어드민 유저가 아닙니다.', + code: 'Auth-0005' + }, + + //401 + 'Auth-1000': { + model: UnauthorizedException, + exampleDescription: '헤더 Bearer 형식을 지키지 않고 잘못 요청 보냈을 때', + exampleTitle: '어세스토큰-잘못된 헤더 요청', + message: '잘못된 헤더 요청', + code: 'Auth-1000' + }, + + 'Auth-1001': { + model: UnauthorizedException, + exampleDescription: + '손상되거나 Bearer 형식은 맞췄는데 토큰이 이상한 토큰일 때', + exampleTitle: '어세스토큰-잘못된 토큰', + message: '잘못된 토큰', + code: 'Auth-1001' + }, + + 'Auth-1002': { + model: UnauthorizedException, + exampleDescription: '기한이 지난 토큰일때', + exampleTitle: '어세스토큰-기한만료', + message: '기한만료', + code: 'Auth-1002' + }, + 'Auth-1003': { + model: UnauthorizedException, + exampleDescription: + '어세스토큰은 살아있지만 db에서 유저가 삭제되었을때 (테스트할때 발생할수있는 오류)', + exampleTitle: '어세스토큰-유저없음', + message: '없는 유저입니다.', + code: 'Auth-1003' + }, + + //403 + 'Auth-3000': { + model: ForbiddenException, + exampleDescription: '일반 유저가 관리자의 메소드에 접근 하려고 할때', + exampleTitle: '관리자메소드에 유저가 접근', + message: '권한이 없습니다.', + code: 'Auth-3000' + }, + + //500 + 'Auth-5000': { + model: InternalServerErrorException, + exampleDescription: '문자메시지 발송이 실패하면 발생합니다.', + exampleTitle: '문자메시지 발송실패 오류', + message: + '문자메시지 발송 실패. 고스락 카카오톡 채널을 활용해서 관리자한테 연락 부탁드립니다.', + code: 'Auth-5000' + }, + + // 스로틀 + 'Auth-9000': { + model: ThrottlerException, + exampleDescription: + '과도한 요청을 보낼시에 ( 인증문자 요청 , 관리자 슬랙 인증 요청', + exampleTitle: '과도한 요청', + message: 'ThrottlerException: Too Many Requests', + code: 'Auth-9000' + } +}; diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index fa76997..eb411b4 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -40,6 +40,7 @@ import { LoginResponseDto } from './dtos/Login.response.dto'; import { ErrorResponse } from 'src/common/decorators/ErrorResponse.decorator'; import { ThrottlerException } from '@nestjs/throttler'; import { SuccessResponse } from 'src/common/decorators/SuccessResponse.decorator'; +import { AuthErrorDefine } from './Errors/AuthErrorDefine'; @ApiTags('auth') @Controller('auth') @@ -70,23 +71,9 @@ export class AuthController { } ]) @ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, [ - { - model: InternalServerErrorException, - exampleDescription: '문자메시지 발송이 실패하면 발생합니다.', - exampleTitle: '문자메시지 발송실패 오류', - exampleMessageInfo: - '문자메시지 발송 실패. 고스락 카카오톡 채널을 활용해서 관리자한테 연락 부탁드립니다.' - } - ]) - @ErrorResponse(HttpStatus.TOO_MANY_REQUESTS, [ - { - model: ThrottlerException, - exampleDescription: - '과도한 요청을 보낼시에 ( 인증문자 요청 , 관리자 슬랙 인증 요청', - exampleTitle: '과도한 요청', - exampleMessageInfo: 'ThrottlerException: Too Many Requests' - } + AuthErrorDefine['Auth-5000'] ]) + @ErrorResponse(HttpStatus.TOO_MANY_REQUESTS, [AuthErrorDefine['Auth-9000']]) @Post('message/send') async requestPhoneValidationNumber( @Body() requestPhoneNumberDto: RequestPhoneNumberDto @@ -113,18 +100,8 @@ export class AuthController { } ]) @ErrorResponse(HttpStatus.BAD_REQUEST, [ - { - model: BadRequestException, - exampleDescription: '3분짜리 인증번호 기한만료시에 발생하는 오류', - exampleTitle: '인증번호-기한만료', - exampleMessageInfo: '인증번호가 기한만료 되었습니다.' - }, - { - model: BadRequestException, - exampleDescription: '인증번호가 일치하지 않으면 발생하는 오류', - exampleTitle: '인증번호-불일치', - exampleMessageInfo: '인증번호가 일치하지 않습니다.' - } + AuthErrorDefine['Auth-0000'], + AuthErrorDefine['Auth-0001'] ]) @Post('message/validate') async validationPhoneNumber( @@ -150,14 +127,7 @@ export class AuthController { } ]) @UseGuards(RegisterTokenGuard) - @ErrorResponse(HttpStatus.BAD_REQUEST, [ - { - model: BadRequestException, - exampleDescription: '중복해서 회원가입을 시도하면 막습니다.', - exampleTitle: '중복회원가입요청', - exampleMessageInfo: '이미 회원가입한 유저입니다.' - } - ]) + @ErrorResponse(HttpStatus.BAD_REQUEST, [AuthErrorDefine['Auth-0002']]) @Post('register') async registerUser( @RegisterUser() registerUser: RegisterJwtPayload, @@ -178,28 +148,10 @@ export class AuthController { type: ResponseAdminSendValidationNumberDto }) @ErrorResponse(HttpStatus.BAD_REQUEST, [ - { - model: BadRequestException, - exampleDescription: '슬랙에 등록되지않은 유저일때 발생하는 오류', - exampleTitle: '정보오류-유저정보,슬랙정보없음', - exampleMessageInfo: '가입한 유저나 어드민 유저가 아닙니다.' - }, - { - model: BadRequestException, - exampleDescription: '받은 슬랙이메일이 올바르지않을경우', - exampleTitle: '정보오류-슬랙정보없음', - exampleMessageInfo: '가입한 슬랙 이메일을 올바르게 입력해 주세요' - } - ]) - @ErrorResponse(HttpStatus.TOO_MANY_REQUESTS, [ - { - model: ThrottlerException, - exampleDescription: - '과도한 요청을 보낼시에 ( 인증문자 요청 , 관리자 슬랙 인증 요청', - exampleTitle: '과도한 요청', - exampleMessageInfo: 'ThrottlerException: Too Many Requests' - } + AuthErrorDefine['Auth-0003'], + AuthErrorDefine['Auth-0004'] ]) + @ErrorResponse(HttpStatus.TOO_MANY_REQUESTS, [AuthErrorDefine['Auth-9000']]) @ApiBody({ type: RequestAdminSendValidationNumberDto }) @Post('/slack/send') async slackSendValidationNumber( @@ -220,24 +172,9 @@ export class AuthController { }) @ApiBody({ type: RequestAdminLoginDto }) @ErrorResponse(HttpStatus.BAD_REQUEST, [ - { - model: BadRequestException, - exampleDescription: '3분짜리 인증기한이 지났을때', - exampleTitle: '인증번호-기한만료', - exampleMessageInfo: '인증 기한이 지났습니다.' - }, - { - model: BadRequestException, - exampleDescription: '인증번호가 맞지 않을때', - exampleTitle: '인증번호-검증오류', - exampleMessageInfo: '인증 번호가 맞지 않습니다.' - }, - { - model: BadRequestException, - exampleDescription: '가입한 유저나 어드민 유저가 아닐때', - exampleTitle: '인증번호-검증이후', - exampleMessageInfo: '가입한 유저나 어드민 유저가 아닙니다.' - } + AuthErrorDefine['Auth-0000'], + AuthErrorDefine['Auth-0001'], + AuthErrorDefine['Auth-0005'] ]) @Post('/slack/validation') async slackLoginUser( diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index abb1371..320706c 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -33,6 +33,7 @@ import { SmsService } from 'src/sms/sms.service'; import { MessageDto } from 'src/sms/dtos/message.dto'; import { SlackValidationNumberDMDto } from 'src/slack/dtos/SlackValidationNumberDM.dto'; import { returnValueToDto } from 'src/common/decorators/returnValueToDto.decorator'; +import { AuthErrorDefine } from './Errors/AuthErrorDefine'; @Injectable() export class AuthService { @@ -67,7 +68,10 @@ export class AuthService { try { await this.smsService.sendMessages([message]); } catch (error) { - throw new InternalServerErrorException('문자발송 실패'); + throw new InternalServerErrorException( + AuthErrorDefine['Auth-5000'], + '네이버 문자발송이 실패할시 보내는 오류' + ); } // insert to redis @@ -99,10 +103,16 @@ export class AuthService { // 인증이 유효하지 않다면 if (!savedValidationNumber) { - throw new BadRequestException('인증번호가 기한만료 되었습니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0000'], + '인증번호 기한이 만료되었을때 보내는 오류' + ); } if (savedValidationNumber !== requestValidateNumberDto.validationNumber) { - throw new BadRequestException('인증번호가 일치하지 않습니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0001'], + '인증 번호가 일치하지 않았을때 보내는 오류' + ); } // 회원가입을 한 유저가아니라면 회원가입용 토큰 발급 if (!checkSingUpState) { @@ -114,7 +124,7 @@ export class AuthService { const user = await this.userRepository.findByPhoneNumber(userPhoneNumber); //console.log(user); if (!user) { - throw new BadRequestException('잘못된 접근'); + throw new BadRequestException('잘못된 접근', '비정상 접근입니다.'); } const accessToken = this.accessJwtSign({ id: user.id, @@ -139,7 +149,10 @@ export class AuthService { registerUser.phoneNumber ); if (checkUserAlreadySignUp) { - throw new BadRequestException('이미 회원가입한 유저입니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0002'], + '중복 회원가입 요청할때 발생하는 오류' + ); } // 트랜잭션 예시 // typeOrm 으로 부터 주입받은 dataSource(커넥션 풀) 로부터 쿼리러너를 받고 @@ -196,19 +209,27 @@ export class AuthService { ); if (!searchUser) { - throw new BadRequestException('가입한 유저나 어드민 유저가 아닙니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0003'], + '가입한 유저나 어드민 유저가 아닐때 발생하는 오류' + ); } if (searchUser.role !== Role.Admin) { - throw new BadRequestException('가입한 유저나 어드민 유저가 아닙니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0003'], + '가입한 유저나 어드민 유저가 아닐때 발생하는 오류' + ); } // 레디스에서 전화번호가지고 정보를 빼내온다. + const slaceUserId = await this.slackService.findSlackUserIdByEmail( requestAdminSendValidationNumberDto.slackEmail ); //console.log(slaceUserId); if (!slaceUserId) { throw new BadRequestException( - '가입한 슬랙 이메일을 올바르게 입력해 주세요' + AuthErrorDefine['Auth-0004'], + '슬랙 이메일이 고스락 채널 정보에 없을때 발생하는 오류' ); } @@ -234,13 +255,19 @@ export class AuthService { requestAdminLoginDto.slackEmail ); if (!findValidationNumberFromRedis) { - throw new BadRequestException('인증 기한이 지났습니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0000'], + '3분짜리 인증번호 기한이 지났을 때 발생하는 오류입니당.' + ); } if ( findValidationNumberFromRedis !== requestAdminLoginDto.validationNumber ) { - throw new BadRequestException('인증 번호가 맞지 않습니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0001'], + '인증번호 검증이 알맞지 않을때 발생하는 오류입니다.' + ); } const searchUser = await this.userRepository.findByPhoneNumber( @@ -248,10 +275,16 @@ export class AuthService { ); if (!searchUser) { - throw new BadRequestException('가입한 유저나 어드민 유저가 아닙니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0005'], + '가입한 유저나 어드민 유저가 아닙니다.' + ); } if (searchUser.role !== Role.Admin) { - throw new BadRequestException('가입한 유저나 어드민 유저가 아닙니다.'); + throw new BadRequestException( + AuthErrorDefine['Auth-0005'], + '가입한 유저나 어드민 유저가 아닙니다.' + ); } const accessToken = this.accessJwtSign({ ...searchUser }); return { @@ -314,8 +347,14 @@ export class AuthService { }; } catch (e) { if (e.name === 'TokenExpiredError') - throw new UnauthorizedException('기한만료'); - throw new UnauthorizedException('잘못된 토큰'); + throw new UnauthorizedException( + AuthErrorDefine['Auth-1002'], + '토큰 기한만료시 발생되는 에러' + ); + throw new UnauthorizedException( + AuthErrorDefine['Auth-1001'], + '잘못된 토큰일시 발생되는 에러' + ); } } @@ -338,8 +377,14 @@ export class AuthService { }; } catch (e) { if (e.name === 'TokenExpiredError') - throw new UnauthorizedException('기한만료'); - throw new UnauthorizedException('잘못된 토큰'); + throw new UnauthorizedException( + AuthErrorDefine['Auth-1002'], + '토큰 기한만료시 발생되는 에러' + ); + throw new UnauthorizedException( + AuthErrorDefine['Auth-1001'], + '잘못된 토큰일시 발생되는 에러' + ); } } diff --git a/src/auth/guards/AccessToken.guard.ts b/src/auth/guards/AccessToken.guard.ts index 2b047d5..5c702d8 100644 --- a/src/auth/guards/AccessToken.guard.ts +++ b/src/auth/guards/AccessToken.guard.ts @@ -11,6 +11,7 @@ import { AuthService } from '../auth.service'; import { UsersService } from 'src/users/users.service'; import { Reflector } from '@nestjs/core'; import { Role } from 'src/common/consts/enum'; +import { AuthErrorDefine } from '../Errors/AuthErrorDefine'; @Injectable() export class AccessTokenGuard implements CanActivate { @@ -26,10 +27,16 @@ export class AccessTokenGuard implements CanActivate { private async validateRequest(request: Request, context: ExecutionContext) { const checkHeader = request.headers.authorization; if (!checkHeader) { - throw new UnauthorizedException('잘못된 헤더 요청'); + throw new UnauthorizedException( + AuthErrorDefine['Auth-1000'], + '잘못된 헤더 형식으로 요청보냈을때 발생하는 에러' + ); } if (Array.isArray(checkHeader)) { - throw new UnauthorizedException('잘못된 헤더 요청'); + throw new UnauthorizedException( + AuthErrorDefine['Auth-1000'], + '잘못된 헤더 형식으로 요청보냈을때 발생하는 에러' + ); } const jwtString = checkHeader.split('Bearer ')[1]; @@ -46,7 +53,10 @@ export class AccessTokenGuard implements CanActivate { const user = await this.authService.findUserById(payload.id); if (!user) { - throw new UnauthorizedException('없는 유저입니다.'); + throw new UnauthorizedException( + AuthErrorDefine['Auth-1003'], + '디비에서 유저 조회시에 발생하는 오류' + ); } const newObj: any = request; newObj.user = user; @@ -64,7 +74,10 @@ export class AccessTokenGuard implements CanActivate { } else if (user.role === Role.Admin) { return true; } else { - throw new ForbiddenException('권한이 없습니다.'); + throw new ForbiddenException( + AuthErrorDefine['Auth-3000'], + '어드민 롤에 일반유저가 접근한 경우' + ); } } } diff --git a/src/auth/guards/TrottlerBehindProxy.guard.ts b/src/auth/guards/TrottlerBehindProxy.guard.ts index 51b2da0..ec12ea2 100644 --- a/src/auth/guards/TrottlerBehindProxy.guard.ts +++ b/src/auth/guards/TrottlerBehindProxy.guard.ts @@ -2,10 +2,15 @@ // 고스락 백엔드 서버는 nginx 뒤에 프록시 형태로 연결되어있기 때문에 // X-Forwarded-For 헤더값을 통해서 // 요청한 사람의 원래 ip 주소를 가져와야합니다. -import { ThrottlerGuard } from '@nestjs/throttler'; -import { Injectable } from '@nestjs/common'; +import { ThrottlerException, ThrottlerGuard } from '@nestjs/throttler'; +import { + BadRequestException, + ExecutionContext, + Injectable +} from '@nestjs/common'; import { Request } from 'express'; import { v4 } from 'uuid'; +import { AuthErrorDefine } from '../Errors/AuthErrorDefine'; @Injectable() export class ThrottlerBehindProxyGuard extends ThrottlerGuard { @@ -24,6 +29,9 @@ export class ThrottlerBehindProxyGuard extends ThrottlerGuard { return req.ips.length ? req.ips[0] : req.ip; // individualize IP extraction to meet your own needs } } + // protected throwThrottlingException(context: ExecutionContext): void { + // throw new ThrottlerException(AuthErrorDefine['Auth-9000']); + // } } // app.controller.ts diff --git a/src/common/decorators/ErrorResponse.decorator.ts b/src/common/decorators/ErrorResponse.decorator.ts index 289eec7..d1b9d5e 100644 --- a/src/common/decorators/ErrorResponse.decorator.ts +++ b/src/common/decorators/ErrorResponse.decorator.ts @@ -18,7 +18,7 @@ import { ValidationErrorResponseDto } from '../errors/ValidationError.response.d import { CustomValidationError } from '../errors/ValidtionError'; import { makeInstanceByApiProperty } from '../utils/makeInstanceByApiProperty'; -interface ErrorResponseOption { +export interface ErrorResponseOption { /** * HttpException을 extend한 에러 타입을 인자로 받습니다. * 예시 : BadRequestException @@ -31,11 +31,15 @@ interface ErrorResponseOption { /** * 서비스 레이어에서 적었던 오류 메시지를 기술합니다. */ - exampleMessageInfo: string | Record>; + message: string | Record>; /** * 어떠한 상황일 때 오류가나는지 기술합니다. */ exampleDescription: string; + /** + * 에러 코드에 대해 기술합니다. + */ + code?: string; } /** @@ -55,22 +59,21 @@ export const ErrorResponse = ( let innerErrorDto; if (error.model === CustomValidationError) { flagValidationErrorExist = true; - if (typeof error.exampleMessageInfo === 'string') { + if (typeof error.message === 'string') { throw Error( '검증오류는 넘겨줄때 Record> 타입으로 주셔야합니다.' ); } - innerErrorDto = new ValidationErrorResponseDto( - error.exampleMessageInfo - ); + innerErrorDto = new ValidationErrorResponseDto(error.message); } else { - if (typeof error.exampleMessageInfo !== 'string') { + if (typeof error.message !== 'string') { throw Error('http오류는 넘겨줄때 string 타입으로 주셔야합니다.'); } innerErrorDto = new HttpExceptionErrorResponseDto( StatusCode, error.model.name, - error.exampleMessageInfo + error.message, + error.code ); } const commonErrorInstance = diff --git a/src/common/errors/HttpExceptionError.response.dto.ts b/src/common/errors/HttpExceptionError.response.dto.ts index f364f68..3fe1f47 100644 --- a/src/common/errors/HttpExceptionError.response.dto.ts +++ b/src/common/errors/HttpExceptionError.response.dto.ts @@ -26,10 +26,24 @@ export class HttpExceptionErrorResponseDto { @Expose() statusCode: number; - constructor(statusCode: number, error: string, message: string) { + @ApiProperty({ + type: String, + description: '에러코드가 넘어옵니다. 널값일 수 있습니다!!!', + nullable: true + }) + @Expose() + code?: string; + + constructor( + statusCode: number, + error: string, + message: string, + code?: string + ) { this.error = error; this.statusCode = statusCode; this.message = message; + this.code = code; } // @ApiProperty({ type: () => PageMetaDto }) diff --git a/src/common/errors/ValidationError.response.dto.ts b/src/common/errors/ValidationError.response.dto.ts index 59f90f5..e4cc1cd 100644 --- a/src/common/errors/ValidationError.response.dto.ts +++ b/src/common/errors/ValidationError.response.dto.ts @@ -13,6 +13,14 @@ export class ValidationErrorResponseDto { @Expose() error = 'ValidationError'; + @ApiProperty({ + type: String, + description: '밸리데이션 에러는 코드도 ValidationError입니다.', + example: 'ValidationError' + }) + @Expose() + code = 'ValidationError'; + @ApiProperty({ type: String, description: '에러메시지', diff --git a/src/common/errors/ValidtionError.ts b/src/common/errors/ValidtionError.ts index 04a3696..2b72b34 100644 --- a/src/common/errors/ValidtionError.ts +++ b/src/common/errors/ValidtionError.ts @@ -23,10 +23,11 @@ export class CustomValidationError extends HttpException { }, {}); // null 값 있을경우 필터링 super( { - error: 'Validation Error', + error: 'ValidationError', message: '검증 오류', validationErrorInfo: objectsOfError, - statusCode: HttpStatus.BAD_REQUEST + statusCode: HttpStatus.BAD_REQUEST, + code: 'ValidationError' }, HttpStatus.BAD_REQUEST ); diff --git a/src/common/exceptions/http-exception.filter.ts b/src/common/exceptions/http-exception.filter.ts index efdffce..817808d 100644 --- a/src/common/exceptions/http-exception.filter.ts +++ b/src/common/exceptions/http-exception.filter.ts @@ -7,10 +7,13 @@ import { UnauthorizedException, Logger } from '@nestjs/common'; +import { ThrottlerException } from '@nestjs/throttler'; import { Request, Response } from 'express'; +import { AuthErrorDefine } from 'src/auth/Errors/AuthErrorDefine'; import { SlackService } from 'src/slack/slack.service'; import { ErrorCommonResponse } from '../errors/ErrorCommonResponse.dto'; import { HttpExceptionErrorResponseDto } from '../errors/HttpExceptionError.response.dto'; +import { CustomValidationError } from '../errors/ValidtionError'; @Catch() export class AllExceptionsFilter implements ExceptionFilter { @@ -23,7 +26,22 @@ export class AllExceptionsFilter implements ExceptionFilter { let statusCode: number; let error: HttpExceptionErrorResponseDto; - if (exception instanceof HttpException) { + if (exception instanceof ThrottlerException) { + statusCode = 429; + error = { + code: AuthErrorDefine['Auth-9000'].code, + message: AuthErrorDefine['Auth-9000'].message as string, + error: ThrottlerException.name, + statusCode: 429 + }; + } else if (exception instanceof CustomValidationError) { + statusCode = exception.getStatus(); + const getError = exception.getResponse(); + const objError = getError as HttpExceptionErrorResponseDto; + error = { + ...objError + }; + } else if (exception instanceof HttpException) { statusCode = exception.getStatus(); const getError = exception.getResponse(); if (typeof getError === 'string') { @@ -33,9 +51,13 @@ export class AllExceptionsFilter implements ExceptionFilter { statusCode: statusCode }; } else { + // 에러 코드화를 진행할 부분 + const objError = getError as HttpExceptionErrorResponseDto; error = { - ...(getError as HttpExceptionErrorResponseDto), - error: exception.name + code: objError.code, + message: objError.message, + error: exception.name, + statusCode: statusCode }; } } else { @@ -69,7 +91,7 @@ export class AllExceptionsFilter implements ExceptionFilter { // //console.log(error); const errorResponse: ErrorCommonResponse = { - statusCode, + statusCode: statusCode, timestamp: new Date(), path: request.url, method: request.method,