TypeScript 브랜딩(Branding) 개념과 활용법
TypeScript를 사용하다 보면, 동일한 기본 타입을 공유하지만 서로 다른 의미를 갖는 값을 구분하고 싶을 때가 있다. 예를 들어, 숫자로 표현되는 UserId
와 OrderId
를 혼동해서 사용하는 실수를 방지하고 싶다면 어떻게 해야 할까?
이런 경우에 브랜딩(Branding) 기법을 활용하면 타입을 더욱 안전하게 관리할 수 있다.
1. TypeScript 브랜딩이란?
브랜딩(Branding)은 TypeScript의 타입 시스템을 활용하여 기본 타입(primitive type)에 의미를 부여하는 기법이다. 이를 통해 동일한 기본 타입을 사용하지만, 의도치 않은 값의 혼용을 방지할 수 있다.
예를 들어, 아래와 같은 코드에서 number
타입을 직접 사용하면 UserId
와 OrderId
가 같은 타입이므로 서로 바꿔서 사용해도 컴파일러가 오류를 감지하지 못한다.
type UserId = number;
type OrderId = number;
function getUser(id: UserId) { /* 사용자 정보를 가져오는 함수 */ }
const orderId: OrderId = 123;
getUser(orderId); // 논리적으로 잘못된 호출이지만, TypeScript는 문제를 발견하지 못함
이 문제를 해결하기 위해 브랜딩 기법을 활용할 수 있다.
2. 브랜딩 적용 방법
2.1 인터섹션 타입을 활용한 브랜딩
가장 많이 사용되는 방법은 인터섹션 타입(&)과 유니크한 속성을 추가하는 방식이다.
type Brand<T, B> = T & { __brand: B };
type UserId = Brand<number, "UserId">;
type OrderId = Brand<number, "OrderId">;
function getUser(id: UserId) { /* 사용자 정보를 가져오는 함수 */ }
const userId = 123 satisfies UserId;
const orderId = 456 satisfies OrderId;
getUser(userId); // 정상 작동
getUser(orderId); // 오류 발생: OrderId는 UserId가 아님
Brand<T, B>
는T
타입에__brand
라는 고유한 속성을 추가하여 구별한다.OrderId
를UserId
로 전달하면 컴파일러가 오류를 발생시킨다.
2.2 Nominal Typing을 활용한 브랜딩
위 방법과 유사하지만, 인터페이스를 활용하는 방식도 가능하다.
interface UserId {
__brand: "UserId";
}
interface OrderId {
__brand: "OrderId";
}
function getUser(id: UserId) { /* 사용자 정보를 가져오는 함수 */ }
const userId = { __brand: "UserId" } satisfies UserId;
const orderId = { __brand: "OrderId" } satisfies OrderId;
getUser(userId); // 정상 작동
getUser(orderId); // 오류 발생
이 방법도 서로 다른 타입을 구별하는 데 유용하지만, 기본 타입(예: number
)을 직접 사용할 수 없다는 단점이 발생한다.
const id: UserId = 123; // 오류 발생: 'number' 타입은 'UserId'에 할당할 수 없음
위 코드에서 UserId
는 인터페이스 기반의 타입이므로 number
값을 직접 할당할 수 없습니다. 대신 객체 형태로 생성해야 하므로 사용성이 떨어질 수 있습니다.
3. 브랜딩이 유용한 경우
브랜딩 기법은 아래와 같은 상황에서 특히 유용하다.
ID, 키 값 구별
데이터베이스에서 불러온 ID 값들을 명확하게 구별할 수 있다.
type ProductId = Brand<number, "ProductId">;
type CategoryId = Brand<number, "CategoryId">;
function getProduct(id: ProductId) { /* 상품 정보를 가져오는 함수 */ }
단위가 다른 숫자 구별
단위를 가진 숫자 값도 헷갈리지 않도록 구별할 수 있다.
type Kilograms = Brand<number, "Kilograms">;
type Pounds = Brand<number, "Pounds">;
const weightKg = 70 satisfies Kilograms;
const weightLb = 154 satisfies Pounds;
보안과 안전한 API 설계
REST API 호출 시 토큰을 명확하게 구분하여 보안성을 높일 수 있다.
type AccessToken = Brand<string, "AccessToken">;
type RefreshToken = Brand<string, "RefreshToken">;
function useAccessToken(token: AccessToken) { /* API 요청 */ }
4. 브랜딩의 한계와 주의할 점
런타임에는
__brand
속성이 존재하지 않음
브랜딩은 타입 시스템에서만 동작하는 개념이며, 실제 실행 시점에서는__brand
속성이 사라진다.타입 단언을 남용하면 의미가 퇴색
satisfies
를 활용하면as
키워드보다 타입 안정성이 증가하지만, 여전히 남용을 피해야 한다.
5. 결론
TypeScript의 브랜딩(Branding) 기법은 동일한 기본 타입을 사용하지만, 의미적으로 다른 값들을 구별하는 데 효과적이다
ID, 단위, 보안 토큰 등의 관리가 필요한 경우, 브랜딩을 적용하면 보다 안전한 코드 작성이 가능하다.
앞으로 TypeScript 프로젝트에서 더 안전한 타입 설계를 원한다면, 브랜딩을 적극 활용해보자.
'Develope > 기타' 카테고리의 다른 글
Chrome 브라우저 언어 설정 변경 및 navigator.language (0) | 2025.01.06 |
---|---|
더 나은 코드 품질을 위한 SOLID 원칙 (0) | 2024.08.30 |
Typescript Infer란? (0) | 2024.08.26 |
Typescirpt any, unknown, never 정리 (0) | 2024.08.16 |
컴파일러(Compiler), 트랜스파일러(Transpiler) (0) | 2024.06.22 |