11/8 wooteco: 겪었던 문제들 : 내가 바보인 이유 전시회

그른손·2023년 11월 8일
0
post-thumbnail

1000 단위로 쉼표(,)넣기, 소수점 첫 째 자리까지 표시하기

  • 1000 단위로 쉼표를 넣고, 소수점 첫 째 자리까지 표시하려면 어떻게 해야 할까?
  • 일단 toLocaleString으로 숫자 표시를 ko-KR 기준으로 바꾸면 천 단위로 쉼표가 들어간다. fractionDigits를 설정해서 소수점 몇 번째 자리까지 표시할 지도 정할 수 있다.
  • 나는 그 대신 Intl 객체의 NumberFormat 생성자를 사용하기로 했다.
const prizeNumberFormatter = new Intl.NumberFormat('ko-KR');
const profitRateFormatter = new Intl.NumberFormat('ko-KR', {
  minimumFractionDigits: 1,
  maximumFractionDigits: 1,
});
  • 이렇게 사용한다.
const MessageFormat = {
 ...
  prize: prize => prizeNumberFormatter.format(prize),
  profitRate: profitRate => profitRateFormatter.format(profitRate),
}
  • 이렇게 하면 당첨금에는 천 단위 쉼표만, 수익률에는 천 단위 쉼표와 소수점 첫 째 자리 표시가 모두 적용된다.
  • 포매터 객체를 만들어두고 재사용하는 게, 사용할 때마다 로케일 정보를 매번 파싱하지 않아서 더 좋겠다고 생각했다!

수익률이 NaN이야

  • MessageFormat에 수익률을 포맷하는 함수를 만들고 수익률 반환 시에 적용했는데, 자꾸만 총 수익률은 NaN%입니다로 출력되는 문제가 있었다.
  • 수익률을 반환하는 LottoResultCalculator의 calculateProfitRate 내에서 모든 값들(총 상금, 수익률, 수익률.toFixed(1)...)을 출력해봤는데, 모두 다 정상적인 값이 나왔다.
  • 그러면 대체 어디서 오류가 나는 거지? 하다가 수익률을 출력해주는 View의 printProfitRate 메서드를 보니... 여기서도 MessageFormat.profitRateMessage를 사용하고 있었따.
  • 즉 이미 포맷이 된 수익률 문자열(1,000.0%)에 다시 포맷을 하니 NaN이 나오는 것이다...

1등 당첨금이 2천억인 혜자복권이 있다?!

  • 테스트를 작성하다가, 1등 당첨 시 수익률이 테스트 케이스에서 예상한 수익률과 엄청나게 차이가 나길래 뭔가 했는데...
  • 상수 파일 수정하다가 1등 당첨금 20억을 2000억으로 수정해버린 것이었다...
  • 화들짝 놀라서 바로 수정했다. 테스트야 고맙다!

수익률이 단위수에서부터 차이나는데?

  • 각 당첨 케이스에 대한 테스트를 작성할 때, 수익률이 예상치와 달라 테스트가 실패하는 문제가 있었다.
  • 예상 수익률 : 67666666.7
  • 실제 수익률 : 66668333.3
  • 예상 수익률은 1등 상금 20억 + 2등 상금 3000만원 / 로또값 3000원 * 100이다.
  • 예상 당첨금 : 2,030,000,000원
  • 그러나 실제 수익률을 역산해보니, 실제 당첨금은 대략 아래와 같았다.
  • 실제 당첨금 : 약 2,000,050,000원
  • 어라 뭔가 이상하다? 이러면 1등 상금 + 4등 상금인데?
  • => 테스트 케이스를 잘못 작성했다... 5개 일치 + 보너스 일치인 케이스가 아니라 4개 일치 + 보너스 일치인 케이스를 모킹해서 4등 당첨이 나오는데 2등 당첨을 예상하고 있었던 것

테스트 자꾸 실패해

  • node 명령어로 앱을 구동해서 손테스트를 했을 때는 문제 없이 작동하는데, 테스트는 계속 실패했다.
  • 지난 번에도 이랬던 적이 있는데... 싶어서 기억을 더듬어보다가 비동기 함수를 쓸 때 await을 안쓴 부분이 있는지를 체크해보았다.
class App {
  async play() {
    const controller = new LottoGameController();
    controller.executeLottoGame();
  }
}
  • 이번에도 범인은 나였다...
  • await controller.executeLottoGame()으로 수정하니 테스트가 잘 통과하는 걸 확인할 수 있었다.
  • 하지만 그러면 왜 노드 환경에서 직접 구동했을 때는 예상한대로 작동했던거지?
  • 비동기 코드를 테스트할 때는 그 비동기 로직이 완료될 때까지 기다리는 것이 중요하다. await을 사용하지 않으면, 테스트 러너(여기선 jest)는 비동기 작업이 완료되기를 기다리지 않고 테스트를 종료할 수 있고, 이것이 테스트 실패로 이어진다.
  • Node.js 환경에서 비동기 작업은 이벤트 루프에 의해 관리된다. 이벤트 루프는 프로세스가 종료되지 않는 한 계쏙해서 실행중인 비동기 작업들을 확인하고, 작업이 완료될 때까지 기다리는 역할을 한다.
  • 따라서 비동기 함수가 반환하는 Promise를 await을 사용하여 명시적으로 기다리지 않더라도 해당 작업은 백그라운드에서 계속 진행되고, 완료되면 이벤트 루프가 다음 프로미스 체인을 실행한다.
  • 하지만 테스트 러너에서는 기다리라고 말하지 않으면 기다리지 않기 때문에 실패한 것!
  • 이라고 추정된다...

교훈

  • 전체적으로 사소한 실수가 자꾸 보인다... 정신을 똑디 차려야 할텐데...
  • 그나마 다행인 건 문제를 빠르게 잡아낼 수 있었다는 것이다. 앞으로 바보짓 조금만 더 하면 바보짓도 데이터베이스가 쌓여서 발생할 때마다 즉시 해결이 가능하지 않을까?
profile
프론트엔드 개발자

0개의 댓글