• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

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

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

 

답변 1

답변을 작성해보세요.

0

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

이승훈님의 프로필

이승훈

질문자

2022.07.09

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

저는 없습니다

이승훈님의 프로필

이승훈

질문자

2022.07.09

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

최신버전 씁니다.

이승훈님의 프로필

이승훈

질문자

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객체를 사용할 방법이 없을까요?

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

이승훈님의 프로필

이승훈

질문자

2022.07.09

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

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

이승훈님의 프로필

이승훈

질문자

2022.07.09

감사합니다!