오늘 한 내용

  • dev-data-server-light
    • DB, Auth Token, Storage에 JSON 기반 영속성 추가
    • JSON 파일 읽기·쓰기 로직 공통화
    • 실행 환경에 따라 JSON과 in-memory 저장소를 선택하도록 구성
  • Stack Day
    • Habit, Habit Entry, Completion의 역할을 나눠 데이터 모델링

배운 내용

저장 방식은 애플리케이션의 관심사가 아니다

dev-data-server-light를 처음 만들 때는 in-memory 저장소만 사용했다. 개발 중에는 빠르고 편하지만 서버를 재시작하면 유저, 토큰, 파일 데이터가 모두 사라진다. MVP를 마무리하려면 실제로 재시작해도 데이터가 남는 저장 방식이 필요했다.

그렇다고 서비스와 라우터가 JSON 파일 저장을 직접 알게 만들고 싶지는 않았다. 저장 방식을 바꾸기 위해 애플리케이션 로직까지 수정하게 되면, 처음부터 인터페이스를 둔 의미가 없어지기 때문이다.

그래서 기존 인터페이스를 구현하는 JSON 저장소를 추가하고, 의존성 조립부에서 어떤 구현체를 사용할지만 선택하도록 했다. 기본값은 JSON 영속 저장소로 두고, 필요하면 PERSISTENCE=memory를 지정해 기존 in-memory 방식으로 실행할 수 있게 했다.

이번 작업을 하면서 DIP를 문서로만 이해할 때보다 실제로 구현체를 교체해보는 편이 훨씬 명확했다. DB, 토큰, 파일 저장소를 추가했지만 서비스와 라우터의 저장 방식은 바뀌지 않았다.

JSON 파일을 그냥 덮어쓰면 안 되는 이유

DB와 Auth 토큰 저장소는 모두 JSON 파일을 사용하므로 파일을 읽고 쓰는 공통 로직을 jsonFile.ts로 분리했다. 특히 파일을 바로 덮어쓰지 않고 임시 파일에 먼저 쓴 뒤 원래 경로로 교체하도록 했다.

저장 중간에 프로세스가 종료되면 JSON 파일이 깨질 수 있기 때문이다. 개발용 서버라도 여러 저장소가 같은 쓰기 규칙을 공유하게 만들면 이런 실수를 한 곳에서 줄일 수 있다.

Habit EntryCompletion은 다르다

Stack Day의 오늘 화면에 보이는 항목을 그대로 저장할지 고민했다. 하지만 Habit Entry는 습관과 현재 날짜를 바탕으로 화면에 표시하기 위해 계산할 수 있는 개념에 가깝다.

실제로 저장해야 하는 것은 사용자가 특정 날짜에 습관을 수행했다는 사실이다. 이 기록을 Completion으로 두면 오늘의 Entry는 필요할 때 계산하고, 과거 기록과 통계는 Completion을 기준으로 만들 수 있다.

완료 취소도 Entry의 상태를 바꾸는 일이 아니라 Completion을 제거하는 일로 설명할 수 있다. 저장해야 하는 원본과 화면에 보여줄 파생 데이터를 나누니, 과거 기록 수정과 통계 정책도 훨씬 자연스럽게 정리됐다.

해결한 것

서버를 재시작해도 유저, 토큰, 파일 데이터가 유지되도록 저장소를 구현했다. 저장소 구현체를 의존성 조립부에서 선택하도록 만들어 서비스와 라우터는 JSON인지 메모리인지 알 필요가 없게 했다.

Stack Day는 Habit을 저장하고 실제 수행 기록인 Completion을 별도로 저장하는 방향으로 모델을 정리했다. 오늘 화면의 Entry는 저장하지 않고 필요한 시점에 계산한다.

내일 할 것

  • dev-data-server-light Docker 이미지 만들기
  • GitHub Actions에서 GHCR 배포 연결하기
  • PS 문제 풀기