# Axim REST Doc Generator 개선 가이드

> **버전**: v1.0
> **작성일**: 2026-03-16
> **기준**: admin-api openapi.json (121 endpoints, 103 schemas)

---

## 개요

admin-api에서 생성된 `openapi.json`을 기반으로 Axim REST Doc Generator의 개선 포인트를 분석했습니다.
구조적으로 잘 생성되고 있으나, Swagger UI 활용도와 코드 생성기 호환성을 높이기 위해 아래 항목들을 권장합니다.

---

## 1. [HIGH] @RequestParam 복합 객체 → 개별 쿼리 파라미터 전개

### 현상

`@RequestParam`으로 바인딩되는 복합 객체(`SearchRequest`, `XPagination` 등)가 단일 `"type": "string"` 파라미터로 직렬화됩니다. admin-api 기준 **50개 파라미터**가 이 패턴에 해당합니다.

### 현재 출력

```json
{
  "name": "search",
  "in": "query",
  "description": "검색 조건 (status, networkId, walletAddressId)",
  "required": true,
  "schema": { "type": "string" }
}
```

### 기대 출력

```json
{
  "name": "status",
  "in": "query",
  "description": "상태 필터",
  "required": false,
  "schema": { "type": "string", "enum": ["PENDING", "APPROVED", "FAILED"] }
},
{
  "name": "networkId",
  "in": "query",
  "description": "네트워크 ID",
  "required": false,
  "schema": { "type": "integer", "format": "int64" }
},
{
  "name": "walletAddressId",
  "in": "query",
  "description": "지갑 주소 ID",
  "required": false,
  "schema": { "type": "integer", "format": "int64" }
},
{
  "name": "page",
  "in": "query",
  "description": "페이지 번호 (1-based)",
  "required": false,
  "schema": { "type": "integer", "format": "int32", "default": 1 }
},
{
  "name": "size",
  "in": "query",
  "description": "페이지 크기",
  "required": false,
  "schema": { "type": "integer", "format": "int32", "default": 20 }
}
```

### 구현 방향

```
1. @RequestParam 파라미터의 타입이 원시/래퍼/String이 아닌 경우:
   → 해당 클래스의 필드를 리플렉션으로 순회
   → 각 필드를 개별 query parameter로 생성

2. 필드 타입별 매핑:
   - String        → { "type": "string" }
   - Long/long     → { "type": "integer", "format": "int64" }
   - Integer/int   → { "type": "integer", "format": "int32" }
   - Boolean       → { "type": "boolean" }
   - LocalDate     → { "type": "string", "format": "date" }
   - LocalDateTime → { "type": "string", "format": "date-time" }
   - Enum          → { "type": "string", "enum": [...] }
   - BigDecimal    → { "type": "number" }

3. XPagination은 프레임워크 내장 클래스이므로 하드코딩 가능:
   - page  (integer, default: 1)
   - size  (integer, default: 20)
   - sort  (string, optional)

4. description 소스 우선순위:
   (1) 필드의 JavaDoc 주석 또는 @Schema(description=...)
   (2) 필드명 자체 (camelCase → 공백 분리)
```

### 영향도

Swagger UI에서 "Try it out" 시 개별 필드 입력이 가능해지며, 프런트엔드 팀이 검색/페이지네이션 API를 즉시 테스트할 수 있습니다. 현재는 `search` 필드에 어떤 값을 넣어야 하는지 description 텍스트를 파싱해야 합니다.

---

## 2. [HIGH] operationId — 해시값 대신 의미있는 이름

### 현상

```json
"operationId": "513bb616f58800df951fca3114f53faf"
```

모든 operationId가 MD5 해시로 생성되어, 코드 생성기(swagger-codegen, openapi-generator)에서 메서드명으로 사용할 수 없습니다.

### 기대 출력

```json
"operationId": "getWalletApprovals"
```

### 구현 방향

```
1. 기본 전략: 컨트롤러 메서드명을 그대로 사용
   - Java 리플렉션으로 HandlerMethod.getMethod().getName() 획득
   - 예: WalletApprovalController.getWalletApprovals() → "getWalletApprovals"

2. 중복 방지: 같은 메서드명이 다른 컨트롤러에 있을 경우
   - 패턴: {controllerPrefix}_{methodName}
   - 예: walletApproval_getList, deposit_getList

3. 폴백: 메서드명 획득 실패 시 현재 해시 방식 유지

4. 선택적 어노테이션 지원:
   @XApiDoc(operationId = "customOperationId")
   → 명시적 지정 시 메서드명 대신 사용
```

### 영향도

openapi-generator로 TypeScript/Kotlin 클라이언트를 생성할 때 `api.getWalletApprovals()` 같은 의미있는 메서드명이 생성됩니다. 현재는 `api._513bb616f58800df()` 같은 호출이 됩니다.

---

## 3. [MEDIUM] enum 타입 값 목록 자동 생성

### 현상

Java enum 타입 필드가 모두 `"type": "string"`으로만 출력됩니다. openapi.json 전체에서 `"enum"` 키워드가 **0건**입니다.

### 현재 출력

```json
{
  "status": {
    "type": "string",
    "description": "파트너 상태"
  }
}
```

### 기대 출력

```json
{
  "status": {
    "type": "string",
    "enum": ["ACTIVE", "SUSPENDED", "TERMINATED"],
    "description": "파트너 상태"
  }
}
```

### 구현 방향

```
1. DTO/Entity 필드 스캔 시 타입이 java.lang.Enum의 하위 클래스인지 확인
2. Enum.values()를 호출하여 name() 배열 추출
3. schema에 "enum" 배열 추가

4. 적용 위치:
   - components/schemas 내 DTO 프로퍼티
   - parameters 내 query/path 파라미터 (1번 개선과 연계)

5. 주의사항:
   - @JsonValue가 있으면 해당 값을 사용 (name() 대신)
   - 내부용 enum 값 제외가 필요하면 @XApiDoc(hidden = true) 지원 고려
```

### 영향도

Swagger UI에서 enum 필드가 드롭다운으로 표시되어 유효한 값만 선택 가능합니다. Cryptoments는 46개 enum을 사용하므로 효과가 큽니다.

---

## 4. [MEDIUM] required 필드 자동 추출

### 현상

일부 스키마에만 `required` 배열이 있고, 대부분의 스키마에는 누락되어 있습니다.

### 구현 방향

```
1. Jakarta Validation 어노테이션 스캔:
   - @NotNull      → required
   - @NotBlank     → required
   - @NotEmpty     → required
   - @Size(min=1)  → required

2. 원시 타입 자동 처리:
   - int, long, boolean, double → nullable 불가이므로 required

3. 출력 위치:
   - components/schemas/{SchemaName}/required 배열에 필드명 추가

4. 예외:
   - @Nullable 또는 Optional<T> 래핑 → required 제외
```

### 영향도

프런트엔드에서 폼 validation을 OpenAPI 스펙 기반으로 자동 생성할 수 있습니다. 현재는 API 문서 description을 읽고 수동 판단해야 합니다.

---

## 5. [MEDIUM] 에러 응답에 ApiError 스키마 자동 첨부

### 현상

에러 응답에 description만 있고 response body 스키마가 없습니다.

### 현재 출력

```json
"404": { "description": "Not Found" }
```

### 기대 출력

```json
"404": {
  "description": "Not Found",
  "content": {
    "application/json": {
      "schema": { "$ref": "#/components/schemas/ApiError" }
    }
  }
}
```

### 구현 방향

```
1. components/schemas에 ApiError 스키마 자동 등록:
   {
     "ApiError": {
       "type": "object",
       "properties": {
         "code":    { "type": "string", "description": "에러 코드" },
         "message": { "type": "string", "description": "에러 메시지" },
         "status":  { "type": "integer", "description": "HTTP 상태 코드" }
       }
     }
   }

2. 4xx/5xx 응답에 자동으로 schema 참조 추가
   - XExceptionHandler가 처리하는 모든 에러는 동일한 ApiError 구조

3. 선택적: @XApiDoc(errors = {404, 409}) 로 명시적 에러 코드 지정 시
   해당 코드만 출력, 미지정 시 기본값 (400, 404, 500)
```

### 영향도

프런트엔드에서 에러 핸들링 타입을 자동 생성할 수 있습니다. `catch (error) { error.code, error.message }` 구조를 스펙에서 보장합니다.

---

## 6. [LOW] LocalDateTime → 표준 date-time 인라인 처리

### 현상

```json
"createdAt": {
  "allOf": [{ "$ref": "#/components/schemas/LocalDateTime" }]
}
```

`LocalDateTime`이 별도 스키마로 참조되고 있습니다.

### 기대 출력

```json
"createdAt": {
  "type": "string",
  "format": "date-time",
  "description": "생성 시각"
}
```

### 구현 방향

```
알려진 Java 시간 타입을 인라인 변환:
- LocalDateTime → { "type": "string", "format": "date-time" }
- LocalDate     → { "type": "string", "format": "date" }
- LocalTime     → { "type": "string", "format": "time" }
- Instant       → { "type": "string", "format": "date-time" }
- ZonedDateTime → { "type": "string", "format": "date-time" }

components/schemas에서 LocalDateTime 스키마 제거.
```

### 영향도

OpenAPI 표준 format이므로 모든 코드 생성기가 언어별 DateTime으로 자동 매핑합니다. 현재는 `LocalDateTime`이라는 커스텀 스키마로 인식되어 단순 object로 생성됩니다.

---

## 7. [LOW] BigDecimal 필드 format 세분화

### 현상

`BigDecimal` 필드가 모두 `"type": "number"`로만 출력됩니다.

### 기대 출력

```json
{
  "amount": {
    "type": "string",
    "format": "decimal",
    "description": "금액 (DECIMAL 36,18)"
  }
}
```

### 구현 방향

```
금융 시스템에서 BigDecimal은 부동소수점 오류를 방지하기 위해
"type": "string", "format": "decimal"로 직렬화하는 것이 권장됩니다.

옵션 A (안전): "type": "string", "format": "decimal"
  → 프런트엔드에서 문자열로 수신 후 BigNumber 라이브러리 사용
  → Jackson @JsonFormat(shape = STRING) 과 일치

옵션 B (호환): "type": "number", "format": "decimal"
  → 기존 클라이언트 호환성 유지, 정밀도 힌트만 추가
  → JavaScript에서 Number로 파싱 시 정밀도 손실 가능

Cryptoments는 DECIMAL(36,18) 을 사용하므로 옵션 A 권장.
단, 선택은 @XApiDoc(numberFormat = STRING|NUMBER) 어노테이션으로 제어 가능하게.
```

---

## 8. [LOW] example 값 지원

### 현상

어떤 필드에도 `example` 값이 없어 Swagger UI "Try it out" 시 빈 값으로 시작합니다.

### 구현 방향

```
1단계: 어노테이션 기반
  @XApiDoc(example = "ACTIVE")
  private PartnerStatus status;
  → { "type": "string", "enum": [...], "example": "ACTIVE" }

2단계: 타입 기반 기본값 (어노테이션 없을 때)
  - String  → ""
  - Long    → 1
  - Integer → 1
  - Boolean → true
  - Enum    → 첫 번째 값
  - BigDecimal → "100.00"
  - LocalDateTime → "2026-01-01T00:00:00"

3단계: requestBody 전체 example
  @XApiDoc(requestExample = "{ ... }")
  → operation.requestBody.content.application/json.example
```

### 영향도

Swagger UI에서 즉시 테스트 가능한 예시 값이 채워져, API 탐색 시간이 크게 단축됩니다.

---

## 구현 우선순위 요약

| 순위 | 항목 | 난이도 | 임팩트 | 비고 |
|------|------|--------|--------|------|
| 1 | @RequestParam 복합 객체 전개 | 중 | ★★★ | Swagger UI 실용성 결정적 |
| 2 | operationId 의미있는 이름 | 하 | ★★★ | 코드 생성기 필수 |
| 3 | enum 값 목록 | 하 | ★★☆ | 46개 enum 활용, 1번과 시너지 |
| 4 | required 자동 추출 | 중 | ★★☆ | Jakarta Validation 리플렉션 |
| 5 | ApiError 스키마 | 하 | ★★☆ | 하드코딩 가능, 간단 |
| 6 | LocalDateTime 인라인 | 하 | ★☆☆ | 타입 매핑 테이블 추가 |
| 7 | BigDecimal format | 하 | ★☆☆ | 옵션 제공 |
| 8 | example 값 | 중 | ★☆☆ | 점진적 확장 |

**권장 순서**: 1 → 2 → 3 → 5 → 6 → 4 → 7 → 8

1~3번만 완료해도 openapi.json의 실용성이 크게 향상됩니다. 특히 1번과 3번은 함께 구현하면 검색 파라미터가 enum 드롭다운으로 표시되어 시너지가 큽니다.

---

## 참고: 현재 잘 되어 있는 부분

개선점만 나열하면 균형이 안 맞으므로, 현재 잘 구현된 부분도 기록합니다.

- **$ref 참조 무결성**: 145개 참조 중 깨진 참조 0건
- **XPage 제네릭 처리**: `XPage<T>` → `XPage_TypeName` 스키마로 정확히 전개
- **securitySchemes**: Access-Token 헤더 인증이 전역 적용으로 정확히 설정
- **태그 분류**: 25개 태그로 121개 엔드포인트가 논리적으로 그룹화
- **path parameter 타입**: `{id}` 같은 경로 변수가 `"type": "integer", "format": "int64"`로 정확히 매핑
- **requestBody 스키마**: POST/PUT/PATCH의 요청 본문이 DTO $ref로 정확히 연결
- **description 충실도**: 대부분의 엔드포인트에 한국어 설명이 포함
