드디어... 말로만 들었던 interceptor에 대해 공부해보는 시간을 가졌다. 컨셉은 이미 알고있었다. 뭐 중간중간 데이터를 가공하는데 쓰는거겠지 라고 대충만 알고있었는데 이 대충만 아는것이 또 문제를 일으킬것같아 조금 자세히 알아보는 시간을 가졌다.
인터셉터는 중간에서 가로챈다는 뜻이다. 그 어원 그대로 인터셉터의 역할은 요청이 컨트롤러로 들어가기전 혹은 컨트롤러로 들어가서 return을 하고 그 리턴값이 사용자에게 전달되기전 등 프로세스 사이사이에서 값을 가공할수있게 해주는 용도로 사용된다.
사용법
common/interceptors/undefinedToNull.intecrptor.ts
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable, map } from 'rxjs';
@Injectable()
export class UndefinedToNull implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
return next
.handle()
.pipe(map((data) => (data === undefined ? null : data)));
}
}
return전에 짜는 로직은 컨트롤러 가기전 로직, return 선언후는 컨트롤러 실행 이후
map안 data는 컨트롤러에서 return 해준값이 담길것이고 그 데이터를 한번더 가공해서 뱉을수도 있다. (참고로 여기서 쓰인 map 함수는 js에서 사용하는 map이 아닌 rxjs에서 사용하는 map 이라는 함수가 따로있다.)
위와같이 interceptor를 지정해주고 controller에서 적용하는 방법은 아래와 같다.
@ApiTags('USERS')
@UseInterceptors(UndefinedToNull)
@Controller('users')
export class UserController {
@Inject()
private userService: UserService;
@ApiOperation({ summary: '회원가입' })
@Post()
postUsers(@Body() data: JoinRequestDto) {
this.userService.postUsers(data);
}
}
위와같은 방식으로 컨트롤러 내부의 모든 기능마다 붙여줄수도 있지만 개별적으로 적용시켜 줄수도있다.
AOP
AOP(aspect oriented programming)은 모듈화의 강화를 목표로 만들어 졌다. 조금 더 자세하게 풀어보자면 하나의 컨트롤러가 요청을 받아 응답을 내기까지 일련의 과정을 거친다. 이때 요청을 받아 응답을 내는 과정에 있어 공통되는 process들이 있다면 그 process를 하나로 묶어 처리한다. 즉 이 묶어서 처리한다는 말은 모듈화를 한다는 말로, 처음으로 돌아가서 aop의 목표성을 다시 살펴 보자면 일련의 process를 모듈화 하여 진행하지만 여러 컨트롤러가 공통되는 process(모듈)을 진행하여 요청을 처리한다면, 그 공통되는 모듈을 묶어 처리한다. 이때 사용될수있는 컨셉이 interceptor이다.
근데 이거 글을 읽어보면 좀 의아 해질수도 있을것같다. 실제로 내가 의아했던 부분이었는데, 아니 뭐하러 따로 interceptor를 설정해 그냥 middleware로 하던대로 하면 되잖아? 그냥 A - B - C에서 A 와 B사이에 어떤 기능이 필요하면 그 사이에 middleware를 넣으면 되는거잖아? 라고 생각할수 있을것같다. 근데 interceptor를 사용하는 장점은 하나의 interceptor에서는 컨트롤러에 요청이 전달되기전 그리고 전달된후 return을 뱉은후 라고 하였다. 만약 middleware로 전 과 후 모두 처리하고싶다면 2개의 middleware를 만들어야 할것이다. 근데 interceptor는 하나의 interceptor안에 전후 모두 가능하고 또 개인적으로 쓰다보니 뭔가 로직의 흐름이 더 자연스럽게 느껴졌던것 같다.