swagger-client-autogen
v0.2.4
Published
Swagger/OpenAPI에서 TypeScript API 클라이언트 코드를 자동 생성하는 CLI 도구
Maintainers
Readme
🚀 swagger-client-autogen
Swagger/OpenAPI에서 TypeScript API 클라이언트 코드를 자동 생성하는 CLI 도구
🎯 한 번의 명령어로 완전한 TypeScript API 클라이언트를 생성하세요!
✨ 주요 기능
- 🚀 인터랙티브 초기화 -
init명령어로 빠른 설정 - 📝 TypeScript 완벽 지원 - 완전한 타입 안정성
- ⚡ Ky HTTP 클라이언트 - 현대적이고 가벼운 HTTP 클라이언트
- 🔄 TanStack Query 통합 - React Query hooks 자동 생성
- 🎯 전역 Mutation Effects - 중앙화된 쿼리 무효화 관리
- 🛡️ Zod 스키마 생성 - 런타임 타입 검증 (선택사항)
- 📁 모듈러 구조 - 태그별 코드 분리
📋 목차
🚀 Quick Start
설치
# 프로젝트에 개발 의존성으로 설치 (권장)
npm install -D swagger-client-autogen
# 또는 yarn
yarn add -D swagger-client-autogen[!TIP]
💡 개발 의존성 설치를 권장하는 이유:
🎯 타입 지원: 설정 파일 작성 시InputCodegenConfig타입 힌트 및 자동완성
📋 package.json 스크립트 활용 (선택사항):
{
"scripts": {
"api:init": "swagger-client-autogen init",
"api:fetch": "swagger-client-autogen fetch --config swagger/config.ts --output swagger/api.yml",
"api:generate": "swagger-client-autogen generate --config swagger/config.ts"
}
}그러면 npm run api:generate로 간편하게 실행할 수 있습니다.
초기화 (init)
🎯 인터랙티브 설정으로 빠른 시작!
# 1. 프로젝트 루트에서 초기화 실행
npx swagger-client-autogen init대화형 질문에 답변하세요:
🚀 swagger-client-autogen 초기화
💡 지원하는 입력 형식
│ 로컬 파일: ./swagger.yml, api/swagger.json
│ 원격 URL: https://api.example.com/swagger.json
│ 개발 서버: http://localhost:3000/api-docs
📋 Swagger JSON/YAML 경로: https://api.example.com/swagger.json
🔒 인증이 필요한 API인가요? No
📝 Zod 스키마를 생성할까요? Yes
📂 Config 파일명: swagger/config.tsAPI 클라이언트 생성
# 2. Swagger 파일 다운로드 및 병합
npx swagger-client-autogen fetch --config swagger/config.ts --output swagger/api.yml
# 3. API 클라이언트 코드 생성
npx swagger-client-autogen generate --config swagger/config.ts생성된 파일들
src/
├── shared/api/ # 공통 API 파일들
│ ├── dto.ts # TypeScript 타입 정의
│ ├── schema.gen.ts # Zod 스키마 (선택)
│ ├── utils.gen.ts # API 유틸리티
│ ├── stream.gen.ts # 스트림 유틸리티
│ └── type-guards.gen.ts # 타입 가드
└── entities/{moduleName}/api/ # 모듈별 API 파일들
├── index.ts # API 클라이언트 클래스
├── instance.ts # API 클라이언트 인스턴스
├── queries.ts # TanStack Query hooks
└── mutations.ts # TanStack Query mutations✨ 바로 사용 가능한 코드가 생성됩니다!
import { useGetUsersQuery, usePostUsersMutation } from '@/entities/users/api';
// GET 요청 - Query Hook
const { data: users, isLoading } = useGetUsersQuery();
// POST 요청 - Mutation Hook
const createUserMutation = usePostUsersMutation({
onSuccess: (data) => {
console.log('사용자 생성 성공:', data);
}
});
// 뮤테이션 실행
createUserMutation.mutate({
payload: { name: 'John', email: '[email protected]' },
});📖 사용법
CLI 명령어
init - 초기화
npx swagger-client-autogen init인터랙티브 방식으로 설정 파일을 생성합니다. 이곳에서 생성된 설정 파일은 fetch, generate 명령에서 사용됩니다.
fetch - Swagger 파일 다운로드
npx swagger-client-autogen fetch --config swagger/config.ts --output swagger/api.ymlConfig 파일에서 지정된 웹 URL로부터 Swagger 파일을 다운로드하고 병합합니다.
옵션:
--config, -c: 설정 파일 경로 (필수)--output, -o: 출력 파일 경로 (선택, 기본값:swagger/{title}.yml)
generate - 코드 생성
npx swagger-client-autogen generate --config swagger/config.tsAPI 클라이언트 코드를 생성합니다.
도움말
npx swagger-client-autogen --help
npx swagger-client-autogen init --help설정 파일
init 명령어로 생성되는 설정 파일 예시:
// swagger/config.ts
import type { InputCodegenConfig } from 'swagger-client-autogen';
const config: InputCodegenConfig = {
// Swagger 설정
uri: 'https://api.example.com/swagger.json',
// 인증 정보 (선택)
username: 'your-username',
password: 'your-password',
// 스키마 생성 여부
createSchema: true,
// 출력 설정 (필요에 따라 주석 해제하여 사용)
/*
customOutput: {
aliasInfo: {
aliasMap: { '@': 'src' }, // path alias 설정
aliasMapDepth: 2, // alias 탐색 깊이
},
pathInfo: {
dto: 'src/shared/api/dto.ts',
api: 'src/entities/{moduleName}/api/index.ts',
apiInstance: 'src/entities/{moduleName}/api/instance.ts',
queries: 'src/entities/{moduleName}/api/queries.ts',
mutations: 'src/entities/{moduleName}/api/mutations.ts',
schema: 'src/shared/api/schema.gen.ts',
apiUtils: 'src/shared/api/utils.gen.ts',
streamUtils: 'src/shared/api/stream.gen.ts',
typeGuards: 'src/shared/api/type-guards.gen.ts',
streamHandlers: 'src/entities/{moduleName}/api/stream-handlers',
},
},
*/
};
export default config;⚙️ 설정 옵션
기본 설정
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| uri | string | - | Swagger JSON/YAML 파일 경로 또는 URL |
| createSchema | boolean | false | Zod 스키마 생성 여부 |
인증 설정
swagger에 인증 정보가 필요한 경우에만 입력해주세요.
{
// 최상위 레벨에 설정
username: 'your-username',
password: 'your-password'
}고급 옵션 (customOutput)
{
customOutput: {
aliasInfo: {
aliasMap: { '@': 'src' }, // path alias 설정
aliasMapDepth: 2 // alias 탐색 깊이
},
pathInfo: {
dto: 'src/shared/api/dto.ts',
api: 'src/entities/{moduleName}/api/index.ts',
apiInstance: 'src/entities/{moduleName}/api/instance.ts',
queries: 'src/entities/{moduleName}/api/queries.ts',
mutations: 'src/entities/{moduleName}/api/mutations.ts',
schema: 'src/shared/api/schema.gen.ts',
// ... 기타 경로 설정
}
}
}💡 개선된 점:
pathInfo에서alias필드 제거 - 중복 제거!aliasMap을 기반으로 alias 자동 계산- 더 간결하고 DRY한 설정 구조
Swagger x- 프로퍼티 (커스텀 확장)
Swagger 스펙에 x- 프로퍼티를 추가하여 코드 생성을 세밀하게 제어할 수 있습니다.
x-ignore - 엔드포인트 무시
특정 엔드포인트의 코드 생성을 건너뛸 수 있습니다.
paths:
/health:
x-ignore: true # 이 경로의 모든 메서드 무시
get:
summary: Health Check
# ... (코드 생성되지 않음)
/users:
get:
x-ignore: true # GET /users만 무시
# ...
post:
# POST /users는 정상적으로 생성됨
# ...x-staleTime - TanStack Query staleTime 설정
Query의 staleTime을 엔드포인트별로 설정할 수 있습니다.
paths:
/users:
get:
x-staleTime: '5m' # 5분
summary: Get Users
# ...
/config:
get:
x-staleTime: 'Infinity' # 무한대 (수동 무효화 전까지 fresh 유지)
summary: Get Config
# ...
/static-data:
get:
x-staleTime: 'static' # 절대 stale로 간주되지 않음 (함수로 사용 시 항상 0 반환)
summary: Get Static Data
# ...지원 형식:
숫자: 밀리초 단위 (예:300000)시간 단위:'5h','30m','45s','1h30m45s''Infinity': 무한대 (수동 무효화 전까지 fresh 유지)'static': 절대 stale로 간주되지 않음 (함수로 사용 시 항상 0 반환)
생성되는 코드:
const queries = {
getUsers: ({ kyInstance, options }) => queryOptions({
queryKey: USERS_QUERY_KEY.GET_USERS(),
queryFn: () => usersApi.getUsers({ kyInstance, options }),
staleTime: 5 * 60 * 1000 // 5분
}),
};x-gcTime - TanStack Query gcTime 설정
Query의 gcTime (가비지 컬렉션 시간)을 엔드포인트별로 설정할 수 있습니다.
paths:
/users:
get:
x-gcTime: '10m' # 10분
summary: Get Users
# ...
/permanent-cache:
get:
x-gcTime: 'Infinity' # 가비지 컬렉션 비활성화
summary: Get Permanent Data
# ...지원 형식:
숫자: 밀리초 단위시간 단위:'5h','30m','45s','1h30m45s''Infinity': 가비지 컬렉션 비활성화
생성되는 코드:
const queries = {
getUsers: ({ kyInstance, options }) => queryOptions({
queryKey: USERS_QUERY_KEY.GET_USERS(),
queryFn: () => usersApi.getUsers({ kyInstance, options }),
gcTime: 10 * 60 * 1000 // 10분
}),
};x- 프로퍼티 병합
fetch 명령어를 실행할 때 기존 파일의 x- 프로퍼티가 자동으로 유지됩니다.
npx swagger-client-autogen fetch --config swagger/config.ts유지된 x- 프로퍼티:
┌─────────┬──────────────┬──────────┬─────────────────────────────┐
│ (index) │ 엔드포인트 │ 메서드 │ x- 프로퍼티 │
├─────────┼──────────────┼──────────┼─────────────────────────────┤
│ 0 │ '/health' │ '-' │ 'x-ignore' │
│ 1 │ '/users' │ 'GET' │ 'x-staleTime, x-gcTime' │
└─────────┴──────────────┴──────────┴─────────────────────────────┘🎯 생성되는 코드
API 클라이언트 클래스
// src/entities/user/api/index.ts
import type { KyInstance, Options } from 'ky';
import { z } from 'zod';
import type { GetUserResponseDto, CreateUserRequestDto, CreateUserResponseDto } from '@/shared/api/dto';
import { getUserResponseDtoSchema, createUserRequestDtoSchema, createUserResponseDtoSchema } from '@/shared/api/schema.gen';
import { validateSchema } from '@/shared/api/utils.gen';
export class UserApi {
private readonly instance: KyInstance;
constructor(instance: KyInstance) {
this.instance = instance;
}
/**
* @tags users
* @summary Get User
* @request GET:/users/{id}
*/
async getUser({
id,
kyInstance,
options,
}: TUserApiRequestParameters['getUser']) {
const instance = kyInstance ?? this.instance;
const response = await instance
.get<GetUserResponseDto>(`users/${id}`, {
...options,
})
.json();
const validateResponse = validateSchema(getUserResponseDtoSchema, response);
return validateResponse;
}
/**
* @tags users
* @summary Create User
* @request POST:/users
*/
async createUser({
payload,
kyInstance,
options,
}: TUserApiRequestParameters['createUser']) {
const instance = kyInstance ?? this.instance;
const validatedPayload = validateSchema(createUserRequestDtoSchema, payload);
const response = await instance
.post<CreateUserResponseDto>(`users`, {
json: validatedPayload,
...options,
})
.json();
const validateResponse = validateSchema(createUserResponseDtoSchema, response);
return validateResponse;
}
}
export type TUserApiRequestParameters = {
getUser: {
id: number;
kyInstance?: KyInstance;
options?: Options;
};
createUser: {
payload: CreateUserRequestDto;
kyInstance?: KyInstance;
options?: Options;
};
};API 클라이언트 인스턴스
// src/entities/user/api/instance.ts
import { UserApi } from './index';
// API 클래스의 인스턴스를 생성하여 내보냄
export const userApi = new UserApi();TypeScript 타입
// src/shared/api/dto.ts
export type GetUserResponseDto = {
id: number;
name: string;
email: string;
createdAt: string;
}
export type CreateUserRequestDto = {
name: string;
email: string;
}Zod 스키마
// src/shared/api/schema.gen.ts
import { z } from 'zod';
export const GetUserResponseSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
createdAt: z.string()
});
export const CreateUserRequestSchema = z.object({
name: z.string().min(1),
email: z.string().email()
});TanStack Query Hooks
📋 queries.ts - 쿼리 훅
// src/entities/users/api/queries.ts
import type { DefaultError, UseQueryOptions } from '@tanstack/react-query';
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import type { UserResponseDto, UnknownConceptReadResponseDto } from '@/shared/api/dto';
import type { TUsersApiRequestParameters } from './index';
import { usersApi } from './instance';
// Query Keys
export const USERS_QUERY_KEY = {
GET_USERS: () => ['users'],
GET_USERS_USERID: (userId: string) => ['users', userId],
GET_USERS_USERID_UNKNOWN_CONCEPTS: (userId: string, params?: any) =>
['users', userId, 'unknown_concepts', params],
};
// Query 객체 (재사용 가능)
const queries = {
getUsers: ({ kyInstance, options }: TUsersApiRequestParameters['getUsers']) => ({
queryKey: USERS_QUERY_KEY.GET_USERS(),
queryFn: () => usersApi.getUsers({ kyInstance, options }),
}),
getUsersByUserId: ({ userId, kyInstance, options }: TUsersApiRequestParameters['getUsersByUserId']) => ({
queryKey: USERS_QUERY_KEY.GET_USERS_USERID(userId),
queryFn: () => usersApi.getUsersByUserId({ userId, kyInstance, options }),
}),
};
export { queries as usersQueries };
// Query Hooks
export const useGetUsersQuery = <TData = UserResponseDto[]>(
requestArgs: TUsersApiRequestParameters['getUsers'],
options?: Omit<UseQueryOptions<UserResponseDto[], DefaultError, TData>, 'queryKey' | 'queryFn'>,
) => {
return useQuery({
...queries.getUsers(requestArgs),
...options,
});
};
// Suspense Query Hooks
export const useGetUsersSuspenseQuery = <TData = UserResponseDto[]>(
requestArgs: TUsersApiRequestParameters['getUsers'],
options?: Omit<UseQueryOptions<UserResponseDto[], DefaultError, TData>, 'queryKey' | 'queryFn'>,
) => {
return useSuspenseQuery({
...queries.getUsers(requestArgs),
...options,
});
};🔄 mutations.ts - 뮤테이션 훅
// src/entities/users/api/mutations.ts
import type { DefaultError, UseMutationOptions } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { UserResponseDto } from '@/shared/api/dto';
import type { TUsersApiRequestParameters } from './index';
import { usersApi } from './instance';
// Mutation Keys
export const USERS_MUTATION_KEY = {
POST_USERS: ['users'] as const,
DELETE_USERS_USERID: ['users', 'userId'] as const,
};
// Mutation 객체 (재사용 가능)
const mutations = {
postUsers: () => ({
mutationFn: ({ payload, kyInstance, options }: TUsersApiRequestParameters['postUsers']) => {
return usersApi.postUsers({ payload, kyInstance, options });
},
mutationKey: USERS_MUTATION_KEY.POST_USERS,
meta: {
mutationFnName: 'postUsers',
}
}),
};
export { mutations as usersMutations };
// Mutation Hooks
export const usePostUsersMutation = <TContext = unknown>(
options?: Omit<
UseMutationOptions<UserResponseDto, DefaultError, TUsersApiRequestParameters['postUsers'], TContext>,
'mutationFn' | 'mutationKey'
>,
) => {
return useMutation({
...mutations.postUsers(),
...options,
});
};🔄 전역 Mutation Effects
swagger-client-autogen은 TanStack Query의 mutation 성공 시 전역적으로 쿼리를 무효화하거나 특정 로직을 실행할 수 있는 Global Mutation Effect 시스템을 제공합니다.
주요 이점
- 🎯 중앙화된 부수 효과 관리: 모든 mutation의 부수 효과를 한 곳에서 관리
- 🔄 자동 쿼리 무효화: mutation 성공 시 관련 쿼리를 자동으로 무효화
- 🛡️ 타입 안정성: 완전한 TypeScript 타입 지원
- 🎨 선택적 적용: 필요한 mutation만 선택적으로 전역 효과 적용
자동 생성되는 타입 파일
generate 명령어를 실행하면 global-mutation-effect.type.ts 파일이 자동으로 생성됩니다:
// src/shared/api/global-mutation-effect.type.ts
export type GlobalMutationEffectMap<M extends MutationMap> = Partial<{
[K in keyof M]: {
onSuccess: {
invalidate: (
data: ExtractMutationData<M, K>,
variables: ExtractMutationVariables<M, K>,
context: unknown,
mutation: Mutation<...>,
) => void;
};
};
}>;
// 각 모듈별 타입
export type TUsersGlobalMutationEffects = GlobalMutationEffectMap<typeof usersMutations>;
export type TChatsGlobalMutationEffects = GlobalMutationEffectMap<typeof chatsMutations>;
// ...
// 통합 팩토리 타입
export type TGlobalMutationEffectFactory = (
queryClient: QueryClient,
) => Partial<
TUsersGlobalMutationEffects &
TChatsGlobalMutationEffects &
// ...
>;사용 예시
1. 전역 Mutation Effects 구현
// src/shared/api/global-mutation-effects.ts
import type { QueryClient } from '@tanstack/react-query';
import { queryClient } from '@/app/provider/tanstack-query';
import { usersQueries } from '@/entities/users/api/queries';
import type {
TUsersGlobalMutationEffects,
TGlobalMutationEffectFactory,
} from './global-mutation-effect.type';
export const globalMutationEffects: TGlobalMutationEffectFactory = (queryClient) => ({
...userGlobalMutationEffects(queryClient),
// 다른 모듈의 effects도 여기에 추가
});
export const isGlobalMutationEffectKey = (
key: unknown
): key is keyof ReturnType<typeof globalMutationEffects> => {
return typeof key === 'string' &&
Object.keys(globalMutationEffects(queryClient)).includes(key);
};
function userGlobalMutationEffects(
queryClient: QueryClient
): TUsersGlobalMutationEffects {
return {
// mutation 함수명을 키로 사용
postUsers: {
onSuccess: {
invalidate: (_data, variables) => {
// 관련 쿼리 무효화
queryClient.invalidateQueries({
queryKey: usersQueries.getUsers({}).queryKey,
exact: true,
});
},
},
},
};
}2. TanStack Query Provider 설정
// src/app/provider/tanstack-query.tsx
import { MutationCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { globalMutationEffects, isGlobalMutationEffectKey } from '@/shared/api/global-mutation-effects';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: (failureCount, error) => {
return failureCount < 2;
},
},
},
mutationCache: new MutationCache({
onSuccess: async (_data, _variables, _context, mutation) => {
const disableGlobalInvalidation = mutation.options.meta?.disableGlobalInvalidation;
const mutationFnName = mutation.options.meta?.mutationFnName;
// 전역 무효화 비활성화 체크
if (disableGlobalInvalidation) {
return;
}
// 전역 mutation effect 실행
if (isGlobalMutationEffectKey(mutationFnName)) {
const invalidate = globalMutationEffects(queryClient)[mutationFnName]?.onSuccess.invalidate;
if (invalidate) {
invalidate(_data as never, _variables as never, _context as never, mutation as never);
return; // 전역 효과를 실행했으면 entity 단위 무효화는 하지 않음
}
}
// entity 단위 기본 무효화 (전역 효과가 없는 경우)
const mutationKey = mutation.options.mutationKey;
if (!mutationKey) return;
await queryClient.invalidateQueries({
queryKey: [mutationKey?.at(0)],
exact: false,
});
},
}),
});
export const TanstackQueryProvider = ({ children }: PropsWithChildren) => {
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
};3. 전역 무효화 비활성화 (선택적)
특정 mutation 호출 시 전역 무효화를 비활성화하려면:
const mutation = usePostUsersMutation({
meta: {
disableGlobalInvalidation: true, // 전역 무효화 비활성화
},
onSuccess: (data) => {
// 커스텀 로직만 실행
},
});자세한 가이드
전역 Mutation Effects 시스템에 대한 자세한 설명은 다음 문서를 참고하세요:
이 가이드에서는 다음 내용을 다룹니다:
- 아키텍처 상세 설명
- 작동 원리
- 다양한 사용 패턴
- 모범 사례
- 트러블슈팅
🔧 개발 가이드
프로젝트 구조
swagger-client-autogen/
├── scripts/ # CLI 스크립트
│ ├── cli.ts # 메인 CLI
│ ├── init.ts # 초기화 스크립트
│ ├── fetch-swagger.js
│ └── generate-all.js
├── templates/ # 코드 생성 템플릿
├── config-builders/ # 설정 빌더
├── utils/ # 유틸리티 함수
└── types/ # 타입 정의빌드 방법
# 개발 환경 설정
git clone https://github.com/your-org/swagger-client-autogen
cd swagger-client-autogen
yarn install
# 빌드
yarn build
# 개발 모드 (watch)
yarn build:watch기여하기
- 이슈 생성 또는 기존 이슈 확인
- 브랜치 생성:
git checkout -b feature/amazing-feature - 변경사항 커밋:
git commit -m 'Add amazing feature' - 푸시:
git push origin feature/amazing-feature - Pull Request 생성
❓ FAQ
Q: Swagger 파일이 인증이 필요한 경우는?
A: init 명령어 실행 시 인증 정보를 입력하거나, 설정 파일에 직접 추가하세요.
{
username: 'your-username',
password: 'your-password'
}Q: 생성된 코드를 커스터마이징하고 싶다면?
A: 현재는 템플릿 커스터마이징을 지원하지 않습니다. 생성된 코드를 직접 수정하거나, 래퍼 함수를 만들어 사용하세요.
Q: TypeScript 대신 JavaScript로 생성할 수 있나요?
A: TypeScript만 지원합니다.
🐛 트러블슈팅
Swagger 파일 다운로드 실패
Error: Failed to fetch swagger file해결 방법:
- URL이 올바른지 확인
- 인증이 필요한 경우
username,password설정 확인
생성된 코드에서 타입 오류
Type 'unknown' is not assignable to type 'UserDto'해결 방법:
- Swagger 스키마가 올바르게 정의되었는지 확인
📄 라이선스
🚀 Happy Coding!
이 도구가 도움이 되었다면 ⭐ 별표를 눌러주세요!
