[TYPESCRIPT] 문자열 키 자동 생성 전략: Mapped Types와 Conditional Types로 설정 규칙을 고정하는 방법

 

Template Literal Types로 문자열 규칙을 타입으로 고정해보면, 곧 다음 단계의 요구가 자연스럽게 등장합니다. “이 문자열 키들을 사람이 직접 만들지 않고, 객체 정의로부터 자동으로 만들 수는 없을까?”라는 질문입니다.

 

실무에서는 다음과 같은 상황을 자주 마주합니다.

  • 설정 객체와 문자열 키 목록이 따로 관리된다
  • 설정은 추가됐는데, 키 타입은 업데이트되지 않았다
  • 권한/설정/피처 플래그 키가 점점 관리 불가능해진다

 

이 문제의 핵심은 “중복 정의”입니다. 이번 글에서는 Mapped Types와 Conditional Types를 조합해, 단 하나의 설정 정의로부터 문자열 키 규칙을 자동 생성하는 패턴을 실무 기준으로 정리합니다.

 

개념/배경 설명: 중복 정의는 왜 반드시 어긋나는가

다음 구조는 실무에서 매우 흔합니다.

 


// 설정 객체
const featureConfig = {
  user: {
    enabled: true,
  },
  order: {
    enabled: false,
  },
};

 

그리고 어딘가에는 이런 타입이 존재합니다.

 


type FeatureKey =
  | 'user.enabled'
  | 'order.enabled';

 

이 구조는 처음에는 명확하지만, 설정이 늘어날수록 반드시 어긋납니다. 문제는 사람이 실수해서가 아니라, 중복이 구조적으로 허용되었기 때문입니다.

 

핵심 설계 1: Mapped Types로 객체 구조를 순회한다

첫 번째 단계는 “객체의 키 구조를 타입 레벨에서 순회하는 것”입니다.

 


type Keys<T> = {
  [K in keyof T]: K;
}[keyof T];

 

이 패턴은 단순하지만 매우 중요합니다. 객체의 모든 키를 타입 유니언으로 바꾸는 기본 도구입니다.

 

실무 포인트 정리

  • Mapped Types는 “순회”의 역할이다
  • 객체 구조를 타입으로 그대로 가져온다
  • 수동 나열을 제거하는 출발점이다

 

핵심 설계 2: Conditional Types로 중첩 여부를 판단한다

설정 객체는 보통 중첩 구조를 가집니다. 이때 필요한 것이 Conditional Types입니다.

 


type IsObject<T> =
  T extends Record<string, any>
    ? true
    : false;

 

이 조건을 이용해, “여기서 멈출지, 더 내려갈지”를 판단할 수 있습니다.

 

핵심 설계 3: Template Literal Types로 키를 조합한다

이제 앞에서 배운 Template Literal Types를 결합합니다.

 


type DotJoin<A, B> =
  A extends string
    ? B extends string
      ? `${A}.${B}`
      : never
    : never;

 

이 타입은 “부모 키 + 자식 키”를 문자열 규칙으로 안전하게 연결합니다.

 

핵심 설계 4: 모든 것을 조합해 키를 자동 생성한다

이제 모든 도구를 하나로 묶습니다.

 


type DeepKeys<T> = {
  [K in keyof T]:
    IsObject<T[K]> extends true
      ? DotJoin<K, DeepKeys<T[K]>>
      : K;
}[keyof T];

 

이 타입은 다음을 보장합니다.

  • 설정 객체 구조와 문자열 키가 항상 일치한다
  • 키 추가/삭제 시 타입이 자동으로 갱신된다
  • 사람이 직접 문자열을 관리하지 않는다

 

코드 예제: 실무에서 바로 쓰는 설정 키 패턴

이제 실제 설정 객체에 적용해봅니다.

 


const config = {
  user: {
    enabled: true,
    beta: false,
  },
  order: {
    enabled: true,
  },
};

type ConfigKey = DeepKeys<typeof config>;

 

ConfigKey는 자동으로 다음 유니언이 됩니다.

  • user.enabled
  • user.beta
  • order.enabled

 

이 구조에서는 설정과 타입이 어긋날 여지가 없습니다.

 

운영/실무에서 자주 겪는 문제

  • 중첩이 너무 깊어 타입 에러 메시지가 길어지는 경우
  • 모든 객체를 재귀 대상으로 처리한 경우
  • 설정보다 타입이 먼저 이해되어야 하는 구조
  • 팀원이 패턴을 수정하지 못하는 상황

 

이 패턴은 강력하지만, “설정 키처럼 반드시 자동화해야 하는 영역”에만 쓰는 것이 중요합니다.

 

실무 권장 체크리스트

  • 문자열 키가 객체 구조와 1:1로 대응되는가
  • 수동 문자열 관리로 인한 버그가 실제로 있었는가
  • 재귀 깊이가 합리적인 수준인가
  • 타입 정의를 팀원이 이해할 수 있는가
  • 이 패턴이 중복 정의를 실제로 제거하는가

 


 

Mapped Types와 Conditional Types의 목적은 타입을 복잡하게 만드는 것이 아닙니다.

중복을 제거하고, 규칙을 한 곳에 고정하며, 실수를 구조적으로 불가능하게 만드는 것. 이 조건이 만족될 때, 문자열 키 자동 생성 패턴은 실무에서 훌륭한 도구가 됩니다.