-
카테고리
-
세부 분야
백엔드
-
해결 여부
해결됨
인터셉터를 통한 응답의 관련된 질문입니다.
22.07.08 23:03 작성 조회수 172
0
안녕하세요 조현영님. 인터셉터 관련해서 궁금한게 있어 질문드립니다. 아래는 제가 만들어 놓은 인터셉터입니다.
intercept(context: ArgumentsHost, next: CallHandler<any>): Observable<any> {
// controller 도달 전
const req = context.switchToHttp().getRequest();
const res = context.switchToHttp().getResponse();
console.log(`Receive request from ${req.method} ${req.originalUrl}`);
const now = Date.now();
return next.handle().pipe(
map((data: JSON<null>) => {
// controller 도달 후
console.log(
`Send response from ${req.method} ${
req.originalUrl
} :: time taken : ${Date.now() - now}ms`,
);
return res.status(data.statusCode).setHeader("X-Powered-By", "").json({ success: true, ...data});
}),
);
}
이전까지는 이런식으로 인터셉터를 구성해서 포스트맨으로 사용할 땐 문제없이 요청과 응답이 오고 갈수 있었습니다. 그런데 시험삼아서 브라우저에 서버 url을 입력후 get메서드를 사용하는 api를 사용해봤는데 응답은 잘 갔었지만 서버 콘솔에 cannot set headers 오류가 났습니다. 이 오류는 꽤나 익숙해서 응답이 두번 보내져서 그런가 싶어 인터셉터 응답을 아래처럼 바꿔보았습니다.
intercept(context: ArgumentsHost, next: CallHandler<any>): Observable<any> {
// controller 도달 전
const req = context.switchToHttp().getRequest();
const res = context.switchToHttp().getResponse();
console.log(`Receive request from ${req.method} ${req.originalUrl}`);
const now = Date.now();
return next.handle().pipe(
map((data: JSON<null>) => {
// controller 도달 후
console.log(
`Send response from ${req.method} ${
req.originalUrl
} :: time taken : ${Date.now() - now}ms`,
);
res.status(data.statusCode).setHeader("X-Powered-By", "");
return { success: true, ...data };
}),
);
}
이상한게 get, delete 메서드를 사용하는 api는 return문을 거쳤을 때 응답이 잘 도달되었지만 post,patch등은 응답이 가고 계속 로딩중입니다.
계속 기다려도 이러한 상태여서 디버깅을 통해 문제를 해결하고자 return문에서 부터 계속 디버깅을 시도했습니다. 그리고 문제였던 아래 코드를 발견했습니다.
return async (result, res) => {
result = await this.responseController.transformToResult(result);
!isResponseHandled &&
(await this.responseController.apply(result, res, httpStatusCode));
};
위의 코드는 node_modules/@nestjs/core/router/router-execution-context.js 라는 파일의 174 ~ 177줄의 코드입니다. 아마 result 변수가 인터셉터에서 리턴된 값으로 사용되는 변수 같은데 그 아래 있는 코드 실행 이후 계속 응답이 닿지 않는 모습이었습니다. 그래서 저는 아래처럼 다시 수정해봤습니다.
return async (result, res) => {
result = await this.responseController.transformToResult(result);
await this.responseController.apply(result, res, httpStatusCode);
};
이후에는 post, patch메서드의 응답이 잘 닿았지만 과연 이게 올바른 해결 방법인지는 잘 모르겠습니다. 만약 이 프로젝트를 git에서 pull, clone등을 할 때 node_modules는 .gitignore에 등록해놓아서 위 처럼 변경 사항을 불러올 수가 없어서 git에서 pull, clone하려면 npm i로 모듈들을 받은 후 계속 저런식으로 수정을 해야 되서 이 방법은 좀 아닌거 같지만 일단 임시방편으로 해놓은 상태입니다. 조현영님께서는 이렇게 어떤 문제가 해결이 안될때 node_modules를 건드려서 해결하신적이 있으신가요?
답변을 작성해보세요.
0
조현영
지식공유자2022.07.09
post랑 put일때 컨트롤러랑 인터셉터 handle 부분은 실행되는 거죠? isResponseHandled가 true인게 문제네요. 그게 왜 true가 되는지 파악해봐야할것같습니다.
이승훈
질문자2022.07.09
제가 몇가지 테스트 해본 결과 컨트롤러 측에서 매개변수로 @Res() res: Response를 사용하게 될 때 isResponseHandled가 true가 된다는 사실을 알아내었습니다. 그런데 res로 send나 json등을 컨트롤러에서 직접 사용하지는 않고 아래 처럼 쿠키를 생성하는 용도로 사용하고 있습니다.
async login(
@Body() loginUserDto: LoginUserDto,
@Res() res: Response,
): Promise<JSON<string>> {
const jwtToken = await this.authService.login(loginUserDto);
res.cookie("JWT_COOKIE", jwtToken, CookieOption);
return {
statusCode: 201,
message: "로그인을 완료하였습니다. 쿠키를 확인하세요.",
};
}
저는 로그인 api를 만들 때 세션 대신 jwt를 주로 사용합니다. 그리고 만들어진 토큰을 쿠키로 보내는 방법을 주로 사용합니다. 그런데 res를 사용하지 않는다면 쿠키를 보낼 수 없어서 결국 로그인이 무용지물이 되어버리게됩니다. 인터셉터를 사용함과 동시에 컨트롤러에서 res객체를 사용할 방법이 없을까요?
답변 1