브랜드(Brand) 타입을 넣었는데도 계산 버그가 멈추지 않았던 이야기
요약
금융 데모 앱에서 금리 및 요율 단위 오류가 빈번하게 발생하여, 단순한 타입 시스템(Brand Type)만으로는 계산 과정에서의 데이터 유효성 문제를 완전히 해결할 수 없었습니다. Brand 타입을 도입해 연이율과 일리 같은 단위 혼동을 컴파일 단계에서 막는 데 성공했지만, 실제 덧셈/뺄셈 등의 '계산' 과정을 거치면 명찰(브랜드)이 떨어져 일반 `number`로 변질되는 치명적인 한계를 발견했습니다. 결국 이 문제를 해결하기 위해 타입 시스템의 경계를 넘어, ESLint의 `no-restricted-syntax`와 같은 정적 분석 도구를 활용하여 데이터가 잘못된 경로로 유입되거나 강제 캐스팅되는 것을 근본적으로 차단하는 구조적 방어막을 구축하게 되었습니다.
핵심 포인트
- Brand 타입은 단위 혼동(예: 연리 vs 일리)을 컴파일 단계에서 효과적으로 막아주지만, 실제 산술 계산 과정에서는 명찰이 떨어져 일반 `number`로 변질되는 한계가 있다.
- 단순한 타입 시스템만으로는 데이터의 '구조적 흐름'까지 제어할 수 없으며, 런타임/컴파일 타임의 모든 경로를 검증해야 한다.
- TypeScript의 강력한 기능(Brand Type)과 ESLint 같은 정적 분석 도구를 결합하여, 코드베이스 전체에 걸친 암묵적인 규칙 위반 및 잘못된 데이터 유입을 근본적으로 차단하는 것이 중요하다.
금융 관련 데모 앱에서 금리 및 요율 단위 오류가 반복적으로 발생했습니다. 배당 수익률은 연이율 %, 수수료는 bps, 계산은 소수점 등 화면마다 표기가 달랐고, 그 결과 집계 화면에서 배당금이 100배 틀어져 표시되는 사고가 빈번하게 발생했습니다.
단위 혼동 문제는 사람이 리뷰로 지적해도, AI에게 코드를 작성시키더라도 완전히 막을 수 없었습니다.
'다음엔 조심하자'는 가장 효과가 없는 재발 방지책이었습니다.
그래서 TypeScript의 강력한 무기인 Brand 타입(명찰이 붙은 타입)을 도입하기로 했습니다. 이걸로 숫자 혼동은 컴파일 에러로 멈출 수 있을 줄 알았습니다.
본 글에서는 왜 타입을 넣었는데도 막을 수 없었는지, 그리고 최종적으로 어떻게 '구조'를 통해 재발을 방지했는지를 되돌아봅니다.
Brand 타입 (number & { __brand: 'hoge' }
type AnnualRate = number & { __brand: 'rate_annual' } // 연리 전용 숫자
type DailyRate = number & { __brand: 'rate_daily' } // 일리 전용 숫자
이로써 '연리를 기대하는 함수에, 실수로 일리를 전달하는' 등의 실수는 컴파일 에러로 막을 수 있게 되었습니다. 하지만 여기서 치명적인 문제에 부딪힙니다.
TypeScript의 사양상, 계산(덧셈이나 뺄셈)을 하면 애써 붙인 명찰이 떨어져 일반 number가 되어 버리는 것입니다.
const a = 0.01 as AnnualRate;
const b = 0.02 as AnnualRate;
const c = a + b; // 결
하지만 이것이 완벽한 방어막은 아닙니다. `parseFloat`을 사용해 숫자로 되돌리는 방식은 타입(Type)만으로는 막을 수 없기 때문에, **계산 경로로의 유입을 타입으로 차단하는 장치** 정도의 위치라고 할 수 있습니다.
안타깝게도 AI는 타입 에러를 제거하는 데 능숙하기 때문에, 타입 에러가 발생해도 `as Rate`을 사용해 당당하게 넘어갑니다. 코드베이스 전체에 걸친 암묵적인 규칙은 타입으로는 전달되지 않는 것 같습니다.
Parser 외부에서는 다음 두 가지를 ESLint로 금지시킵니다.
// 오류 예시
const rate = response.rate_decimal // 원본 필드 직접 접근
const fake = { period: 'annual', value: 0.005 } as Rate // Parser 외부의 as Rate
`no-restricted-syntax`를 사용하여 `TSAsExpression[typeAnnotation.typeName.name='Rate']`와 같이
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기