세상에는 나랑 같은 고민을 가진 사람이 있다 (무조건)

tennfin1·2024년 2월 12일
0

backend

목록 보기
13/18

1. 발단

서버 측에서 로깅을 하는 데에 있어, response로 무엇을 보냈는지 로깅을 하기 위해서는, 프론트엔드로 데이터를 보내기 직전의 함수를 overiding 하여 로깅을 껴넣는게 보편적입니다.

node.js에서 여러가지 방법이 있겠지만, 저는 가장 보편적인 방법인 res.send를 활용하여 수행하고 있었는데
overrideing 하는 과정에서 logging이 2번되는 문제가 발생했습니DA.

export const responseLogger = function () {
  return function (req: Request, res: Response, next: NextFunction) {
    const send = res.send;
    res.send = function (body): Response<unknown, Record<string, unknown>> {
      logger.http(JSON.stringify(body));
      return send.call(this, body);
    };
    next();
  };
};

이걸 해결하려고 단순히 플래그 써서 짜치게(?) 해결할 수도 있습니다.
이렇게 말이죠

export const responseLogger = function () {
  return function (req: Request, res: Response, next: NextFunction) {
    const send = res.send;
    let isSent = false;
    res.send = function (body): Response<unknown, Record<string, unknown>> {
      if (!isSent) {
        logger.http(JSON.stringify(body));
        isSent = true;
      }
      return send.call(this, body);
    };
    next();
  };
};

근데 로깅이 두번 찍힐 때를 보면 logging이 모양이 다르게 찍히는 걸 볼 수 있습니다.

뭔가 호출시점이 다르니 변수가 다른 모양으로 찍히겠죠?
근데 생각해봐도 잘 모르겠으니 구글링 ㄱㄱ

비슷한 경험이 찾아보니 나옵니다
이 사람도 나랑 똑같이 해결하려 함 ㅋㅋ

보시다시피 핸들러는 실제로 첫 번째(유일한) 인수로 객체를 사용하여 코드에 의해 한 번 호출됩니다. 그러나 JSON으로 직렬화된 개체를 사용하여 다시 한 번 호출됩니다. 이것이 res.send내부적으로 작동하는 방식입니다. 자세한 내용은 아래를 참조하세요. 실제 응답 객체에 가로채기 함수를 넣었기 때문에 JSON 인수로 자신을 호출하고 그 동안 함수를 호출한다는 사실조차 알지 못하는 것 같습니다. (해당 글의 번역본)

2. 해결

쉽게 말하면 res.send의 패러미터로 JSON object가 도착하면, 이를 그대로 출력하기 한번, stringify해주는 방법으로 한번, 총 두번 호출해주는 구조기 때문에 2번 실행된다고 합니다.

이를 해결하려면 res.send()를 호출할 때 인수를 json.stringify()를 넣어주는 방법(링크에서 언급한 방법) 이 있는데, 결국 함수를 실행하는 부분에서 값을 전처리해야한다는 점이 상당히 별로입니다.

그렇다면, res.send()가 아닌, res.json()을 이용하면 오류를 해결할 수 있지 않을까?라는 생각이 들었고, 실행해본 결과

한번만 출력됩니다. 아주 좋네요.

3. 문제점

근데 이것도 근본적인 문제가 있습니다.
res.json()과, res.send()의 사용구분을 강제해야한다는 점이며, res.send()에 대해서 또다른 override를 수행해줘야한다는 점입니다.

이를 개선하기 위해서는 express의 server.js의 구조를 개편하는 방법이 있는데,
간단하게 말하면 try-catch 문법의 finally와 같은 미들웨어를 사용하는 구조를 만드는 방법입니다.(무조건 마지막에 실행해주는 미들웨어)
res함수에 로깅을 오버라이딩하는 구조가 아니라, finally middleware에 로깅, res함수 등을 편하게 배치하면 구조를 단순화하면서 위의 문제점들을 해결할 수 있습니다.

도식화된 글과 함께 다음시간에 찾아뵙도록 하겠습니다.

profile
심도깊은개발

0개의 댓글

관련 채용 정보