-
Notifications
You must be signed in to change notification settings - Fork 1
nest js request life cycle
NestJS는 미들웨어, 가드, 인터셉터, 파이프, 예외 필터를 사용하여 요청과 응답을 다양한 방식으로 처리할 수 있습니다.
각각의 기능은 다른 목적을 가지고 있으며, 요청이 서버에 도달하고 응답을 반환할 때까지의 과정에서 특정한 역할을 수행합니다.
| 구성요소 | 역할 | 용도 | 실행순서 |
|---|---|---|---|
| Middleware | 요청을 가로채고 사전 처리 | 로깅,인증 | 요청이 들어오자마자 |
| Guard | 요청의 접근 허용 여부 판단 | 인증, 권한 부여 | 미들웨어 다음 |
| Interceptor | 요청/응답을 가로채고 변형 | 응답 변형,캐싱,로깅 | 가드 이후,컨트롤러 전후 |
| Pipe | 데이터 변형 및 유효성 검사 | 유효성 검사,데이터 변환 | 인터셉터 다음 |
| Exception Filter | 예외 대한 응답 처리 | 예외 대한 응답 처리 | 응답 인터셉터 후 |
용도
- 인증, 로깅, 요청의 변형 및 기타 사전 처리가 필요한 작업에 사용됩니다.
실행 순서
- 요청시 가장 먼저 호출됩니다.
특징
- 모듈단위로 등록할 수 있습니다.
예시
- 요청의 헤더나 본문을 확인
- 특정 경로에 대한 접근 권한을 사전 처리하는 경우.
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('Request...', req.method, req.path);
next();
}
}
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}Middleware는 요청을 처리한 뒤, 다음 미들웨어나 라우트 핸들러로 요청을 넘기기 위해 next() 메서드를 명시적으로 호출해야 합니다. 이는 Express/Fastify 기반 프레임워크의 미들웨어 체인 구조에서 비롯된 설계입니다.
반면, Guard, Pipe, Interceptor와 같은 NestJS 컴포넌트는 요청 흐름이 자동으로 제어되기 때문에 next()를 호출하지 않아도 다음 단계로 요청이 넘어갑니다. 이는 NestJS가 이러한 컴포넌트를 추상화하여 NestJS 내부 흐름 체인에서 자동으로 처리하기 때문입니다.
NestJS는 Express 또는 Fastify와 같은 미들웨어 기반 프레임워크 위에서 동작합니다. 이들 프레임워크는 요청-응답 처리의 핵심 설계로 미들웨어 체인 방식을 사용하며, NestJS도 이러한 본질을 유지했습니다.
미들웨어를 완전히 추상화하지 않은 이유는 다음과 같습니다:
Express/Fastify 생태계와의 호환성
미들웨어를 완전히 추상화했다면, 기존의 Express/Fastify 미들웨어(예: cors, helmet, cookie-parser)를 NestJS에서 사용하는 데 어려움이 생겼을 것입니다.
유연성 유지
미들웨어는 요청의 초기 단계에서 가장 자유롭게 요청과 응답 객체를 조작할 수 있습니다. 이를 제한하거나 추상화하면 미들웨어 본연의 유연성이 떨어질 수 있습니다.
결론적으로, NestJS는 미들웨어의 유연성을 유지하면서 Express/Fastify 생태계와의 호환성을 극대화하기 위해 미들웨어를 완전히 추상화하지 않았습니다. Guard, Pipe, Interceptor는 NestJS가 제공하는 고수준 추상화된 컴포넌트로, 미들웨어와는 역할과 목적이 다릅니다.
용도
- 요청이 처리될 수 있는지 결정하는 작업에 사용
- 인증이 필요하거나 특정 역할을 요구하는 경우
실행 순서
- 미들웨어 다음에 실행되며, 라우트 핸들러가 호출되기 전 요청을 필터링합니다.
특징
- boolean으로 반환하여 허용,거부를 결정할 수 있습니다**.**
- ExecutionContext(http외 프로토콜,데코레이터,현재 메서드 or 객체)를 알 수 있습니다.
예시
- JWT 토큰을 확인하여 요청을 허용하거나 특정 사용자의 역할에 따라 라우트에 접근을 허용하는 경우.
@Injectable()
export class RoleGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return validateRequest(request);}
}
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {
@UseGuards(RolesGuard)
findAdminCats() {
return 'This action returns cats for admin users only';
}
}Guard는 ExecutionContext라는 객체를 인자값으로 받게됩니다.
NestJS에서 ExecutionContext는 요청/응답 흐름과 관련된 실행 정보를 제공하는 인터페이스이며 현재 실행 중인 요청의 컨텍스트 정보를 제공합니다.
이를 통해 요청의 종류(HTTP, WebSocket 등)에 따라 요청 데이터를 처리하거나, 특정 조건에 따라 로직을 분기할 수 있습니다.
용도
- 응답에 데이터를 추가하거나, 로깅, 캐싱, 타이밍과 같은 기능을 추가할 때 사용됩니다.
실행 순서
- 가드 이후, 라우트 핸들러 바로 전후에 실행됩니다. 따라서 요청이 들어오기 전, 응답이 반환되기 전후에 작업을 수행할 수 있습니다.
특징
- AOP와 같이 활용할 수 있습니다.
예시
- 데이터 포맷 변경
- 응답에 특정 값을 추가하거나 실행 시간 측정.
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
@UseInterceptors(LoggingInterceptor)
export class CatsController {}NestJS의 Interceptor는 RxJS Observable 객체를 반환하며, 이를 통해 컨트롤러의 반환값을 가로채고 추가 작업을 수행할 수 있습니다.
Interceptor는 컨트롤러의 반환값을 Observable 데이터 스트림으로 감싸 여러 RxJS 연산자를 사용하여 데이터를 변환하거나 에러를 처리하고, 비동기 작업을 수행할 수 있게 합니다.
주로 사용되는 연산자는 다음과 같습니다.
map
- 컨트롤러의 반환값(Observable 데이터)을 변환하거나, 특정 구조로 포맷팅할 때 사용됩니다.
tap
- 데이터 흐름 중간에 로깅, 타이밍 측정, 이벤트 트리거 등 추가 작업을 수행할 때 사용됩니다**.**
- 반환 데이터를 변경하지는 않습니다.
catchError
- 컨트롤러에서 발생한 에러를 포착하고, 이를 변환하거나 새로운 에러 메시지를 반환합니다.
용도
- 데이터가 예상한 형식과 일치하는지 확인하고, 필요에 따라 변형 작업을 수행할 때 사용됩니다.
실행 순서
- 가드와 인터셉터 이후에 실행되며, 라우트 핸들러가 호출되기 직전에 파이프가 실행됩니다.
예시
- 요청 데이터의 유효성 검사
- 데이터 변환
- DTO(데이터 전송 객체) 적용.
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (typeof value !== 'string') throw new BadRequestException('Validation failed');
return value;
}
}용도
- 예외가 발생했을때에 대한 응답 처리를 할 때 사용됩니다.
실행 순서
- 응답에 대한 인터셉터 이후에 실행됩니다.
예시
- 500에러 발생시 response body 포맷팅
@Catch()
export class ExceptionHandler implements ExceptionFilter {
catch(error: any, host: ArgumentsHost) {
const { getResponse } = host.switchToHttp();
const response = getResponse();
const status = error instanceof HttpException ? error.getStatus() : 500;
const message = error.response?.message || error.message || 'Internal Server Error';
response.status(status).json({
status: false,
error: { code: status, message },
});
}
}레퍼런스