Nest.js 로 개발하며, Guards 와 Pipes 에 대한 지식이 부족한 것 같아 각각의 component 들의 사용 목적과 실행 순서를 파악하기 위해 공부하며 작성한 글입니다. 오류가 있다면 알려주세요.
미리보는 결론
가드가 우선적으로 실행되며, 사용자 인증 및 권한을 확인하는데 사용됩니다.
파이프는 가드가 실행된 이후 실행되며, 값 변환 및 검증에 사용됩니다.전체 동작 흐름
요청 → 미들웨어 → 가드 → 인터셉터 → 파이프 → 인터셉터 → 예외 필터 → 응답
들어온 요청을 가장 처음 만나게 되는 것이 바로 미들웨어이다.
미들웨어는
등의 역할을 수행할 수 있다.
Nest.js 공식문서에서의
logger.middleware.ts
와 같이 로그 역할도 수행할 수 있는 것 같습니다.import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; @Injectable() export class LoggerMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { console.log('Request...'); next(); } }
미들웨어에서 요청-응답 사이클의 종료가 수행되지 않으면, 다음 component 로 제어권이 넘어간다.
app.use()
를 사용해 설정된 미들웨어를 의미한다.```tsx
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}
```
미들웨어를 지나온 요청은 가드를 만나 해당 요청이 Route Handler 에 처리될 지 말지를 결정된다.
주로, 사용자 인증 및 권환 확인에 사용된다.
전역적으로 설정된 가드가 먼저 실행되고,
app.useGlobalGuards(new GlobalGuard())
그 다음으로 컨트롤러에 설정된 가드,
마지막으로 Route 에 설정된 가드가 실행된다.
@UseGuards(ControllerGuard)
@Controller('cats')
export class CatsController {
@UseGuards(RouteGuard)
@Get()
getCats(): Cats[] {
return this.catsService.getCats();
}
}
인터셉터는 그 다음으로 수행되어 아래와 같은 역할을 할 수 있다.
요청시에는 가드와 비슷하게 전역 → 컨트롤러 → 라우터 순으로 동작하지만,
응답에서는 요청과 반대로 라우터 → 컨트롤러 → 전역 순으로 동작한다.
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@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`)),
);
}
}
Before...
After... 1ms
위와 같은 인터셉터를 설정하면, 아래와 같은 결과를 얻게 된다.
위 인터셉터로 부터 제어권을 받게되면 파이프에서 아래와 같은 역할을 수행한다.
가드와 인터셉터(요청) 과 마찬가지로 전역 → 컨트롤러 → 라우터 순으로 동작한다.
Nest.js 에서 어플리케이션 전 과정에서 처리되지 않은 예외를 처리하는 exception layer 가 내장되어 있다.
예외 필터는 라우터 → 컨트롤러 → 전역 순으로 동작한다.
@UseFilters(new HttpExceptionFilter()) // 2. 컨트롤러
export class CatsController {
@Post()
@UseFilters(new HttpExceptionFilter()) // 1. 라우터
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter()); // 3. 전역
await app.listen(3000);
}
bootstrap();
위 순서를 그림으로 그려보았다.