[Express + Typeorm] 프로젝트 리팩토링과 테스트 코드를 작성하면서 이틀동안 겪은 삽질 정리

tkppp·2022년 9월 7일
0

시작

곧 다가오는 롤드컵 시즌에 앞서 개인적으로 혼자 사용하던 스포츠 결과 한눈에 보기 서비스에 롤드컵 기능을 추가하기 전에 프로젝트 리팩토링와 미뤄왔던 테스트 코드를 작성하기로 하였다.

프로젝트 리팩토링은 도메인 주도 개발론을 토대로 프로젝트 구조를 변경하고 서비스 레이어에 모든 비즈니스 로직이 노출되어 있는 것을 개별의 도메인 서비스로 분리하고 코드의 추상화 레벨을 높이는데 주력했다. (사실 제대로 한건지는 모르겠지만...)

도메인 객체에 비즈니스 로직을 작성하는 것이 아니라 이렇게 진행한 이유는 JPA와 다르게 Typeorm은 더티 체킹을 지원하지 않기 때문에 엔티티를 변경하더라도 save() 메소드를 서비스 레이어에서 호출하는것이 맘에 들지 않아 이를 감싸는 비즈니스 로직을 추상화한 도메인 서비스를 만드는 방식을 선택했다

문제 상황 1 - 테스트 DB

첫번째 문제는 테스트 DB를 어떻게 하느냐였다. 도메인 모델부터 테스트 코드를 작성하려 했는데 노드에서는 자바 진영의 H2 데이터베이스 와 같은 다양한 DB 문법을 지원하는 메모리 DB가 없었다. 그렇다고 운영 DB를 사용할 순 없으니 로컬 DB를 테스트 용으로 사용할까 했지만 배포환경에서도 테스트가 실행될텐데 로컬 DB를 사용하는 것은 불가하다고 생각했다

결국 고민과 구글링 끝에 도커를 사용해 테스트 DB 환경을 구축해 테스트 시에만 컨테이너를 올리고 테스트 종료 시 컨테이너를 내리는 방식으로 테스트 DB를 세팅했다. 자세한 내용은 여기를 참조

문제 상황 2 - 날짜와 시간이 안맞아!

이전에는 경기 날짜와 경기 시작 시간을 각각의 컬럼으로 나누었는데 이를 위해 컬럼 정의 시 컬럼 타입을 각각 DateTime을 사용했다. 문제는 이렇게 사용할 시 typeorm은 엔티티의 타입은 Date 인데 DB에서 가져올 시 string 타입의 값을 가져온다는 것이다. 타입 불일치를 해결하기 위해 moment 라이브러리로 Date 객체로 변환해서 사용하고 있었는데 이럴 바에 차라리 자동으로 Date 객체로 직렬화/역직렬화 되는 datetime 컬럼 타입으로 변경하기로 하였다

재앙의 시작

이렇게 바꾸고 간단하게 CRUD 테스트를 진행했다. DB에는 제대로 잘 저장된 것을 확인했다. 그리고 생각없이 엔티티 객체를 출력해 보았다.

const match = await Match.find()
console.log(match)	// 엔티티의 경기 시간 프로퍼티 값: UTC 시간대 출력

현재 시간보다 9시간이 이른 UTC 시간이 출력되었다. 실제 프론트에는 KST 기준의 시간을 넘겨야 했기 때문에 매번 이를 보정해주는건 문제라고 생각했다. 따라서 삽질을 시작했다

삽질 1 - Node 타임존 설정

기본적으로 노드는 타입존이 설정되어 있지 않다. 따라서 환경 변수를 통해 타임존을 설정했다. 하지만 문제가 해결되지 않았다. 이 부분에 대한 내용은 뒤에서 따로 설명

// .dotenv
TZ='Asia/Seoul'

삽질 2 - typeorm 타임존 설정

열심히 구글링 해보니 typeorm은 로컬 시간대에 맞춰 UTC 시간대와의 차이만큼 보정해준다는 것을 알아내었다. 따라서 Date 객체가 UTC 시간이니 typeorm의 타임존을 UTC로 설정하고 Date 객체를 생성할 때 9시간을 더해주는 팩토리 함수를 만들어 사용하면 되지 않을까 생각하여 Datesource 설정의 timezone 옵션을 'Z'(UTC)로 설정했다

export const MysqlDateSource = new DataSource({
  // options...
  timezone: 'Z'	// 'local', 'Z'(UTC), '+HH:MM'(오프셋 직접 설정), '-HH:MM'(오프셋 직접 설정)
})

이렇게 문제를 해결하나 싶었지만... 경기 스케줄 저장 시 시간을 Date 객체의 setXX 메소드를 이용해 직접 설정하는데 이렇게 할 시 UTC 시간으로 되돌아갔다. DB에도 UTC로 저장이 되버려 'Z' 옵션은 사용하지 못하게 되었다. 여기서 문제를 찾기 위해 반나절을 삽질했던것 같다

해결 - Date 객체의 특징

삽질을 계속하다 Date 객체의 MDN 문서를 보다가 이런 setXX, getXX 메소드에서 이런 문구를 발견했다. 주어진 날짜의 현지 시간 기준.

결론은 이렇다. Date 객체를 toString, toLocaleString 등의 메소드를 사용하지 않고 출력하면 로컬 타임존과 관련없이 UTC 시간으로 출력된다. 하지만 setXX, getXX 메소드는 현지 시간이 기준이다. 즉 출력이 UTC로 되었다고 해서 실제 Date 객체의 시간이 UTC 시간이 아니었던 것이었다

어이없게도 실제로 문제가 아닌데 문제라고 생각해서 발생한 해프닝이었다. 이 해프닝으로 인해 하루 가까이를 해맸지만 자바스크립트에서 날짜를 다루는데 이해가 높아지는 나쁘지 않은 해프닝이라 생각한다

0개의 댓글