TypeScript
Template Literal Types — 라우트 타입과 form key 안전하게 잡기
2026.05.14· 1분 읽기· 1· 0
리터럴 유니온을 조립·분해해 라우트 경로, URL 파라미터, form 필드 키를 컴파일 타임에 보장합니다. 라우트 헬퍼 한 번 만들어 두면 모든 호출이 자동 검증됩니다.
TypeScript의 Template Literal Types는 문자열을 타입 수준에서 조립·분해할 수 있게 해줍니다. 단순한 기능 같지만, 라우트 경로와 form 필드 키처럼 "정해진 형식의 문자열"을 다루는 곳에서 매우 유용합니다.
1. 기본 동작
리터럴 유니온을 합쳐서 새로운 리터럴 유니온을 만듭니다.
1type Lang = 'ko' | 'en'; 2type Page = 'home' | 'blog'; 3 4type Route = `/${Lang}/${Page}`; 5// '/ko/home' | '/ko/blog' | '/en/home' | '/en/blog'
Route 변수에 '/ko/about' 같은 값을 넣으면 컴파일 타임에 막힙니다.
2. 라우트 파라미터 추출
infer와 함께 쓰면 동적 세그먼트도 추출할 수 있습니다.
1type ExtractParam<P extends string> = 2 P extends `${string}/:${infer Param}/${infer Rest}` 3 ? Param | ExtractParam<`/${Rest}`> 4 : P extends `${string}/:${infer Param}` 5 ? Param 6 : never; 7 8type P1 = ExtractParam<'/blog/:id'>; // 'id' 9type P2 = ExtractParam<'/users/:userId/posts/:postId'>; 10// 'userId' | 'postId'
이 타입을 사용하면 라우트 헬퍼 함수가 누락된 파라미터를 컴파일 타임에 잡습니다.
1type Params<P extends string> = Record<ExtractParam<P>, string | number>; 2 3function buildPath<P extends string>(path: P, params: Params<P>): string { 4 return path.replace(/:(\w+)/g, (_, k) => String(params[k as keyof typeof params])); 5} 6 7buildPath('/blog/:id', { id: 26 }); 8// 'userId' 빠뜨리면 컴파일 에러 9buildPath('/users/:userId/posts/:postId', { userId: 1, postId: 2 });
3. form key 자동 추론
form 필드가 늘어날 때마다 매번 keyof 하기보다, 객체 모양을 그대로 활용합니다.
1type LoginForm = { email: string; password: string }; 2 3type FieldName<T> = keyof T & string; 4type ErrorMap<T> = Partial<Record<FieldName<T>, string>>; 5 6const errors: ErrorMap<LoginForm> = { 7 email: '이메일 형식이 아닙니다', 8}; 9 10errors.email; // string | undefined 11errors.unknown; // 컴파일 에러
중첩 form도 비슷하게 — ${Prefix}.${Key} 패턴으로 평탄화한 키 타입을 만들 수 있습니다.
1type Path<T, P extends string = ''> = 2 T extends object 3 ? { [K in keyof T & string]: Path<T[K], `${P}${P extends '' ? '' : '.'}${K}`> }[keyof T & string] 4 : P; 5 6type Profile = { name: string; address: { city: string; zip: string } }; 7type ProfilePath = Path<Profile>; 8// 'name' | 'address.city' | 'address.zip'
react-hook-form 같은 라이브러리가 내부에서 비슷한 패턴을 사용합니다.
4. CSS 색상 / 토큰 검증
디자인 토큰처럼 형식이 정해진 문자열도 강제할 수 있습니다.
1type Hex = `#${string}`; 2type CSSVar = `var(--${string})`; 3 4const a: Hex = '#8b5cf6'; // OK 5const b: Hex = 'red'; // 에러 6const c: CSSVar = 'var(--accent)';
너무 강하게 잡으면 사용성이 떨어지므로, 핵심 토큰에만 적용합니다.
5. 주의할 점
- 깊은 재귀(중첩 5단계 이상)는 컴파일 시간이 길어지고 추론이 멈출 수 있습니다.
${string}는 거의 모든 문자열을 허용해서 너무 느슨할 수 있습니다. 가능하면 리터럴 유니온으로 좁힙니다.- Template Literal로 만든 타입을 런타임에서 사용하려면 별도의 검증 함수가 필요합니다(타입은 컴파일 타임만).
6. 정리
| 활용 | 효과 |
|---|---|
| 라우트 경로 | 잘못된 경로 컴파일 차단 |
| URL 파라미터 추출 | 누락된 파라미터 컴파일 에러 |
| form 키 / 에러 맵 | 오타 차단, 자동 추천 |
| 토큰 형식 강제 | #, var(--) 같은 prefix 보장 |
라우트 헬퍼 하나만 이 패턴으로 만들어 두면 그 뒤 모든 라우트 호출이 컴파일러로 검증됩니다. 작은 한 번의 투자로 큰 안정성을 얻습니다.
참고: TypeScript Handbook — Template Literal Types.
0
이 글이 도움이 되셨나요?