2023. 1. 13. 18:15γBackend/NestJS
μ΄μ κΈ
- 2022.12.22 - [Backend/NestJS] - NestJS Request Lifecycle (0) - κ°μ
- 2022.12.22 - [Backend/NestJS] - NestJS Request Lifecycle (1) - Middleware
- 2022.12.23 - [Backend/NestJS] - NestJS Request Lifecycle (2) - Guard
- 2022.12.26 - [Backend/NestJS] - NestJS Request Lifecycle (3) - Interceptor
- 2022.12.29 - [Backend/NestJS] - NestJS Request Lifecycle (4) - Pipe
μμΈ νν°
NestJSμμ μμΈ νν°λ λΉμ¦λμ€ λ‘μ§ λ 벨μμ μ²λ¦¬λμ§ μμ λͺ¨λ μμΈλ₯Ό μ¬μ©μ μΉνμ μΈ λ°©μμΌλ‘ μ²λ¦¬νλ μν μ νλ€. λ°λΌμ μμΈ νν°λ₯Ό μ΄μ©νλ©΄ νλ‘κ·Έλλ¨Έκ° μμμΉ λͺ»ν μλ¬κ° λ°μνλλΌλ μλ²κ° μ£½μ§ μκ³ , λν λ‘κΉ λ±μ μμ μ ν¨κ» μνν μ μλ€.
NestJS μ ν리μΌμ΄μ μμ± μ κΈ°λ³Έμ μΌλ‘ NestJS κΈ°λ³Έ μμΈ νν°κ° λ΄μ₯λλ€.
NestJSμ μμΈ
NestJS κΈ°λ³Έ μ 곡 μμΈ
NestJSμμλ κΈ°λ³Έμ μΌλ‘ HttpExceptionμ΄λΌλ κΈ°λ³Έ μμΈλ₯Ό μ 곡νλ©°, μ΄λ₯Ό μμν μλ§μ λ΄μ₯ μμΈκ° μ‘΄μ¬νλ€.
- BadRequestException
- UnauthorizedException
- NotFoundException
- ForbiddenException
- NotAcceptableException
- RequestTimeoutException
μμ μμ±ν μμλ€μ HttpExceptionμ μμν κΈ°λ³Έ λ΄μ₯ μμΈλ€μ μΌλΆλΆμ΄λ€. μ ν리μΌμ΄μ λ΄μμ μμ μμλ€μ λμ§λλ‘ λ‘μ§μ μμ±νλ©΄, NestJSμ κΈ°λ³Έ λ΄μ₯ μμΈ νν°κ° μ΄λ₯Ό μΊμΉνμ¬ μ¬μ©μμκ² κ° μμΈμ λν λ°νμ μννλ€.
Custom μμΈ μμ±
λ€μν μμΈ μν©μ λμ²νκΈ° μν΄ HttpExceptionμ μμ±μμ νλμ½λ©μ ν μλ μμ§λ§ μ¬μ¬μ©μ±, μ μ§λ³΄μμ±μ μν΄μ HttpExceptionμ μμνμ¬ λ°λ‘ κ°μ²΄λ‘ κ΄λ¦¬νλ κ²μ μΆμ²νλ€.
μμλ₯Ό νλ λ€μ΄λ³΄μ. μ¬μ©μμ νμκ°μ μμ²μμ μ΄λ―Έ μ‘΄μ¬νλ μ΄λ©μΌμ μ¬μ©ν κ²½μ° μ¬μ©μμκ² μμΈ μ²λ¦¬λ₯Ό ν΄μΌνλ€λ©΄ λ€μκ³Ό κ°μ΄ μ½λλ₯Ό μμ±ν μ μλ€.
import { BadRequestException } from '@nestjs/common';
export class DuplicateEmailSignupException extends BadRequestException {
constructor() {
super('The email you inputed is duplicated.');
}
}
μ΄ λ HttpExceptionμ μμνλ κ²μ΄ μλ κ·Έμ μμ ν΄λμ€μΈ BadRequestExceptionμ μμνλλ°, κ²°κ΅ μ΄ λν HttpExceptionμ νμ ν΄λμ€μ΄κΈ° λλ¬Έμ μ μλνλ€.
μ΄λ₯Ό μ¬μ©νκΈ° μν΄μλ DB λ΄ μ€λ³΅ μ΄λ©μΌμ΄ μ‘΄μ¬ν λ κ·Έμ μ ν΄λμ€μ μΈμ€ν΄μ€λ₯Ό λμ Έμ£ΌκΈ°λ§ νλ©΄ λλ€. μλλ κ·Έμ μμμ΄λ€.
async signup({ email, password }: SignupDto): Promise<boolean> {
const duplicatedUser = await this.userRepository.findOneBy({
email,
deletedAt: null,
});
// μμΈ μ²λ¦¬
if (duplicatedUser) {
throw new DuplicateEmailSignupException();
}
...
}
μμΈ νν° μμ±
NestJSμλ μ΄λ―Έ κΈ°λ³Έ λ΄μ₯ μμΈ νν°κ° μ‘΄μ¬νκΈ° λλ¬Έμ λ°λ‘ λ‘μ§ μμ±μ΄ νμμμ§λ§, κΈ°λ³Έ λ΄μ₯ νν°μ κΈ°λ₯μ νμ₯νκΈ° μν΄μλ Custom μμΈ νν°μ μμ±μ΄ νμνλ€.
Custom μμΈ νν° μμ±
NestJSμμ Custom μμΈ νν°λ₯Ό μμ±νκΈ° μν λ°©λ²μ ν¬κ² 2κ°μ§κ° μ‘΄μ¬νλ€.
- ExceptionFilter μΈν°νμ΄μ€λ₯Ό μμ(implements)νμ¬ catch λ©μλ μ§μ ꡬν
- BaseExceptionFilter ν΄λμ€λ₯Ό μμ(extends)νμ¬ κΈ°λ₯ μΆκ°
μΈν°νμ΄μ€ μμμ ν΅ν μμΈ νν° μμ±
ν΄λΉ λ°©μμ μμΈ νν°λ‘μ νμν μΈν°νμ΄μ€λ₯Ό κ°μ Έμ¨ λ€ λ΄λΆ ꡬνμ μλ‘νλ λ°©μμ΄λ€. μμλ μλμ κ°λ€.
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
// HttpException μλ¬λ₯Ό μ²λ¦¬
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
μ΄ λ @Catch λ°μ½λ μ΄ν°λ ν΄λΉ νν°κ° μ΄λ€ μ’ λ₯μ Exceptionμ μ²λ¦¬νλμ§μ λν λ©νλ°μ΄ν°λ₯Ό μ 곡νλ€. λ§μ½ λͺ¨λ μ’ λ₯μ μμΈλ₯Ό μ²λ¦¬νκ³ μΆλ€λ©΄, μΈμλ₯Ό μΆκ°νμ§ μμΌλ©΄ λλ€.
catch λ©μλμ exception μΈμλ ν΄λΉ νν°κ° μ²λ¦¬νλ μμΈμ΄λ€. ν΄λΉ μμμμλ HttpExceptionλ§μ μ²λ¦¬νκ³ μκΈ° λλ¬Έμ exceptionμ νμ μ HttpExceptionμ΄ λλ€.
catch λ©μλμ host μΈμλ ArgumentsHostμ΄λ€. μ΄λ₯Ό ν΅νλ©΄ νμ¬ μ€ν컨ν μ€νΈμμ λ€μν μ 보μ μ κ·Όμ΄ κ°λ₯νλ€. (ex. Expressμ Request, Response κ°μ²΄; WebSocketμμ Client, Data κ°μ²΄ λ±)
μ μμλ νν°κ° μ€μ λ λ°μ΄λ리 λ΄μμ λ°μν HttpExceptionμ λν΄, μ¬μ©μμκ² μν μ½λ, μμΈ λ°μμΌμ, μμΈ λ°μ κ²½λ‘λ₯Ό λ΄μ JSONμ λ°ννλ νν°λ‘ λ³Ό μ μλ€.
κΈ°λ³Έ μμΈ νν° ν΄λμ€λ₯Ό μμνμ¬ κΈ°λ₯μ μΆκ°νλ λ°©μ
ν΄λΉ λ°©μμ κΈ°μ‘΄μ λ΄μ₯ μμΈ νν°μ κΈ°λ₯μ μ μ§νμ± μΆκ°μ μΈ κΈ°λ₯μ λ§λΆμ΄κΈ° μν λ°©μμ΄λ€. 곡μλ¬Έμμ μμλ μλμ κ°λ€.
import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
super.catch(exception, host);
}
}
μ μμμμλ λͺ¨λ μμΈλ₯Ό μ²λ¦¬νλ νν°λ₯Ό μν΄ @Catch λ°μ½λ μ΄ν°μ μΈμλ₯Ό ν λΉνμ§ μμλ€. λν catch λ©μλ λ΄λΆμμ λΆλͺ¨ ν΄λμ€μ catch λ©μλλ₯Ό νΈμΆνλλ°, μ΄λ λ¨μν μ κ·Ό λ°©μμ 보μ΄κΈ° μν¨μ΄λ©° μ€μ ꡬνμμλ κ° μν©μ λ§λ λΉμ¦λμ€ λ‘μ§μ΄ μΆκ°λ μ μλ€.
곡μλ¬Έμμ λ°λ₯΄λ©΄ μ λ°©μμ 컨νΈλ‘€λ¬, λ©μλ λ²μμ νν°λ‘ μ¬μ©ν κ²½μ° μΈμ€ν΄μ€λ₯Ό ν λΉνλ κ²μ΄ μλ, ν΄λμ€ μ체λ₯Ό ν λΉνμ¬ NestJSμ μΈμ€ν΄μ€νλ₯Ό μμν΄μΌνλ€.(μ μ νν° μ€μ μ μν΄ λͺ¨λ λ°μμ useGlobalFilters λ©μλλ₯Ό μ¬μ©νλ€λ©΄, κ·Έ λμλ μΈμ€ν΄μ€λ₯Ό ν λΉν΄μΌνλ€.)
μμΈ νν° μ μ©
NestJSμμ Custom μμΈ νν°λ₯Ό μ μ©νλ λ°©λ²μλ ν¬κ² 4κ°μ§κ° μ‘΄μ¬νλ€.
- λͺ¨λ λ°μ μμΉνλ μ μ νν°
- λͺ¨λ λ΄μ μμΉνλ μ μ νν°
- 컨νΈλ‘€λ¬ λ 벨μ μλνλ νν°
- λ©μλ(νΈλ€λ¬) λ 벨μ μλνλ νν°
λͺ¨λ λ°μ μμΉνλ μ μ νν°
λͺ¨λ λ°μ μμΉνλ μ μ νν° μ€μ μ μν΄μλ NestJS μ ν리μΌμ΄μ μ useGlobalFilters λ©μλλ₯Ό μ¬μ©ν΄μΌνλ€. μ΄ λ λ©μλμ μΈμλ‘λ ν΄λμ€λ₯Ό ν΅ν΄ μμ±ν μΈμ€ν΄μ€κ° μμΉν΄μΌ νλ€. ν΄λΉ λ°©μμ μΈμ κΉμ§λ λͺ¨λ λ°μ νν°κ° μμΉνκΈ° λλ¬Έμ, λͺ¨λ λ΄μ μ‘΄μ¬νλ μμ‘΄μ±μ λν μ£Όμ μ λΆκ°λ₯νλ€. μλλ 곡μλ¬Έμμ μμμ΄λ€.
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// μ μ νν° μ€μ
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
λͺ¨λ λ΄μ μμΉνλ μ μ νν°
νν°λ₯Ό λͺ¨λ λ΄μ μμΉμν€λ©΄μλ, νν°λ₯Ό μ μμΌλ‘ μ¬μ©ν μ μλ€. λͺ¨λ λ΄ νλ‘λ°μ΄λμ νν°λ₯Ό μ£Όμ μ μ΄λ μ μ νν°κ° λλ€.
μ΄λ€ λͺ¨λμμ μ£Όμ νλλΌλ μ΄λ μ μμΌλ‘ μλνκΈ° λλ¬Έμ, νν°κ° μμ±λ λͺ¨λμμ λ°λ‘ μ£Όμ μ νλ©΄ λλ€.
ν΄λΉ λ°©μμ μ΄μ©ν κ²½μ° NestJS λͺ¨λ λ΄μ μμ‘΄μ±μ νν°μ μ£Όμ νμ¬ μ΄μ©ν μ μλ€λ μ₯μ μ΄ μλ€. μμλ μλμ κ°λ€.
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
컨νΈλ‘€λ¬ λ 벨μμ μλνλ νν°
νΉμ 컨νΈλ‘€λ¬μ λν΄μλ§ νν°λ₯Ό μ μ©ν μλ μλ€. μ΄ λλ NestJSμμ μ 곡νλ @UseFilters λ°μ½λ μ΄ν°λ₯Ό μ΄μ©ν΄μΌνλ€.
곡μ λ¬Έμμμλ ν΄λΉ λ°©μμ μ΄μ©ν κ²½μ°, μΈμ€ν΄μ€λ₯Ό μΈμλ‘ ν λΉν기보λ€λ ν΄λμ€ μ체λ₯Ό ν λΉνμ¬ μΈμ€ν΄μ€νμ λν μ± μμ NestJSμ μ΄κ΄νλ©΄ μ λ°μ μΈ λ©λͺ¨λ¦¬μ μ¬μ©μ μ€μΌ μ μλ€κ³ νλ€. λλλ‘μ΄λ©΄ ν΄λμ€ μ체λ₯Ό ν λΉνμ.
μμλ μλμ κ°λ€.
// NestJSμ λνμ μΈ μμμΈ Cats μμ
@UseFilters(HttpExceptionFilter)
// @UseFilters(new HttpExceptionFilter()) λ κ°λ₯νλ,
// λ©λͺ¨λ¦¬ μ μ½ μΈ‘λ©΄μμ μΆμ²νμ§ μλ λ°©μ
export class CatsController {}
λ©μλ(νΈλ€λ¬) λ 벨μμ μλνλ νν°
νΉμ 컨νΈλ‘€λ¬μ λ©μλμ λν΄μλ§ νν°λ₯Ό μ μ©ν μλ μλ€. ν΄λΉ λ°©μ λν μμ μΈκΈν @UseFilters λ°μ½λ μ΄ν°λ₯Ό μ΄μ©ν΄μΌνλ©°, 컨νΈλ‘€λ¬ μ체μ μ μ©νλ κ²κ³Ό μ¬μ©λ²μ΄ λ€λ₯΄μ§ μλ€.
μμλ μλμ κ°λ€.
@Post()
@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
Reference
https://docs.nestjs.com/exception-filters
'Backend > NestJS' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
NestJS Request Lifecycle (4) - Pipe (0) | 2022.12.29 |
---|---|
NestJS Request Lifecycle (3) - Interceptor (0) | 2022.12.26 |
NestJS Request Lifecycle (2) - Guard (0) | 2022.12.23 |
NestJS Request Lifecycle (1) - Middleware (0) | 2022.12.22 |
NestJS Request Lifecycle (0) - κ°μ (0) | 2022.12.22 |