TypeScript 4.4 템플릿 리터럴 타입으로 API 엔드포인트 타입 안전하게 관리하기

문제 상황

회사 프로젝트에서 API 엔드포인트를 관리하는 유틸 함수를 만들었는데, 문자열 기반이라 오타가 자주 발생했다.

const getApiUrl = (resource: string, id?: string) => {
  return id ? `/api/${resource}/${id}` : `/api/${resource}`;
};

// 사용
fetch(getApiUrl('usres', '123')); // 오타인데 컴파일 에러 없음

해결 방법

TypeScript 4.4부터 지원하는 템플릿 리터럴 타입을 활용했다.

type Resource = 'users' | 'posts' | 'comments';
type ApiEndpoint<T extends Resource> = `/api/${T}` | `/api/${T}/${string}`;

const getApiUrl = <T extends Resource>(resource: T, id?: string): ApiEndpoint<T> => {
  return (id ? `/api/${resource}/${id}` : `/api/${resource}`) as ApiEndpoint<T>;
};

// 타입 안전성 확보
const url: ApiEndpoint<'users'> = getApiUrl('users', '123'); // OK
const wrongUrl = getApiUrl('usres', '123'); // 컴파일 에러!

추가 개선

HTTP 메서드까지 타입으로 관리하는 방식으로 확장했다.

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

type ApiConfig<T extends Resource> = {
  method: HttpMethod;
  endpoint: ApiEndpoint<T>;
};

const createApiConfig = <T extends Resource>(
  method: HttpMethod,
  resource: T,
  id?: string
): ApiConfig<T> => ({
  method,
  endpoint: getApiUrl(resource, id),
});

결과

오타로 인한 API 호출 실패가 배포 전에 잡히기 시작했다. IDE의 자동완성도 정확해져서 개발 속도도 향상됐다. 템플릿 리터럴 타입은 생각보다 실용적이었다.

TypeScript 4.4 템플릿 리터럴 타입으로 API 엔드포인트 타입 안전하게 관리하기