TypeScript 4.4 template literal type으로 타입 안전성 높이기
문제 상황
프로젝트에서 RESTful API 엔드포인트를 string으로 관리하다 보니 오타로 인한 404 에러가 종종 발생했다. /api/users/:id 같은 동적 경로는 더 문제였다.
// 기존 방식
const endpoint = `/api/users/${userId}`; // 오타 가능성
fetch(endpoint);
Template Literal Types 활용
TypeScript 4.1부터 지원된 template literal type을 4.4에서 더 강화된 방식으로 활용했다.
type ApiVersion = 'v1' | 'v2';
type Resource = 'users' | 'posts' | 'comments';
type ApiPath = `/api/${ApiVersion}/${Resource}`;
// 자동완성 지원되고 타입 체크됨
const path: ApiPath = '/api/v1/users'; // OK
const invalid: ApiPath = '/api/v3/users'; // Error
동적 ID가 필요한 경우도 처리 가능하다.
type IdPath<T extends string> = `${T}/${number}`;
type UserIdPath = IdPath<'/api/v1/users'>;
// 타입 가드와 함께 사용
function fetchUser(path: UserIdPath) {
return fetch(path);
}
실용적인 API 클라이언트
실제 프로젝트에 적용한 래퍼 함수다.
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = `/api/${string}`;
class ApiClient {
async request<T>(
method: HttpMethod,
endpoint: Endpoint,
data?: unknown
): Promise<T> {
const response = await fetch(endpoint, {
method,
headers: { 'Content-Type': 'application/json' },
body: data ? JSON.stringify(data) : undefined,
});
return response.json();
}
}
const api = new ApiClient();
api.request('GET', '/api/users/123'); // OK
api.request('GET', '/wrong/path'); // Error
결과
- IDE에서 자동완성으로 엔드포인트 입력 편의성 증가
- 컴파일 타임에 잘못된 경로 차단
- 리팩토링 시 타입 에러로 누락 방지
아직 복잡한 쿼리 파라미터까지는 타이핑하지 않았지만, 기본적인 경로 검증만으로도 충분히 효과를 봤다. TypeScript 4.4의 개선된 타입 추론 덕분에 이전보다 안정적으로 동작한다.