8.6 KiB
category, tags, title, last_updated
| category | tags | title | last_updated | ||||
|---|---|---|---|---|---|---|---|
| Unified |
|
|
2026-05-02 |
기본 타입에의 집착 (Primitive Obsession)
📌 Brief Summary
'기본 타입에의 집착(Primitive Obsession)'은 서로 다른 의미와 맥락을 지닌 데이터를 구별하지 않고 범용적인 원시 기본 타입(예:
string,number)만 사용하여 코드를 작성하려는 안티 패턴을 의미합니다 [1, 2]. TypeScript와 같은 구조적 타이핑 환경에서는 이메일 주소와 이름이 모두string으로 취급되어, 의도치 않은 잘못된 값이 전달되더라도 컴파일러가 오류를 잡아내지 못하는 취약점을 발생시킵니다 [2]. 이를 방지하고 타입의 의미적 경계를 명확히 하기 위해, 런타임에는 동일한 원시 값이지만 컴파일 단계에서는 서로 다르게 구별되도록 강제하는 브랜디드 타입(Branded Types)이나 오패크 타입(Opaque Types) 기법이 해결책으로 활용됩니다 [2, 3].
'기본 타입에의 집착(Primitive Obsession)'은 이메일 주소, 이름, 식별자, 통화 등 의미적으로 서로 다른 데이터를
string이나number같은 단일한 기본(원시) 타입으로만 취급하여 표현하려는 문제를 뜻한다[1, 2]. TypeScript와 같이 구조적 타이핑(Structural Typing)을 사용하는 언어에서는 컴파일러가 이러한 데이터들의 의미적 차이를 구분하지 못해 의도치 않은 데이터 혼용 실수를 방지할 수 없다[2, 3]. 이 문제를 해결하기 위해 고유한 표식을 부여하는 브랜디드 타입(Branded Types)이나 불투명 타입(Opaque Types) 같은 기법이 해결책으로 사용된다[2-4].
📖 Core Content
-
개념 및 원인
- 현대 애플리케이션에서는 토큰, 리소스 식별자, 이벤트 이름 등을 모두
string으로 타이핑하고 통화, 타임스탬프, 내부 카운터 등을 모두number로 뭉뚱그려 처리하는 경향이 있습니다 [1]. - TypeScript의 구조적 타이핑(Structural Typing)은 객체의 형태가 일치하면 같은 타입으로 간주하므로 유연성이 높지만, 이로 인해 의미적으로 전혀 다른 데이터를 구분하지 못하는 '기본 타입에의 집착' 문제를 본질적으로 야기하게 됩니다 [2].
- 현대 애플리케이션에서는 토큰, 리소스 식별자, 이벤트 이름 등을 모두
-
발생할 수 있는 위험성
- 식별 불가능성: 함수 매개변수로 사용자 ID와 계정 ID를 전달할 때 둘 다
string으로 타이핑되어 있다면, 매개변수의 순서를 실수로 바꿔서 전달해도 TypeScript 컴파일러가 에러를 발생시키지 않아 런타임 버그로 이어집니다 [2, 3]. - 역사적 실패 사례: 마스 클라이메이트 오비터(Mars Climate Orbiter) 프로젝트는 단위계(파운드-힘 초 vs 뉴턴-초)가 다른 숫자를 단순 원시 타입으로 혼용하여 연산하다가 탐사선이 파괴되는 치명적인 시스템 실패를 겪기도 했습니다 [4].
- 식별 불가능성: 함수 매개변수로 사용자 ID와 계정 ID를 전달할 때 둘 다
-
해결 방법론
- 명목적 구분의 부여: TypeScript는 기본적으로 명목적 타이핑(Nominal Typing)을 지원하지 않으나, '오패크 타입(Opaque Types)' 또는 '브랜디드 타입(Branded Types)'을 도입하여 이 한계를 극복할 수 있습니다 [5, 6].
- 브랜딩(Branding) 기법:
unique symbol이나 고유 속성(__brand)을 기존 원시 타입과 교집합(&)으로 엮어 런타임 데이터 표현에는 영향을 주지 않으면서 컴파일 시점에만 존재하는 엄격한 고유 표식을 부여합니다 [2, 6, 7]. - 데이터 오염 차단: 도메인 기반 설계(DDD) 관점에서는 이 기법을 이용해
UserId와OrderId를 엄격히 분리하고, 런타임 검증을 통과한 데이터(예:SanitizedString)만이 시스템의 내부 비즈니스 로직으로 진입하도록 강제함으로써 데이터가 오염되는 것을 원천 차단합니다 [2, 8].
-
기본 타입 집착의 원인 및 한계 현대 애플리케이션에서
string은 토큰, 리소스 식별자, 이벤트 이름 등 아주 다양한 역할을 하며,number역시 통화, 가상 포인트, 타임스탬프 등을 나타낸다[1]. 하지만 TypeScript의 구조적 타이핑 특성상, 이메일 주소가 필요한 매개변수에 사용자의 이름을 전달해도 둘 다string이라는 이유로 컴파일러가 호환성을 인정해버리며 오류를 잡아내지 못한다[2]. 즉, 외견상 구조가 같아 보이는 기본 타입들을 의미론적으로 차별화할 수 있는 내장된 방법이 부족한 것이 근본적인 원인이다[5]. -
실제 발생할 수 있는 위험성 이러한 기본 타입에의 집착은 런타임 오류와 심각한 논리적 결함을 유발한다. 가장 널리 알려진 비극적 사례는 미국 단위계(파운드힘 초)와 SI 단위계(뉴턴 초)를 동일한 숫자 타입으로 혼용하여 3억 2,760만 달러 규모의 궤도선이 소실된 Mars Climate Orbiter 사건이다[4]. 이 외에도 서로 다른 통화(예: EUR와 GBP)의 상품 가격을 의미 구분 없이
number타입으로 묶어 연산할 경우 시스템의 불일치나 수익 손실을 초래할 수 있으며[6], 인증 서버 토큰과 API 토큰을 혼동하여 잘못된 인자로 전달하는 버그가 발생할 수 있다[7]. -
해결 방안: 명목적 타이핑의 수복 기본 타입에의 집착을 해결하기 위해 도메인 기반 설계(DDD) 등에서 '브랜디드 타입(Branded Types)' 또는 '불투명 타입(Opaque Types)'이 널리 사용된다[2, 8]. 이는 런타임에는 존재하지 않지만 컴파일 시점에만 존재하는 고유한 속성(브랜드)을 원시 타입에 강제로 덧붙여(예:
string & { __brand: 'UserId' }) 의미가 다른 타입 간의 호환과 섞임을 차단하는 기법이다[2, 9]. 이를 통해 사용자 ID와 주문 ID를 엄격히 분리하거나[10], 철저히 검증된 데이터(Sanitized String 등)만이 시스템 내부 로직으로 진입하도록 강제하여 데이터 오염을 예방할 수 있다[2, 11].
⚖️ Trade-offs & Caveats
- 과거 데이터와의 충돌: 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
- 정책 변화: Programming & Language 분야의 자동 자산화 수행.
- 과거 데이터와의 충돌: 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
- 정책 변화: Programming & Language 분야의 자동 자산화 수행.
🔗 Knowledge Connections
- Related Topics: 구조적 타이핑 (Structural Typing), 명목적 타이핑 (Nominal Typing), 브랜디드 타입 (Branded Types), 오패크 타입 (Opaque Types)
- Projects/Contexts: 도메인 기반 설계 (DDD), 마스 클라이메이트 오비터 (Mars Climate Orbiter)
- Contradictions/Notes: 브랜디드 타입과 같은 해결책은 프로그램의 안전성을 높이고 '기본 타입에의 집착'을 해소해 주지만, 추가적인 타입 작성 및 개념적 복잡성이 증가하는 단점(비용)이 수반됩니다. 따라서 개발팀은 애플리케이션에서 실제로 직면할 가능성이 높은 문제인지 득실을 판단하여 도입해야 한다고 조언합니다 [9, 10].
Last updated: 2026-04-18
- Related Topics: 구조적 타이핑(Structural Typing), 브랜디드 타입(Branded Types), 불투명 타입(Opaque Types), 도메인 기반 설계 (DDD)
- Projects/Contexts: TypeScript 타입 시스템, Mars Climate Orbiter 사례
- Contradictions/Notes: 소스에 따르면 타입 브랜딩은 프로그램의 안전성을 극대화하지만, 코드의 개념적 복잡성을 증가시키고 작성해야 할 타이핑 량이 많아진다는 단점이 있다. 따라서 일부 소스에서는 값이 0이 아님이 개발자에 의해 명확히 인지되는 등의 특정 상황에서는 브랜디드 타입의 강박적 사용을 피하고 단순한 원시 타입과 단일 예외 처리(어설션)를 사용하는 것이 코드를 더 간결하고 읽기 쉽게 만들 수 있다고 조언한다[12-14].
Last updated: 2026-04-18