Skip to Content
DecisionsADR-0002: snake_case ↔ camelCase 변환을 fetcher 경계에서 한 번만 한다

ADR-0002: snake_case ↔ camelCase 변환을 fetcher 경계에서 한 번만 한다

  • Status: Accepted
  • Date: 2026-08-31 (소급 기록)
  • 관련: packages/api/fetcher/fetch.ts, packages/api/lib/HowmuchhomeAPI.ts, packages/utils-ts/object.ts

소급 기록입니다. Context는 코드에서 재구성했습니다.

Context

코어 API(Python/FastAPI)는 관례대로 snake_case JSON을 주고받습니다. 프론트(TypeScript)의 관례는 camelCase입니다.

변환을 어디서 할지 정하지 않으면 두 표기가 코드베이스 전체에 섞입니다. 그러면 타입 정의, 컴포넌트 props, 폼 필드명, 로컬 스토리지 키가 서로 다른 규칙을 따르게 되고, “이 객체는 서버에서 온 건가 우리가 만든 건가”를 매번 확인해야 합니다.

검토한 대안

대안채택 안 한 이유
변환하지 않고 서버 표기를 그대로 쓴다TS 코드 전체가 snake_case가 되어 생태계 관례와 어긋남. 서버 필드와 파생 필드가 구분되지 않음
화면·훅마다 필요할 때 변환한다변환 지점이 N개로 늘어나 누락이 생기고, 같은 필드가 파일마다 다른 표기로 나타남
백엔드가 camelCase로 응답한다다른 레포·다른 언어의 관례를 프론트 사정으로 바꾸는 것. 다른 소비자에게도 영향

Decision

packages/api의 fetcher 경계에서 양방향으로 한 번만 변환한다.

  • 응답: objectKeySnakeToCamelfetcher/fetch.ts. 성공 응답과 에러 바디 모두 적용
  • 요청: objectKeyCamelToSnakelib/HowmuchhomeAPI.ts. 쿼리 파라미터JSON으로 직렬화되는 객체 body에 적용

따라서 경계 안쪽(도메인 타입, 프론트 코드 전부)은 예외 없이 camelCase입니다. API 스펙 문서에 snake_case로 적혀 있어도 프론트 타입은 camelCase로 정의합니다.

예외 — 자동 변환이 적용되지 않는 body FormData · URLSearchParams · 문자열 body는 HowmuchhomeAPI.ts에서 변환 대상에서 명시적으로 제외되어 그대로 전송됩니다. 파일 업로드 같은 multipart 엔드포인트를 호출할 때는 호출자가 직접 snake_case 필드명을 만들어야 합니다. camelCase로 채우면 서버가 조용히 필드를 못 읽습니다.

Consequences

좋아지는 것

  • 프론트 코드 어디서도 표기를 고민하지 않는다. 서버에서 온 값과 파생 값이 표기상 구분되지 않는 게 오히려 이점 — 소비 코드가 출처를 몰라도 된다
  • JSON 요청·응답에서는 변환 누락 버그가 구조적으로 불가능하다 (통과 지점이 하나)

감수하는 것

  • API 스펙 문서와 프론트 타입의 표기가 다르다. 처음 보는 사람은 반드시 한 번 혼란을 겪는다
  • 응답 전체를 재귀 순회하므로 큰 페이로드에 비용이 있다 (현재 문제된 적 없음)
  • multipart 경로는 이 결정의 보호를 받지 못한다. 위 예외 때문에 FormData 호출부만 표기 규칙이 다르다 — 규칙이 100% 균일하지 않다는 비용이다
  • 변환기가 키를 데이터로 취급하지 않는다. 서버가 의도적으로 특정 표기를 유지해야 하는 키가 있으면 예외 목록에 넣어야 한다 — 이미 PRESERVE_CAMEL_CASE_KEYS(packages/utils-ts/object.ts)에 two_way_info 하나가 그렇게 들어가 있다. 사용자 입력이 키가 되는 map을 주고받으려면 이 방식은 맞지 않는다
  • 양방향이 대칭이 아니다. 응답 변환은 _가 있는 키만 바꾸므로 camelCase 응답은 그대로 통과한다(→ front-api는 camelCase로 응답해도 된다). 반면 요청 변환은 대문자를 무조건 _소문자로 바꾸므로, front-api를 포함한 모든 대상이 snake_case 요청을 받는다. front-api 핸들러를 camelCase 기준으로 짜면 필드가 조용히 사라진다

이 결정을 다시 열어야 할 때

  • 코어 API가 camelCase 응답 옵션을 제공할 때
  • 재귀 변환 비용이 실제 성능 측정에 잡힐 때 (RUM 기준)
Last updated on