[TIL] Service Layer jest Test code, FE 연동시 403 에러

김시원·2023년 5월 9일
0

TIL

목록 보기
18/50
post-custom-banner

Issues encountered

  1. UPDATE worldcup: service layer test code error
TypeError: Cannot read properties of undefined (reading 'map')

const worldcup_choices = worldcup.Worldcup_choices.map((choice) => ({                                                      
	choice_name: choice.choice_name,
	choice_url: choice.choice_url,
}));
  1. 프론트엔드에서 로그인을 하고 유저 인증이 필요한 요청에 대해 403 에러를 뱉는다. 근데 postman에서는 문제 없이 작동됌.

What I tried

Issue #1

1) 먼저, map()함수를 인식하지 못하기 때문에 getOne()를 mock function으로 정의해주는 과정에서 map을 추가하였다.

mockWorldcupRepository.getOne = jest
      .fn()
      .mockImplementation((worldcup_id) => {
        const Worldcup_choices = choices.map((choice) => ({
          choice_name: choice.choice_name,
          choice_url: choice.choice_url,
        }));

        return { ...getOneFuncReturnValue, Worldcup_choices };
      });

2) 리팩토링을 시도해보았다. 위의 에러에서 map()이 실행되지 않았던 이유는 원래 정의된 함수의 worldcup.Worldcup_choices.map()에서 worldcup.Worldcup_choices가 undefined가 나오기 때문에 map()을 돌릴 수 없었던 것이었다. 따라서, getOneFuncReturnValue라는 객체의 이름을 worldcup으로 변경해주고, mock getOne()에서 Worldcup_choices key만 전달을 해주었다.

mockWorldcupRepository.getOne = jest.fn(() => ({
      ...worldcup,
      Worldcup_choices: choices,
    }));

3) 실제 map()을 돌면서 map() 안에서 findAllWorldcupChoices()라는 함수를 실행하는 getAllWorldcups method에서는 실제 map()을 mockImplementation()을 통해 정의해주었다.

mockWorldcupChoicesRepository.findAllWorldcupChoices = jest.fn()
      .mockImplementation(() => choices.map((choice) => choice));

Issue #2


  1. token이 undefined value를 지정받는다는 오류였다. 즉, 클라이언트에게 전달받은 쿠키에서 refreshToken 해더에 해당되는 토큰이 전달되지 않고 undefined 상태였다는 의미이다.
  2. 일단, 우리는 refresh token을 cookie에 넣어서 전달하였는데, 현업에서는 body에 넣어서 전달을 한다고 해서 그 부분을 수정하였다.
  3. 나중에 알고보니 refresh token 자체는 쿠키로 전달이 잘 되었는데, 클라이언트쪽에서 넘겨진 쿠키들을 header에서 처리를 해주어서 발생하는 오류였다. API 명세서를 더 구체적으로 써서 이런 협업 과정에서의 실수를 줄여야겠다.
  • 기존에 joi validation을 validateAsync()로 처리했었는데, 클라이언트에서 body data를 받아올 때 값을 잘 반환하지 않았다. 이 역시도 postman에서는 잘 처리되었던 부분이었다. 해당 함수를 validate()로 바꿔주니 문제가 해결되었다.
const { value, error } = createCommentSchema.validate(req.body);
      // validation 에러 처리, 에러 없는 경우: error === undefined
      if (error) {
        error.errorCode = 412;
        next(error, req, res, error.message);
      }

동기 함수인 validate()은 validation을 통과한 body 데이터를 첫번째 인자로 반환하고, 실패시 두번째 인자로 error를 반환한다. value는 객체로 구성되어 있어 각 값을 key-value로 저장한다.

What I newly learned

Issue #1

여러개의 함수가 정의되어 있는 service layer의 비지니스 로직을 테스트하기가 쉽지 않았다. map()과 같은 내장 함수 처리에서 시간이 많이 걸렸고, 처음엔 mockRespository.getOne()과 같은 사용자 정의 함수를 mock function으로 어떻게 정의하는지도 헷갈렸었는데 지금은 확실히 이해하고 테스트 코드를 짤 수 있게 되었다.

Issue #2

내가 만들었던 create API에서 body 데이터에 대한 여러 validation을 주었는데 (choice_url은 무조건 uri 형태이어야함, choices 배열에는 2개 이상의 객체가 들어가야함, choices 배열 내의 객체는 짝수개여야함 등등) 이 부분을 프론트 분들께 전달해주지 않아, 계속 에러가 떠서 헤매셨다고 한다. API 문서를 꼼꼼하게 작성해야하고 클라이언트와 직접 소통하는 부분은 프론트 분들께 잘 전달해야한다는 것을 느꼈다.

에러 로깅이 매우매우매우매우 중요하다는 걸 다시금 느꼈고, 팀원들이 같이 확인할 수 있는 툴이 있으면 좋겠다고 생각했다. 배포된 서버에서 난 에러들을 다른 팀원의 터미널 pm2에서 로그 확인을 하였는데 불편한 부분이 있었다. 이 부분이 문서화가 깔끔하게 되면 좋을 것 같고, Sentry 같은 툴의 필요성을 느꼈다.

const Sentry = require("@sentry/node");

module.exports = (error, req, res, defaultMessage) => {
  console.error(
    `에러로그 ${error.errorCode} ${req.method} ${req.originalUrl} : ${error.message}`
  );
  return Sentry.Handlers.errorHandler({
    shouldHandleError(error) {
      if (
        error.errorCode === 403 ||
        error.errorCode === 404 ||
        error.errorCode === 500
      ) {
        return true;
      }
      return false;
    }, // 1. Sentry에 error event를 저장한다.
  })(error, req, res, () => {
    if (!error.errorCode) {
      return res.status(500).json({ errorMessage: defaultMessage });
    } else {
      return res.status(error.errorCode).json({ errorMessage: error.message });
    } // 2. error message를 body에 전달해준다.
  });
};

오늘 깃헙 서버 터짐


우리 PR merge 해야되는데...

What to learn next

  • CI/CD의 필요성을 느끼는 중이다.
  • Worldcup Integration Test
  • AWS 배포 강의 듣기
post-custom-banner

0개의 댓글