728x90
- Interceptor란?
NestJS에서 유일하게 요청이 들어올 때 그리고 응답이 나갈 때 모두 로직을 실행할 수 있는 미들웨어다.
- 함수 실행 전/후에 추가 로직을 바인딩한다.
- 함수에서 반환된 값을 변환한다.
- 함수에서 던진 에러를 변환한다
- 함수의 기본 기능에서 추가 기능을 연장한다.
- 조건에 따라 함수의 기능을 override한다.
Interceptor Response 핸들링은 기본적으로 RxJS를 사용한다.
- Interceptor 구현방법
@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`)));
}
}
로직 실행 시간 체크하는 예시
- 예시
- 시간 체크
// response-time.interceptor.ts
@Injectable()
export class ResponseTimeInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
const req = context.switchToHttp().getRequest();
const reqTime = Date.now();
return next.handle().pipe(
delay(1000),
tap(() => {
const resPTime = Date.now();
const diff = resPTime - reqTime;
if (diff > 1000) {
console.log(`[!!!TIMEOUT!!! ${req.method} ${req.path}] ${diff}ms`);
throw new InternalServerErrorException(
'시간이 너무 오래 걸렸습니다!',
);
} else {
console.log(`[${req.method} ${req.path}] ${diff}ms`);
}
}),
);
}
}
1초 이상 걸리면 프론트에 에러 던져주는 코드 (중간에 delay()를 넣어서 일부러 에러 던졌음)
// app.module.ts
providers: [
{ provide: APP_INTERCEPTOR,
useClass: ResponseTimeInterceptor },
],
전체 적용
- 캐시
// cache.interceptor.ts
@Injectable()
export class CacheInterceptor implements NestInterceptor {
private cache = new Map<string, any>();
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
const request = context.switchToHttp().getRequest();
// GET /movie
const key = `${request.method}-${request.path}`;
if (this.cache.has(key)) {
return of(this.cache.get(key));
}
return next.handle().pipe(tap((response) => this.cache.set(key, response)));
}
}
같은 method & path로 요청오면 캐시에 저장해뒀던거 그대로 반환
실제로는 redis 연결해서 쓰고, pagination같이 요청에 따라 결과값이 달라져야한다면 문제 발생 등의 이유로 예시 코드일뿐임
// movie.controller.ts
@Get()
@Public()
@UseInterceptors(CacheInterceptor)
getMoives(
@Query() dto: GetMoviesDto, //
) {
return this.movieService.findAll(dto);
}
특정 라우터에 적용
- Transaction
// transaction.interceptor.ts
@Injectable()
export class TransactionInterceptor implements NestInterceptor {
constructor(private readonly dataSource: DataSource) {}
async intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Promise<Observable<any>> {
const req = context.switchToHttp().getRequest();
const qr = this.dataSource.createQueryRunner();
await qr.connect();
await qr.startTransaction();
req.queryRunner = qr;
return next.handle().pipe(
catchError(async (e) => {
await qr.rollbackTransaction();
await qr.release();
throw e;
}),
tap(async () => {
await qr.commitTransaction();
await qr.release();
}),
);
}
}
qr 만들어서 req에 넣어주고, qr 시작부분 실행된 상태로 로직 돌고 나올 때 뒷 부분 실행됨.
// movie.service.ts
return await qr.manager.findOne(Movie, {
where: {
id: movieId,
},
relations: ['director', 'genres'],
});
나가는 인터셉터에서 commit과 release가 이루어지기 때문에 기존 코드의 끝이 qr.manager로 바뀜
https://fastcampus.co.kr/classroom/239666
커리어 성장을 위한 최고의 실무교육 아카데미 | 패스트캠퍼스
성인 교육 서비스 기업, 패스트캠퍼스는 개인과 조직의 실질적인 '업(業)'의 성장을 돕고자 모든 종류의 교육 콘텐츠 서비스를 제공하는 대한민국 No. 1 교육 서비스 회사입니다.
fastcampus.co.kr
728x90
'코딩 > Nest.js' 카테고리의 다른 글
Custom Decorator (1) | 2024.10.24 |
---|---|
Exception Filter (1) | 2024.10.23 |
Pagination (0) | 2024.10.23 |
RBAC (Role Based Access Control) (0) | 2024.10.23 |
Guard 실사용적 (0) | 2024.10.23 |