강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

이승훈님의 프로필 이미지
이승훈

작성한 질문수

Slack 클론 코딩[백엔드 with NestJS + TypeORM]

인터셉터를 통한 응답의 관련된 질문입니다.

해결된 질문

작성

·

298

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를 건드려서 해결하신적이 있으신가요?

 

답변 1

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

post랑 put일때 컨트롤러랑 인터셉터 handle 부분은 실행되는 거죠? isResponseHandled가 true인게 문제네요. 그게 왜 true가 되는지 파악해봐야할것같습니다.

이승훈님의 프로필 이미지
이승훈
질문자

네 컨트롤러, 인터셉터 핸들은 실행되는데 리턴 이후가 문제입니다. 혹시 조현영님께서 인터셉터 작성하실 때 이런문제를 겪으신적이 있으신가요?

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

저는 없습니다

이승훈님의 프로필 이미지
이승훈
질문자

혹시 @nestjs/core버전 몇 쓰시나요?

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

최신버전 씁니다.

이승훈님의 프로필 이미지
이승훈
질문자

제가 몇가지 테스트 해본 결과 컨트롤러 측에서 매개변수로 @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객체를 사용할 방법이 없을까요?

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

인터셉터에서 res 작업을 다 하시면 됩니다. 컨트롤러에서 return으로 인터셉터로 데이터를 보낼 수 있으니까요.

이승훈님의 프로필 이미지
이승훈
질문자

쿠키를 보내는 컨트롤러와 쿠키를 제거하는 컨트롤러, 쿠키와 무관한 컨트롤러 들이 각각 있어서 그런데 그럼 각각 인터셉터들을 만들어 주면 될까요?

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

네네 따로 만드시면 좋습니다. @Req, @Res는 웬만하면 안 쓰시는 게 좋아요.

이승훈님의 프로필 이미지
이승훈
질문자

감사합니다!

이승훈님의 프로필 이미지
이승훈

작성한 질문수

질문하기