
1. 🎭 Interceptor란 무엇인가요?
인터셉터(Interceptor)는 AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍) 개념을 구현한 NestJS의 컴포넌트입니다. 요청이 컨트롤러 핸들러로 가기 직전과, 컨트롤러가 응답을 반환한 후 양방향으로 실행 흐름을 가로채서(Intercept) 추가적인 로직을 실행할 수 있습니다.
이는 컨트롤러 메서드 실행 전후에 공통적인 부가 기능(Cross-cutting Concerns)을 삽입하여, 컨트롤러의 핵심 비즈니스 로직을 깨끗하게 유지하는 데 도움을 줍니다.
Interceptor의 주요 역할
- 응답 변형 (Response Mapping): 컨트롤러가 반환하는 결과에 공통적인 형식(예: statusCode, data, message)을 래핑(Wrapping)합니다.
- 로깅 (Logging): 요청이 들어온 시점과 응답이 나가는 시점의 시간을 측정하여, API의 처리 시간을 정확히 기록합니다.
- 캐싱 (Caching): 요청이 컨트롤러에 도달하기 전에 캐시를 확인하고, 유효한 캐시가 있다면 컨트롤러 실행을 건너뛰고 캐시된 응답을 반환합니다.
- 예외 변환: 데이터베이스 관련 예외 등 특정 예외를 HTTP 친화적인 다른 예외로 변환합니다.
2. 🏗️ Interceptor 구현 방법
Interceptor는 NestInterceptor 인터페이스를 구현하며, RxJS의 Observable 스트림을 사용하여 비동기적으로 작동합니다.
2.1. NestInterceptor 인터페이스
핵심 메서드는 intercept(context, next)입니다.
- context: Guard와 마찬가지로 현재 실행 컨텍스트(ExecutionContext) 정보를 담고 있습니다.
- next: 컨트롤러 핸들러의 실행을 나타내는 RxJS의 CallHandler 인터페이스입니다. next.handle()을 호출해야 비로소 컨트롤러 메서드가 실행됩니다.
2.2. 응답 시간 로깅 Interceptor 예시
이 예시는 요청과 응답 사이의 시간을 측정하여 기록합니다.
// logging.interceptor.ts
import {
Injectable, NestInterceptor, ExecutionContext, CallHandler
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators'; // RxJS의 연산자 사용
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const now = Date.now();
const req = context.switchToHttp().getRequest();
console.log(`[REQUEST START] ${req.url} @ ${now}`);
// 💡 next.handle()은 컨트롤러 핸들러 실행을 나타내는 Observable을 반환합니다.
return next
.handle()
.pipe(
// 💡 응답이 나가는 시점에 실행될 로직을 tap 연산자를 사용하여 정의
tap(() => {
const responseTime = Date.now() - now;
console.log(`[RESPONSE END] ${req.url} took ${responseTime}ms`);
}),
);
}
}
2.3. 응답 래핑(Wrapping) Interceptor 예시
컨트롤러의 반환 값을 { data: result } 형태로 변형하여 일관성을 부여합니다.
// transform.interceptor.ts
import { Injectable, NestInterceptor, CallHandler } from '@nestjs/common';
import { map } from 'rxjs/operators';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
// 💡 map 연산자를 사용하여 컨트롤러 반환 값을 조작
return next.handle().pipe(
map(data => ({
statusCode: context.switchToHttp().getResponse().statusCode,
timestamp: new Date().toISOString(),
data: data, // 컨트롤러의 실제 반환 값
}))
);
}
}
3. 📌 Interceptor 적용 및 실행 순서
Interceptor는 Guard, Pipe와 마찬가지로 @UseInterceptors() 데코레이터를 사용하여 컨트롤러, 메서드, 또는 글로벌 레벨에 적용할 수 있습니다.
3.1. 실행 순서 (요청 및 응답 흐름)
Interceptor는 실행 흐름을 가로채기 때문에 요청 처리 과정 중 두 번 실행됩니다.
- Middleware
- Guard
- Pipe
- Interceptor (요청 전): next.handle() 호출 직전
- Controller Handler (실행)
- Interceptor (응답 후): .pipe()의 tap이나 map 연산자가 실행
- Client (응답)
3.2. DI와 확장성
Guard와 Pipe처럼 Interceptor 역시 DI 컨테이너에 의해 관리되므로, 로깅 서비스, 캐싱 서비스 등 다른 의존성 서비스를 주입받아 사용할 수 있습니다. 이는 복잡한 횡단 관심사(Cross-cutting Concerns)를 시스템에 깨끗하게 통합하는 데 매우 강력한 방법입니다.
'Nest.js를 배워보자 > 6. NestJS Middleware , Guard , Intercept' 카테고리의 다른 글
| Custom Pipe 만들기 (0) | 2025.12.02 |
|---|---|
| Guard (가드): 인증/인가 처리 (0) | 2025.12.02 |
| Middleware (미들웨어): 요청 가공 및 전처리 (0) | 2025.12.02 |