본문 바로가기
Spring

Spring Boot 3 + Swagger Codegen 적용하기

by 흑시바 2024. 12. 27.

Swagger Editor를 활용하여 Spring Boot 3와 Gradle 환경에서 OpenAPI Specification (OAS) YAML 파일을 작성하고 이를 기반으로 API를 구현하는 방법을 단계별로 설명하겠다. 이 과정은 Swagger Editor를 사용하여 YAML 파일을 작성하고, Spring Boot 프로젝트에서 이를 활용하는 방법을 포함한다.

1. Swagger Editor를 활용해서 openapi.yaml 파일생성하기

Swagger Editor( https://editor.swagger.io/ )에 접근하면 YAML 파일로 작성이 가능하고 즉시 문법확인과 예시를 확인할 수 있다. 

2. Swagger Editor에서 YAML 파일 작성

- 기본 구조 설정

 

Swagger Editor를 열면 기본적으로 예제 YAML 파일이 로드된다. 이 예제를 참고하여 새로운 YAML 파일을 작성할 수 있다. 기본 구조는 다음과 같다.

 

openapi: 3.0.3
info:
  title: Swagger Petstore - OpenAPI 3.0
  description: |-
    This is a sample Pet Store Server based on the OpenAPI 3.0 specification.  You can find out more about
    Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!
    You can now help us improve the API whether it's by making changes to the definition itself or to the code.
    That way, with time, we can improve the API in general, and expose some of the new features in OAS3.

    _If you're looking for the Swagger 2.0/OAS 2.0 version of Petstore, then click [here](https://editor.swagger.io/?url=https://petstore.swagger.io/v2/swagger.yaml). Alternatively, you can load via the `Edit > Load Petstore OAS 2.0` menu option!_
    
    Some useful links:
    - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
    - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
  termsOfService: http://swagger.io/terms/
  contact:
    email: apiteam@swagger.io
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html
  version: 1.0.11
externalDocs:
  description: Find out more about Swagger
  url: http://swagger.io
servers:
  - url: https://petstore3.swagger.io/api/v3
tags:
  - name: pet
    description: Everything about your Pets
    externalDocs:
      description: Find out more
      url: http://swagger.io
  - name: store
    description: Access to Petstore orders
    externalDocs:
      description: Find out more about our store
      url: http://swagger.io
  - name: user
    description: Operations about user

 

openapi: OpenAPI 버전을 지정한다. OpenAPI는 시맨틱 버전 관리를 사용하므로 버전이 major:minor:patch 형식이 된다.

 

info: API에 대한 메타데이터가 포함된다. 이 정보는 문서 생성에 사용되며 클라이언트가 사용할 수 있다.
- title: API의 제목을 지정한다.

- description: API에 대한 설명을 제공한다. 마크다운 구문을 사용할 수 있다.

- termOfService: 서비스 약관으로 연결되는 URL이다.

- contact: API 제공자의 연락처 정보다.

- license: 라이선스 정보다. name 필드는 Apache 2.0 같은 라이선스 이름을 나타내는 필수 필드다.

- version: API의 버전을 지정한다.

 

externalDocs: externalDocs는 노출된 API의 확장 문서를 가리키는 필드다.

- description: 외부 문서의 요약을 정의한다. 마크다운 구문을 사용할 수 있다.

- url: 외부 문서에 대한 링크이다.

 

servers: API를 호스팅하는 서버 목록을 가진다. 이 부분이 제공되지 않으면 기본 값으로 호스트 된 문서 서버의 루트(/)를 가리킨다.

 

tags: API 엔드포인트를 논리적으로 그룹화하는 데 사용된다. 이를 통해 API 문서를 더 구조화되고 이해하기 쉽게 만들 수 있다. 해당 섹션은 선택 사항이지만, API 문서를 더 잘 조직화하는 데 도움이 된다.

 

- Paths 정의

 

paths:
  /hello:
    get:
      summary: Returns a greeting message
      operationId: getHello
      responses:
        '200':
          description: A greeting message
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Hello, World!

 

/hello: 엔드포인트 경로를 지정한다.
get: HTTP 메서드를 지정한다. 여기서는 GET 요청을 정의한다.
summary: 엔드포인트의 간단한 설명을 제공한다.
operationId: 이 엔드포인트의 고유 식별자를 지정한다.


responses: 모든 API 오퍼레이션의 필수 필드다. API가 요청에 대해 응답할 수 있는 응답 타입을 정의한다. 이 속성에는 200과 같이 HTTP 상태 코드가 될 수 있는 응답이 하나 이상 필요하다.

description: 응답에 대한 설명을 제공한다.

content: 다양한 미디어 타입을 나타내는 콘텐츠 타입을 정의한다. (application/json, application/xml 등).
schema: 응답 데이터의 스키마를 정의한다.
type: 데이터 타입을 지정한다. 여기서는 객체(object)를 지정한다.
properties: 객체의 속성을 정의한다.
message: 속성 이름을 지정한다.
type: 속성의 데이터 타입을 지정한다. 여기서는 문자열(string)을 지정한다.
example: 예제 값을 제공한다.

 

- Components 정의

 

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
          format: email
    Product:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        price:
          type: number
          format: float
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  parameters:
    UserId:
      name: userId
      in: path
      required: true
      schema:
        type: integer
  requestBodies:
    User:
      description: User object that needs to be added
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/User'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-KEY

 

components 절은 여러 재사용 가능한 객체를 정의할 수 있는 컨테이너이다. 주요 섹션은 다음과 같다

schemas: 재사용 가능한 데이터 구조를 정의한다.
responses: 재사용 가능한 응답을 정의한다.
parameters: 재사용 가능한 파라미터를 정의한다.
examples: 재사용 가능한 예제를 정의한다.
requestBodies: 재사용 가능한 요청 본문을 정의한다.
headers: 재사용 가능한 헤더를 정의한다.
securitySchemes: 재사용 가능한 보안 스키마를 정의한다.
links: 재사용 가능한 링크를 정의한다.
callbacks: 재사용 가능한 콜백을 정의한다.

 

추가적인 속성에 대한 자세한 설명은 https://swagger.io/docs/specification/v3_0/basic-structure/를 참고한다.

3. Spring Boot에 적용하기

2에서 작성한 yaml 파일은 src/main/resources/api 에 openapi.yaml 파일로 추가한다.

 

이후 build.gradle 파일에 해당 의존성을 추가한다. (예시는 현재 가장 최신인 Spring Boot 3.4.1 버전 기준)

 

swaggerCodegen 'org.openapitools:openapi-generator-cli:7.10.0'
compileOnly 'io.swagger:swagger-annotations:1.6.14'
compileOnly 'org.openapitools:jackson-databind-nullable:0.2.6'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml'
implementation 'io.springfox:springfox-oas:3.0.0'
implementation 'io.swagger.core.v3:swagger-annotations:2.2.27'
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
implementation 'org.springframework.boot:spring-boot-starter-validation'

 

이후 방금 작성한 API 정의로부터 코드를 생성하기 위해 다음과 같은 작업이 필요하다.

 

1. plugins 부분에 org.hidetake.swagger.generator를 추가한다.

( https://github.com/int128/gradle-swagger-generator-plugin )

 

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.4.1'
	id 'io.spring.dependency-management' version '1.1.7'
	id 'org.hidetake.swagger.generator' version '2.19.2'
}

 

2. OpenAPI Config 정의

 

OpenAPI Generator의 CLI가 사용해야 하는 모델과 API 패키지 이름이나, REST 인터페이스 또는 날짜/시간 관련 객체 생성 시에 사용하는 라이브러리 등 여러 설정이 필요하다. 이러한 모든 설정은 config.json(/src/main/resources/api/config.json)에 정의한다.

( https://openapi-generator.tech/docs/generators/spring/ )

 

{
  "library": "spring-boot",
  "dateLibrary": "java8",
  "hideGenerationTimestamp": true,
  "modelPackage": "com.kmhoon.app.model",
  "apiPackage": "com.kmhoon.app",
  "invokerPackage": "com.kmhoon.app",
  "serializableModel": true,
  "useTags": true,
  "useGzipFeature" : true,
  "hateoas": true,
  "unhandledException": true,
  "useSpringBoot3": true,
  "useSwaggerUI": true,
  "importMappings": {
    "ResourceSupport":"org.springframework.hateoas.RepresentationModel",
    "Link": "org.springframework.hateoas.Link"
  }
}

 

해당 설정은 spring-boot를 라이브러리로 설정하고, Swagger Codegen이 스프링 부트와 일치하는 클래스를 생성하도록 한다. useSpringBoot3 설정을 통해 생성된 클래스를 스프링 부트 3과 일치시키도록 한다.

 

modelPackage, apiPackage, invokerPackage 부분에 정의된 경로는 이후 Api 인터페이스가 어디에 생성될지 경로이다. 개발중인 패키지 경로에 맞추어 작성하도록 한다.

 

3. OpenAPI Generator ignore 파일 생성

 

.gitignore와 같이 생성에서 제외할 코드를 특정할 수도 있다. src/main/resources/api/.openapi-generator-ignore에 다음 코드 줄을 추가한다. 해당 항목을 추가하면 API 인터페이스와 모델만 생성한다.

 

**/*Controller.java

 

4. build.gradle 파일 설정 작업

 

- swaggerSources 태스크 정의

- compieJava 작업 의존성에 swaggerSources 추가

- processResources 태스크에 generateSwaggerCode 태스크를 의존성으로 추가한다.

- 생성된 코드를 Gradle SourceSets에 추가

 

swaggerSources {
	def typeMappings = 'URI=URI'
	def importMappings = 'URI=java.net.URI'
	shibaHolic {
		def apiYaml = "${rootDir}/src/main/resources/api/openapi.yaml"
		def configJson = "${rootDir}/src/main/resources/api/config.json"
		inputFile = file(apiYaml)
		def ignoreFile = file("${rootDir}/src/main/resources/api/.openapi-generator-ignore")
		code {
			language = 'spring'
			configFile = file(configJson)
			rawOptions = ['--ignore-file-override', ignoreFile, '--type-mappings',
						  typeMappings, '--import-mappings', importMappings] as List<String>
			components = [models: true, apis: true, supportingFiles: 'ApiUtil.java']
			dependsOn validation
		}
	}
}

compileJava.dependsOn swaggerSources.shibaHolic.code
sourceSets.main.java.srcDir "${swaggerSources.shibaHolic.code.outputDir}/src/main/java"
sourceSets.main.resources.srcDir "${swaggerSources.shibaHolic.code.outputDir}/src/main/resources"

processResources {
	dependsOn(generateSwaggerCode)
}

 

5. 빌드 실행

 

gradlew clean build 명령어를 수행해서 자바 인터페이스 코드를 생성한다.

 

 

빌드가 정상적으로 성공하고 build/swagger-code-shibaHolic을 확인해보면, 이미지와 같이 Api 인터페이스와 Model이 생성되어 있는 것을 확인할 수 있다. Swagger에서 기본으로 제공하는 yaml을 적용하는 경우 이미지와 같이 나오게 된다.

 

6. 컨트롤러 오버라이딩 하기

 

이전에 ignore 처리를 했기 때문에, 컨트롤러를 직접 생성해서 구현해야 한다.

 

PetController를 생성해서 petApi를 상속하고 하기 이미지와 같이 Override Method 목록을 확인해보면 yaml 파일에 정의한 메서드 들을 모두 Override 할 수 있다는 것을 확인할 수 있다.

 

PetApi를 확인해보면, 하기 이미지와 같이 default로 구현되어 있는 것을 확인할 수 있다. 따라서, 반드시 구현하지 않는다고 오류가 발생하지는 않지만, 별도에 구현이 없이 호출하게 되면 501 상태를 받게 된다.

 

getPetById()를 구현해서 호출해 본다.

 

포스트맨으로 테스트하면, 다음과 같이 정상적으로 API가 호출되는 것을 확인할 수 있다.

 

댓글