노트

타입 브랜딩

Branded Types

프런트엔드#ts · 연결된 개념 6개

쉽게 말하면

타입 브랜딩은 똑같이 생긴 열쇠에 색 스티커를 붙여 '현관용', '창고용'을 구분하는 것과 같아요. 둘 다 문자열인 회원 ID와 게시글 ID를 실수로 바꿔 넣지 못하게 막아 줘요.

비유가 깨지는 곳 스티커는 타입에만 있고 런타임에는 사라져요. 붙일 때 as 단언이 필요하니 그 단언은 검증 함수 한 곳에만 두고, 검증을 통과한 값에만 브랜드를 붙여요.

타입 브랜딩(Type Branding)은 같은 구조의 타입(예: 둘 다 string인 UserId와 PostId)을 서로 다른 타입으로 취급하게 하려고, 실제로는 없는 표식 속성을 교집합으로 붙이는 패턴이다. 구조적 타이핑 위에서 명목적 타이핑(Nominal Typing)을 흉내 낸다.

type Brand<T, B> = T & { readonly __brand: B }
type UserId = Brand<string, 'UserId'>
type PostId = Brand<string, 'PostId'>
 
function getUser(id: UserId) {}
getUser('u_1' as UserId)   // ✅
getUser('p_1' as PostId)   // ❌ 다른 브랜드
  • 용도: ID 섞임 방지, 단위 구분(Meter·Kilometer), 검증을 통과한 값 표시(Email, SanitizedHTML)
  • 검증 함수만 브랜드 타입을 반환하게 하면 "검증된 값만 이 함수에 들어온다"를 타입으로 강제할 수 있다. Zod의 .brand()도 같은 아이디어다
  • __brand는 예약어가 아니라 관례다. 이름은 무엇이든 되지만, 여러 브랜드가 같은 속성 이름을 쓰고 값으로 구분해야 헬퍼 하나로 관리하기 쉽다
  • 브랜드는 타입에만 있고 런타임에는 사라진다. 만들 때 as가 필요하므로 그 단언은 검증 함수 한 곳에만 둔다
  • 같은 문제를 클래스로 푸는 방법이 값 객체이고, 원시값을 그대로 쓰는 냄새는 기본형 집착이다. 외부 값은 unknown으로 받아 검증 후 브랜드를 붙인다

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 타입 추론과 타입 검사

    타입 추론(Type Inference)은 표기가 없어도 컴파일러가 값의 타입을 정하는 단계이고, 타입 검사(Type Checking)는 정해진(추론됐거나 명시된) 타입을 코드가 지키는지 확인하는 단계다. 보통 추론이 먼저 일어나고, 그 결과를 기준으로 검사한다.

  • 매핑된 타입과 keyof

    매핑된 타입(Mapped Types)은 기존 타입의 키를 하나씩 돌며 새 타입을 만드는 문법([K in keyof T]: …)이다. keyof T는 객체 타입의 키 유니언을, T[K](인덱스 접근 타입, Indexed Access Type)는 그 키의 값 타입을 꺼낸다.

  • 템플릿 리터럴 타입

    템플릿 리터럴 타입(Template Literal Types)은 문자열 리터럴 타입을 템플릿 문자열 문법으로 조합해 새 문자열 유니언을 만드는 기능이다. 유니언을 넣으면 가능한 모든 조합이 나온다.

  • 제네릭

    제네릭(Generics)은 타입을 매개변수처럼 받아, 여러 타입에 같은 로직을 재사용하면서도 타입 정보를 잃지 않게 하는 기능이다.

  • type과 interface

    type(타입 별칭, Type Alias)과 interface는 둘 다 타입에 이름을 붙이는 방법이다. 객체 모양을 정의할 때는 대부분 바꿔 써도 되지만, 확장 방식과 표현 범위가 다르다.

보기 옵션