@g1cloud/api-gen
v1.0.0
Published
CLI tool to generate OpenAPI v3 YAML from Spring Boot Java source code
Downloads
46
Maintainers
Readme
@g1cloud/api-gen
Spring Boot Java 소스 코드에서 OpenAPI v3 YAML 스펙을 생성하는 CLI 도구입니다.
기능
- Java 소스 파싱:
java-parser를 사용하여 Java 파일에서 클래스 및 메서드 정보 추출 - REST Controller 분석:
@RestController,@Controller및 HTTP 매핑 어노테이션 감지 - 파라미터 추출:
@RequestParam,@PathVariable,@RequestHeader,@RequestBody지원 - 응답 분석: 반환 타입 추출 및 응답 스키마 생성
- 보안 어노테이션 파싱:
@PreAuthorize,@PostAuthorize,@Secured어노테이션 분석 - SpEL 표현식 파싱: Spring Security 표현식에서 역할(role) 및 권한(authority) 추출
- DTO 스키마 생성: 요청/응답 DTO에 대한 스키마 재귀적 생성
- 특수 타입 처리:
SearchParam: 페이지네이션 파라미터 자동 추가 (offset, limit, filter, sort)PaginatedList<T>: 페이지네이션 헤더 추가 (X-Total-Count, X-Offset, X-Limit)
설치
# 의존성 설치
pnpm install
# 프로젝트 빌드
pnpm build
# 또는 ts-node로 직접 실행
pnpm dev -- --source ./examples --output ./test-output.yaml사용법
# 기본 사용법 - 단일 통합 YAML 파일 생성
npx spring-to-openapi --source ./src/main/java --output ./openapi.yaml
# 컨트롤러별 모드 - 각 컨트롤러마다 별도 YAML 파일 생성
npx spring-to-openapi --source ./src/main/java --out-dir ./api-docs
# 모든 옵션 사용
npx spring-to-openapi \
--source ./src/main/java \
--output ./openapi.yaml \
--title "My API" \
--api-version "1.0.0" \
--base-path "/api"CLI 옵션
| 옵션 | 단축 | 설명 | 기본값 |
|--------|-------|-------------|---------|
| --source | -s | Java 소스 코드 디렉토리 경로 | (필수) |
| --output | -o | 출력 YAML 파일 경로 (단일 통합 파일) | openapi.yaml |
| --out-dir | -d | 출력 디렉토리 경로 (컨트롤러별 YAML 생성) | (없음) |
| --title | -t | API 제목 | API Documentation |
| --api-version | | API 버전 | 1.0.0 |
| --base-path | -b | API 기본 경로 | (없음) |
참고:
--output과--out-dir은 상호 배타적입니다. 단일 통합 파일은--output을, 컨트롤러별 YAML 파일 생성은--out-dir을 사용하세요.
예제
입력: Java Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
@PreAuthorize("hasRole('USER')")
public ResponseEntity<PaginatedList<UserDTO>> searchUsers(SearchParam searchParam) {
return ResponseEntity.ok(new PaginatedList<>());
}
@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
public ResponseEntity<UserDTO> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(new UserDTO());
}
@PutMapping("/{id}")
@PreAuthorize("hasRole('ADMIN') or (#id == authentication.principal.id and hasRole('USER'))")
public ResponseEntity<UserDTO> updateUser(
@PathVariable Long id,
@Valid @RequestBody UpdateUserRequest request) {
return ResponseEntity.ok(new UserDTO());
}
}출력: OpenAPI YAML
openapi: 3.0.0
info:
title: My API
version: 1.0.0
paths:
/api/users:
get:
summary: Search users
operationId: user_searchUsers
parameters:
- name: offset
in: query
schema:
type: integer
description: Pagination offset
- name: limit
in: query
schema:
type: integer
description: Pagination limit
- name: filter
in: query
schema:
$ref: '#/components/schemas/Filter'
- name: sort
in: query
schema:
$ref: '#/components/schemas/Sort'
responses:
'200':
description: Successful response
headers:
X-Total-Count:
schema:
type: integer
description: Total number of items
X-Offset:
schema:
type: integer
description: Current offset
X-Limit:
schema:
type: integer
description: Current limit
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedListUserDTO'
security:
- bearerAuth: []
x-required-roles:
- ROLE_USER
/api/users/{id}:
get:
summary: Get user by ID
operationId: user_getUserById
parameters:
- name: id
in: path
required: true
schema:
type: integer
format: int64
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/UserDTO'
security:
- bearerAuth: []
x-required-roles:
- ROLE_USER
- ROLE_ADMIN
put:
summary: Update user
operationId: user_updateUser
parameters:
- name: id
in: path
required: true
schema:
type: integer
format: int64
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/UserDTO'
security:
- bearerAuth: []
x-required-roles:
- ROLE_ADMIN
- ROLE_USER
x-security-expression: "hasRole('ADMIN') or (#id == authentication.principal.id and hasRole('USER'))"
components:
schemas:
UserDTO:
type: object
properties:
id:
type: integer
format: int64
firstName:
type: string
minLength: 2
maxLength: 50
lastName:
type: string
minLength: 2
maxLength: 50
email:
type: string
required:
- firstName
- lastName
- email
Filter:
type: object
description: Filter conditions
additionalProperties: true
Sort:
type: object
description: Sort conditions
properties:
field:
type: string
direction:
type: string
enum:
- ASC
- DESC
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT특수 타입 처리
SearchParam
메서드 파라미터가 SearchParam 타입인 경우, 생성기가 자동으로 페이지네이션 쿼리 파라미터를 추가합니다:
offset(integer) - 페이지네이션 오프셋limit(integer) - 페이지네이션 제한filter(object) - 필터 조건 (Filter 스키마 참조)sort(object) - 정렬 조건 (Sort 스키마 참조)
PaginatedList
메서드가 PaginatedList<T>를 반환하는 경우, 생성기가:
응답에 페이지네이션 헤더를 추가합니다:
X-Total-Count- 전체 항목 수X-Offset- 현재 오프셋X-Limit- 현재 제한
다음 구조의
PaginatedListT스키마를 생성합니다:PaginatedListUserDTO: type: object properties: items: type: array items: $ref: '#/components/schemas/UserDTO' total: type: integer offset: type: integer limit: type: integer
보안 표현식
생성기가 @PreAuthorize 및 @PostAuthorize의 SpEL 표현식을 파싱합니다:
hasRole('ADMIN')같은 단순 표현식 →x-required-roles: [ROLE_ADMIN]hasAnyRole('USER', 'ADMIN')의 다중 역할 →x-required-roles: [ROLE_USER, ROLE_ADMIN]- 복잡한 표현식은
x-security-expression에 보존
유효성 검사 어노테이션
생성기가 DTO 필드의 다음 유효성 검사 어노테이션을 인식합니다:
| 어노테이션 | OpenAPI 매핑 |
|------------|-----------------|
| @NotNull, @NotBlank, @NotEmpty | required: true |
| @Size(min, max) | minLength, maxLength |
| @Min(value) | minimum |
| @Max(value) | maximum |
| @Pattern(regexp) | pattern |
| @Email | pattern (이메일 정규식) |
프로젝트 구조
api-gen/
├── src/
│ ├── index.ts # CLI 진입점
│ ├── parser/
│ │ ├── javaParser.ts # Java 파일 파싱
│ │ └── astAnalyzer.ts # AST 분석 유틸리티
│ ├── analyzer/
│ │ ├── controllerAnalyzer.ts # Controller 감지
│ │ ├── parameterAnalyzer.ts # 파라미터 추출
│ │ ├── responseAnalyzer.ts # 응답 분석
│ │ ├── securityAnalyzer.ts # 보안 어노테이션 파싱
│ │ └── schemaGenerator.ts # DTO 스키마 생성
│ ├── generator/
│ │ └── openapiGenerator.ts # OpenAPI YAML 생성
│ └── types/
│ └── index.ts # TypeScript 타입 정의
├── examples/ # 샘플 Java 파일
├── package.json
├── tsconfig.json
└── README.md개발
# 의존성 설치
pnpm install
# 개발 모드로 실행
pnpm dev -- --source ./examples
# 프로덕션 빌드
pnpm build
# 테스트 실행 (예제 파일 사용)
pnpm test실제 사용 예시
pnpm start --source /git/g1cloud2/g1cloud-sales --output ./out/g1cloud-sales.yaml --title "G1cloud Sales API" --api-version "2.0.0-SNAPSHOT"
npx @redocly/cli build-docs ./out/g1cloud-sales.yaml -o ./out/g1cloud-sales.htmlpnpm start --source /git/g1cloud2/g1cloud-sales \
--api-source /git/g1cloud2/g1cloud-sales/g1cloud-sales-app \
--output ./out/g1cloud-sales-app.yaml \
--title "G1cloud Sales API" \
--api-version "2.0.0-SNAPSHOT"
npx @redocly/cli build-docs \
./out/g1cloud-sales-app.yaml \
-o ./out/g1cloud-sales-app.htmlpnpm start --source /git/g1cloud2/g1cloud-sales-service \
--api-source /git/g1cloud2/g1cloud-sales-service/module-basis \
--output ./out/g1cloud-sales-basis.yaml \
--title "G1cloud Sales Basis API" \
--api-version "2.0.0-SNAPSHOT"
npx @redocly/cli build-docs \
./out/g1cloud-sales-basis.yaml \
-o ./out/g1cloud-sales-basis.html