새로운 프로젝트에서 백엔드 팀이 Swagger 문서를 제공했지만, 서버 구현은 진행 중이고 한 달 내에 모든 기능을 완성해야 하는 상황이었습니다.
빡빡한 일정 속에서 API 타입을 수동으로 정의할 시간이 없었고, 동료 개발자가 openapi-typescript 사용을 권유했습니다. "Swagger 문서만 있으면 바로 TypeScript 타입을 생성할 수 있어서 개발 속도를 크게 높일 수 있다"는 것이 핵심 이유였습니다.
짧은 기간 내에 개발 시간을 단축하면서도 코드 타입 안전성을 확보할 수 있는 최적의 선택이었습니다.
먼저 OpenAPI와 Swagger의 관계를 이해해야 합니다.
"The OpenAPI Specification defines a standard interface to RESTful APIs which allows both humans and computers to understand service capabilities"
"OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs... Swagger UI – renders OpenAPI definitions as interactive documentation."
즉, Swagger UI는 OpenAPI 스펙을 사람이 읽기 쉽게 보여주는 인터페이스일 뿐이고, 실제 데이터는 YAML 또는 JSON 형식의 OpenAPI 스펙 파일에 있습니다.
Swagger UI (시각화 도구)
↓
OpenAPI Specification (YAML/JSON 스펙)
↓
openapi-typescript (변환 라이브러리)
↓
TypeScript Types (.d.ts 파일)
"openapi-typescript turns OpenAPI 3.0 & 3.1 schemas into TypeScript quickly using Node.js"
openapi-typescript는 OpenAPI 3.0 및 3.1 스키마를 TypeScript로 신속하게 변환하며, 로컬 YAML/JSON 파일과 원격 스키마를 모두 지원합니다.
Swagger UI에서 실제 OpenAPI 스펙 파일 URL을 어디서 확인할 수 있을까요?
대부분의 Swagger UI 페이지 상단에 스펙 파일 URL이 표시됩니다.
백엔드 서버 URL에 Swagger input창에 있는 spec endpoint를 붙이면 됩니다.
api-docs
, swagger.json
, openapi.json
, v3/api-docs
등의 요청 찾기프로젝트에서는 Spring Boot를 사용했기 때문에 v3/api-docs
엔드포인트를 사용했습니다.
# openapi-typescript 설치
npm install -D openapi-typescript
# 타입 생성 스크립트 추가 (package.json)
{
"scripts": {
"generate-types": "openapi-typescript {OpenAPI 스펙 URL} -o {타입을 저장할 문서의 상대 경로}",
}
}
openapi-typescript로 생성된 타입 파일은 주요 3가지 구조로 구성됩니다:
import type { paths, components, operations } from './types/api';
1. paths
: API 엔드포인트별 타입
/users
, /projects
등)의 HTTP 메서드별 요청/응답 타입2. components
: 재사용 가능한 스키마 타입
components.schemas
에 정의된 모델 타입User
, Project
같은 데이터 객체 타입들3. operations
: 각 API 작업별 타입
operationId
로 정의된 개별 API 호출의 전체 타입 정보실제 사용 예시:
// 스키마 타입 사용
type User = components['schemas']['User'];
type CreateUserRequest = components['schemas']['CreateUserRequest'];
// 엔드포인트별 타입 사용
type GetUsersResponse = paths['/api/users']['get']['responses'][200]['content']['application/json'];
type GetUserParams = paths['/api/users/{id}']['get']['parameters'];
이렇게 생성된 타입들을 활용하면 API 호출 시 자동완성과 타입 검증을 받을 수 있어 개발 효율성이 크게 향상됩니다.
가장 크게 느낀 장점은 타입 안전성 확보입니다.
프로젝트를 하다보면, 서버 API 구현이 완전히 픽스된 후 프론트엔드 개발을 하는 경우는 정말 드뭅니다. 완성되었다 하더라도 변경 사항이 있기 마련입니다. 이전 팀 프로젝트에서도 API 변경 사항이 백엔드와 프론트엔드간 공유가 잘 되지 않아 QA시 오류가 생긴 일도 있었습니다.
그러나 openapi-typescript를 사용하면, 프론트엔드는 라이브러리 실행만으로도 API 변경 사항을 체크할 수 있습니다.
👍 타입 안전성 확보
1. 복잡한 타입 구조
// 유틸리티 타입으로 복잡한 구조 단순화
type ApiResponse<T extends keyof paths, M extends keyof paths[T]> =
paths[T][M] extends { responses: { 200: { content: { 'application/json': infer R } } } }
? R
: never;
type UserListResponse = ApiResponse<'/users', 'get'>;
2. 수동 실행의 한계와 타입 동기화 문제
라이브러리를 사용하면서 가장 아쉬웠던 점은 수동으로 openapi-typescript를 실행해야만 최신 API 문서가 반영된다는 것이었습니다.
실제로 겪었던 문제들:
이런 상황들을 겪으면서 API 변경 여부를 몰라서 openapi-typescript를 재실행하지 않는다면 최신 API 문서를 반영하는 데 한계가 있다는 것을 깨달았습니다.
해결방법: CI/CD 통합을 통한 자동화
이 문제를 해결하기 위해 CI/CD 파이프라인에 openapi-typescript 실행을 통합하는 방안을 고려하게 되었습니다. (아직은 도입하지 않았지만, 기능이 추가된다면 팀에 도입을 제안할 예정입니다.)
CI 과정에서 실행하면, 아래와 같은 이점을 얻을 수 있습니다.
3. 백엔드 스펙 의존성과 에러 응답 타입 관리
openapi-typescript의 또 다른 아쉬운 점은 HTTP 상태 코드별 정확한 타입을 받으려면 백엔드에서 OpenAPI 스펙을 상세하게 작성해야 한다는 것입니다. 그러나 현실에서 타이트한 일정 속에서 상세한 문서 작성이 이루어지지 않았고, 특정한 타입이 필요한 경우 백엔드와 소통을 하면서 타입을 구체화했습니다.
openapi-typescript를 사용해 swagger 문서만으로 타입 안전한 개발 환경을 구축할 수 있어 개발 속도를 크게 높일 수 있었습니다. 자동화를 통해 휴먼 에러를 줄여 개발 생산성과 안전성을 높일 수 있었습니다.
앞으로도 API 스펙이 있는 프로젝트에서는 openapi-typescript를 적극 활용할 계획입니다. 특히 CI/CD 파이프라인과 통합하여 더욱 안전하고 효율적인 개발 환경을 구축하고 싶습니다.
🔖 출처 자료
와웅 신기신기하네용