Daily Bible 어드민 4개 페이지 스키마 불일치 일괄 수정
목차
Daily Bible 어드민 페이지 4개가 한꺼번에 schema mismatch로 터졌다. 한 번에 정리했다.
배경: 왜 4종이 동시에?
actions/daily-bible.ts 하나가 여러 클라이언트 컴포넌트에 데이터를 공급하는 구조였다. Server Action 혹은 data-fetching 레이어가 반환하는 타입 shape이 실제 DB 스키마나 API 응답과 살짝 어긋나 있었는데, 그게 CalendarClient, PushClient, VersesClient, stats/page 전부에 걸쳐 드러난 것이다. 한 곳에서 잘못 정의된 타입이 여러 소비 지점에서 동시에 터지는 전형적인 패턴이다.
이런 상황은 보통 두 가지 흐름 중 하나에서 발생한다. 하나는 DB 마이그레이션이나 API 스펙 변경 후 타입 정의를 업데이트하지 않은 경우, 다른 하나는 처음부터 action 반환 타입을 너무 느슨하게(또는 잘못) 선언해두고 각 클라이언트 컴포넌트에서 암묵적으로 쓰다가 null 분기를 빠뜨린 경우다. 이번엔 후자에 가까웠다.
작업 내용
변경된 파일 6개를 기준으로 정리하면 다음과 같다.
| 파일 | 주요 수정 포인트 |
|---|---|
src/actions/daily-bible.ts |
반환 타입 재정의, 필드명·nullable 여부 정렬 |
CalendarClient.tsx |
schema 맞춰 props 접근 경로 수정 + null-safe 처리 |
PushClient.tsx |
동일 패턴, push 관련 필드 null guard 추가 |
VersesClient.tsx |
구절 데이터 접근 시 optional chaining 적용 |
verses/page.tsx |
SSR 레벨에서 fetch 결과 null 체크 추가 |
stats/page.tsx |
통계 집계 필드 mismatch 수정, undefined 분기 처리 |
핵심은 daily-bible.ts action 쪽을 먼저 정리한 것이다. 소비하는 쪽 컴포넌트들을 개별로 패치하는 것보다 공급 지점의 타입을 먼저 확정하고, 그 타입에 맞게 클라이언트들을 맞춰가는 순서가 맞다. 반대로 하면 각 컴포넌트가 각자 방어 코드를 다르게 쓰게 되고 나중에 또 다른 형태의 불일치가 생긴다.
null-safe 처리는 주로 optional chaining(?.)과 nullish coalescing(??) 조합으로 처리했다.
// before: schema mismatch + null 미처리
const title = verse.titleKr;
const pushDate = schedule.date.toISOString();
// after: 필드명 수정 + null-safe
const title = verse?.title_kr ?? '';
const pushDate = schedule?.date?.toISOString() ?? null;
단순해 보이지만 어드민 페이지에서 이게 터지면 해당 기능 전체가 렌더링 자체를 못 하거나, 운영자가 데이터를 읽다가 빈 화면을 보게 된다.
회고: schema mismatch는 빨리 잡을수록 싸다
이런 류의 버그는 TypeScript를 쓰면서도 any를 중간에 낀다거나, Prisma/ORM 반환 타입을 그대로 흘려보내지 않고 별도 타입으로 재선언할 때 필드 이름을 손으로 쳐넣는 과정에서 슬금슬금 생긴다. 특히 DB 컬럼이 snake_case인데 앱 타입이 camelCase로 매핑되는 구간에서 자주 난다.
팀에서 이걸 예방하는 가장 확실한 방법 몇 가지:
- action / fetch 레이어 반환 타입을
zod등으로 런타임에서도 검증하는 것. 빌드 타임 타입 체크만으로는 API 응답이 실제로 다른 shape으로 오는 경우를 못 잡는다. - 컴포넌트가 받는 props 타입을 action 반환 타입에서 직접
infer하게 만들어두면, action 타입 바꿀 때 컴포넌트까지 타입 에러가 전파된다. - 어드민 페이지라도 null 화면 대신 에러 바운더리 + fallback UI를 달아두면, 터져도 운영자가 빈 화면 대신 "데이터를 불러올 수 없습니다" 정도는 본다.
이번엔 한 번에 4개 페이지를 같이 고쳤는데, 오히려 한 번에 보면서 패턴이 보였다. 하나씩 들어갔으면 오히려 더 오래 걸렸을 거다. schema가 흔들리면 소비 지점 전체를 한 번에 훑는 게 낫다는 걸 다시 확인함.
다음 스프린트에서 action 레이어 반환 타입에 Zod inference 붙이는 걸 작업 목록에 올려놨다.
댓글 0
첫 댓글 달아줘.