πŸ“˜ Programming/Spring

Swag λ„˜μΉ˜κ²Œ μ†Œν†΅ν•΄λ³΄μž! Swagger in SpringBoot 3.x 정볡 (SpringFox, SpringDoc, API λ¬Έμ„œ μžλ™ν™”ν•˜λŠ” 방법)

ν•œμ½”λ”© 2023. 10. 23. 17:43
728x90
728x90

μ˜€λžœλ§Œμ— ν¬μŠ€νŒ…ν•˜λŠ” μ΄μœ λŠ” ν•œλ™μ•ˆ 별 νƒˆ 없이 ν”„λ‘œμ νŠΈλ₯Ό 이어가고 있기 λ•Œλ¬Έμ—.. μƒˆλ‘­κ²Œ 무언가λ₯Ό μ•Œμ•„κ°€κ±°λ‚˜ λ³΅μŠ΅ν•˜λŠ” 일이 μ μ—ˆμŠ΅λ‹ˆλ‹€..^^ μ˜€λŠ˜μ€ Swaager μ‚¬μš© 도쀑에 κΆκΈˆν•œ 점이 λ§Žμ•„μ§€λ©΄μ„œ μ‘°μ‚¬ν•œ λ‚΄μš©λ“€μ„ μ •λ¦¬ν•œ λ‚΄μš©μ„ ν¬μŠ€νŒ…ν•΄λ³΄λ € ν•©λ‹ˆλ‹€.


Swagger λ“±μž₯ μ „κ³Ό ν›„ μ°¨μ΄λŠ”?

Swaggerκ°€ 세상에 λ“±μž₯ν•˜κΈ° μ „, 개발자 κ°„ μ†Œν†΅ 방식은 λ‹¨μˆœν•˜κ²Œ λ°±μ—”λ“œ κ°œλ°œμžκ°€ URL 및 Request, Responseλ₯Ό 직접 λ¬Έμ„œν™”ν•΄μ„œ ν”„λ‘ νŠΈμ—”λ“œ κ°œλ°œμžμ—κ²Œ μ „λ‹¬ν•˜λŠ” 방식을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.

 

λ”°λΌμ„œ κΈ°λŠ₯ κ΅¬ν˜„μ„ μœ„ν•œ API λͺ…μ„Έ 및 μš”κ΅¬μ‚¬ν•­μ΄ μžˆλ‹€κ³  κ°€μ •ν–ˆμ„ λ•Œ, 각 개발자 κ°„μ˜ 필연적인 μ±…μž„μ΄ λ”°λ¦…λ‹ˆλ‹€.  

  • λ°±μ—”λ“œ κ°œλ°œμžλŠ” API κ΅¬ν˜„에 μžˆμ–΄μ„œ μ–΄λ– ν•œ μ‹€μˆ˜κ°€ μ—†μ–΄μ•Ό ν•©λ‹ˆλ‹€.
  • λ°±μ—”λ“œ κ°œλ°œμžλŠ” κΈ°λŠ₯ μš”ꡬ사항을 μ™„λ²½νžˆ μ΄ν•΄ν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • ν”„λ‘ νŠΈμ—”λ“œ κ°œλ°œμžλŠ” κΈ°λŠ₯ κ΅¬ν˜„에 μžˆμ–΄μ„œ μ–΄λ– ν•œ μ‹€μˆ˜κ°€ μ—†μ–΄μ•Ό ν•©λ‹ˆλ‹€.
  • ν”„λ‘ νŠΈμ—”λ“œ κ°œλ°œμžλŠ” κΈ°λŠ₯ μš”ꡬ사항을 μ™„λ²½νžˆ μ΄ν•΄ν•΄μ•Ό ν•©λ‹ˆλ‹€.

λ‹¨μˆœν•œ 반볡 μž‘μ—…μΈ 만큼 생산성 μΈ‘λ©΄μ—μ„œλŠ” λ‹€μ†Œ λΉ„νš¨μœ¨μ μž…λ‹ˆλ‹€. 이둜 인해 μ—¬λŸ¬ 방면으둜 λΆˆνŽΈν•¨μ΄ μ•ΌκΈ°λ©λ‹ˆλ‹€.

  • λ°±μ—”λ“œ κ°œλ°œμžκ°€ API λ¬Έμ„œλ₯Ό 일일이 μž‘μ„±ν•˜λŠ” λ²ˆκ±°λ‘œμ›€
  • APIκ°€ λ³€κ²½λ˜λ©΄μ„œ 비일관적인 λ¬Έμ„œλ₯Ό λ‹€μ‹œ μˆ˜μ •ν•΄μ•Ό ν•˜λŠ” λ²ˆκ±°λ‘œμ›€
  • 수기 μž‘μ„±μœΌλ‘œ μΈν•œ νœ΄λ¨Όμ—λŸ¬ λ°œμƒμœΌλ‘œ 개발자 κ°„μ˜ μ˜μ‚¬μ†Œν†΅μ΄ 어렀움
  • ν”„λ‘ νŠΈμ—”λ“œ κ°œλ°œμžκ°€ Postman, cURL둜 직접 URL 및 Request μž‘μ„±ν•˜μ—¬ APIλ₯Ό ν…ŒμŠ€νŠΈν•΄μ•Ό ν•˜λŠ” λ²ˆκ±°λ‘œμ›€

μœ„μ™€ 같은 λ²ˆκ±°λ‘œμ›€μ„ ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ“±μž₯ν•œ SwaggerλŠ” OAS(Open Api Specification)λ₯Ό μœ„ν•œ μ˜€ν”ˆμ†ŒμŠ€ ν”„λ ˆμž„μ›Œν¬μž…λ‹ˆλ‹€. μ‚¬μš©ν•˜λ©΄ 얻을 수 μžˆλŠ” μž₯점이 μžˆμŠ΅λ‹ˆλ‹€.

  • API λ¬Έμ„œλ₯Ό 생성할 λ•Œ, κ°œλ°œμžκ°€ λ¬Έμ„œλ₯Ό μž‘μ„±ν•˜μ§€ μ•Šμ•„λ„ λ˜λ―€λ‘œ 개발 μ‹œκ°„ 단좕
  • Swagger UIλ₯Ό μ΄μš©ν•˜λ©΄ APIλ₯Ό μ‰½κ²Œ ν…ŒμŠ€νŠΈν•  수 있으며, API 호좜 μ‹œ 전달해야 ν•  νŒŒλΌλ―Έν„°λ₯Ό 확인할 수 있음
  • Swaggerλ₯Ό μ‚¬μš©ν•˜λ©΄ API 버전 관리가 μš©μ΄ν•΄μ§€κ³  λ‹€μ–‘ν•œ API λ¬Έμ„œλ₯Ό 톡합할 수 있음 
  • Spring Bootμ—μ„œ Swaggerλ₯Ό μ‚¬μš©ν•˜λ©΄ API λ¬Έμ„œλ₯Ό μƒμ„±ν•˜λŠ” λ° ν•„μš”ν•œ μ½”λ“œλ₯Ό μ§μ ‘ μž‘μ„±ν•˜μ§€ μ•Šμ•„도 λ˜λ―€λ‘œ κ°œλ°œμžλŠ” API κ°œλ°œμ— μ§‘쀑할 μˆ˜ μžˆμŒ

Swaggerλ₯Ό λ‹€λ£¨λŠ” 두 가지 방법

1) YAML

Swaggerλ₯Ό κ΅¬μ„±ν•˜λŠ” 방법 쀑 첫 λ²ˆμ§ΈλŠ” YAMLνŒŒμΌμ„ μ‚¬μš©ν•˜κ²Œ λœλ‹€λ©΄ Swagger UIλ₯Ό μœ„ν•œ μ„œλ²„λ₯Ό λ”°λ‘œ 두고, Swaggerμ—μ„œ API μš”μ²­μ„ ν•˜λ©΄ κ·Έ μš”μ²­μ„ ν•΄λ‹Ή μ„œλ²„λ‘œ μ „λ‹¬ν•˜κ²Œ λ©λ‹ˆλ‹€. 반면 Spring Frameworkμ—μ„œ ꡬ성 μ‹œ μ„œλ²„ μžμ²΄κ°€ Swagger κ²Έ λ°±μ—”λ“œ μ„œλ²„κ°€ λΌμ„œ, μš”μ²­ 및 응닡을 μ²˜λ¦¬ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

μ΄λŸ¬ν•œ ꡬ쑰λ₯Ό λ”°λ₯΄λ©΄ YAML νŒŒμΌμ„ μ΄ν•΄ν•˜κ³  μˆ˜μ •ν•˜κΈ° μ‰¬μ›Œμ§€κ³ , λͺ…ν™•ν•œ μ•„ν‚€ν…μ²˜λ₯Ό κ°€μ§ˆ 수 μžˆμ–΄μ„œ 개발자뿐만 μ•„λ‹ˆλΌ κΈ°νšμžλ„ μ‰½κ²Œ 이해할 수 μžˆμŠ΅λ‹ˆλ‹€. YAML νŒŒμΌμ„ 톡해 κ΅¬μ„±ν•˜λŠ” 방법은 좜처 원글을 톡해 μžμ„Έν•œ λ‚΄μš©μ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

2) Spring Frameworkμ—μ„œ ꡬ성

YAML파일둜 κ΅¬μ„±ν•˜λŠ” 것과 λ‹€λ₯΄κ²Œ 각 Spring Framework ν™˜κ²½μ—μ„œ λ™μž‘ν•˜λŠ” μ„œλ²„ λ‚΄μ˜ Swaggerλ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨ 각 μ„œλ²„μ˜ API λͺ…μ„Έλ₯Ό 확인할 수 μžˆλ„λ‘ κ΅¬μ„±ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€. μ„€μ •κ³Ό κ΄€λ ¨λœ μ–΄λ…Έν…Œμ΄μ…˜κ³Ό ν•¨μˆ˜λ“€μ΄ 잘 λ‚˜μ™€μžˆμ–΄ μ μš©ν•˜λŠ” 방법은 μ‰½μ§€λ§Œ, μ½”λ“œ 내에 μž‘μ„±μ„ ν•˜λ‹€ λ³΄λ‹ˆ μ†ŒμŠ€ μ½”λ“œμ˜ 가독성이 μ €ν•˜λœλ‹€λŠ” 단점이 μžˆμŠ΅λ‹ˆλ‹€.


Swagger μ‚¬μš©μ„ λ•λŠ” Tool & Library

1) Tool

1. Swagger Codegen

Codegen은 server stubs client SDKsλ₯Ό 생성할 수 있게 ν•΄ μ€λ‹ˆλ‹€. 즉, Swaggerμ—μ„œ μ •μ˜ν•œ μ„€κ³„λŒ€λ‘œ κ°œλ°œμ„ 진행할 수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€. λͺ¨λ“  μ–Έμ–΄λ₯Ό μ§€μ›ν•˜κ³  μžˆμ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€.

 

2. Swagger Editor

Swagger EditorλŠ” Swaggerλ₯Ό μ˜¨λΌμΈμ—μ„œ μž‘μ„±ν•  수 μžˆλŠ” 것이 νŠΉμ§•μž…λ‹ˆλ‹€. Cloud ν™˜κ²½μ—μ„œ μž‘μ—…ν•  μˆ˜λ„ 있고, Editorλ₯Ό λ‹€μš΄λ‘œλ“œν•˜μ—¬μ„œ μ‚¬μš©ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. Dockerλ₯Ό 톡해 μ΄λ―Έμ§€λ‘œ μ œκ³΅ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

 

3. Swagger UI

Swagger의 ν‘œμ€€μ— 맞좰 UIλ₯Ό μž‘μ„±ν•΄ μ£ΌλŠ” μ—λ””ν„°μž…λ‹ˆλ‹€.

 

2) Library

Swaggerλ₯Ό Spring ν™˜κ²½μ—μ„œ μ‰½κ²Œ μ‚¬μš©ν•  수 μžˆλ„λ‘ λ„μ™€μ£ΌλŠ” λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μžˆμŠ΅λ‹ˆλ‹€.

 

1. Springfox

Springfox Swagger의 경우 3.00 버전을 λ§ˆμ§€λ§‰μœΌλ‘œ μ—…λ°μ΄νŠΈκ°€ μ€‘λ‹¨λ˜μ–΄μ„œ 잘 μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 좔세라고 ν•©λ‹ˆλ‹€. (Maven Repository κΈ°μ€€ 2020λ…„ 7μ›” λ§ˆμ§€λ§‰ μ—…λ°μ΄νŠΈ)

 

2. SpringDoc

SpringFox와 κΈ°λŠ₯은 λ™μΌν•˜μ§€λ§Œ Spring WebFlux (λ…Ό 블둝킹 비동기 λ°©μ‹μ˜ μ›Ή 개발)을 μ§€μ›ν•©λ‹ˆλ‹€. λ˜ν•œ, κ·Έλ£Ή κ°„ API 정렬이 κ°€λŠ₯ν•˜λ‹€λŠ” μž₯점이 μžˆμŠ΅λ‹ˆλ‹€.

Springfox Swagger의 경우 2020λ…„ 7월에 μ—…λ°μ΄νŠΈ 이후 후속 μ—…λ°μ΄νŠΈκ°€ μ—†μŠ΅λ‹ˆλ‹€. 이후 λ‚˜μ˜¨ κΈ°λŠ₯듀에 λŒ€μ‘μ΄ μ•ˆ λ˜λŠ” κ²½μš°κ°€ μžˆμ–΄μ„œ SpringDocλ₯Ό 자주 μ‚¬μš©ν•©λ‹ˆλ‹€. κ²Œλ‹€κ°€ SpringBoot 3 ν™˜κ²½μ—μ„œ SpringFoxλŠ” μ‚¬μš©μ΄ λΆˆκ°€λŠ₯ν•˜λ‹€κ³  ν•©λ‹ˆλ‹€. 

λ°˜μ‘ν˜•

Swagger μ‚¬μš© 방법

SpringBootμ—μ„œ Swagger μ„€μ •ν•˜κΈ°

섀정에 μ•žμ„œ μ €λŠ” SpringBoot 3.0.3 λ²„μ „μ—μ„œ μ μš©ν–ˆμŠ΅λ‹ˆλ‹€. μ•„λž˜ μ˜μ‘΄μ„±μ„ μΆ”κ°€ν•˜μ—¬ Swaggerλ₯Ό μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•΄μ€λ‹ˆλ‹€.

implementation 'org.springdoc:springdoc-openapi-starter-common:2.0.2'

 

μœ„ μ„€μ •λ§ŒμœΌλ‘œλ„ ν˜„μž¬ λ‘œμ»¬μ—μ„œλ„ Swagger λ¬Έμ„œκ°€ μƒμ„±λ©λ‹ˆλ‹€. 

http://localhost:8080/swagger/index.html

 

Swagger API λ¬Έμ„œ κ΄€λ ¨ 섀정듀을 μ œμ–΄ν•  수 μžˆλ„λ‘ SwaggerConfig νŒŒμΌμ„ μΆ”κ°€ν•©λ‹ˆλ‹€. 

@Configuration
public class SwaggerConfig {

    private String version = "1.0.0";

    @Bean
    public OpenAPI openAPI() {
        return new OpenAPI()
                .components(new Components())
                .info(apiInfo());
    }
 
    private Info apiInfo() {
        return new Info()
                .title("제λͺ©")
                .description("μ„€λͺ…")
                .version(version);
    }
}

 

λ˜ν•œ Swaggerλ₯Ό μ œμ–΄ν•  수 μžˆλŠ” yaml νŒŒμΌμ„ μΆ”κ°€ν•΄ μ€λ‹ˆλ‹€. packages-to-scan 속성에 Swaggerλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•œ Controllerκ°€ μžˆλŠ” νŒ¨ν‚€μ§€ 경둜λ₯Ό μ§€μ •ν•˜λ©΄ μžλ™μœΌλ‘œ Swagger λ¬Έμ„œμ— λ“±λ‘λ©λ‹ˆλ‹€. 등둝을 μ›μΉ˜ μ•ŠλŠ” μ»¨νŠΈλ‘€λŸ¬μ— @Hidden μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μ œμ™Έν•  수 μžˆμŠ΅λ‹ˆλ‹€. 각 속성에 λŒ€ν•΄μ„œ 더 μ•Œκ³  μ‹ΆμœΌμ‹œλ©΄ 곡식 λ¬Έμ„œλ₯Ό 확인해 μ£Όμ„Έμš”. (https://springdoc.org/#properties)

springdoc:
  packages-to-scan: com.example.swagger.web
  default-consumes-media-type: application/json;charset=UTF-8
  default-produces-media-type: application/json;charset=UTF-8
  swagger-ui:
    path: /
    disable-swagger-default-url: true
    display-request-duration: true
    operations-sorter: alpha

 

이제 Swagger λ¬Έμ„œμ— 등둝할 Controller νŒŒμΌμ„ μˆ˜μ •ν•΄μ„œ Swagger λ¬Έμ„œλ₯Ό 등둝할 수 μžˆμŠ΅λ‹ˆλ‹€. SpringBoot 3 버전과 2 λ²„μ „μ—μ„œ μ–΄λ…Έν…Œμ΄μ…˜ 차이가 μžˆμ–΄ λ¨Όμ € 짚고 λ„˜μ–΄κ°€ λ³΄κ² μŠ΅λ‹ˆλ‹€.

@Api → @Tag
@ApiIgnore → @Parameter(hidden = true) or @Operation(hidden = true) or @Hidden
@ApiImplicitParam → @Parameter
@ApiImplicitParams → @Parameters
@ApiModel → @Schema
@ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
@ApiModelProperty → @Schema
@ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
@ApiParam → @Parameter
@ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")

 

μ•„λž˜μ™€ 같이 예제 APIλ₯Ό μž‘μ„±ν•΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

    @GetMapping("/example/{id}")
    @Operation(summary = "쑰회 예제", description  = "μ‘°νšŒν•œλ‹€", responses = {
    	@ApiResponse(responseCode = "200", 
            	      description = "쑰회 성곡", 
                          content = @Content(array = @ArraySchema(schema = @Schema(implementation = exampleResource.class))))
    })
    @Parameter(name = "id", description = "예제 νŒŒλΌλ―Έν„°", in = ParameterIn.PATH)
    public List<CafeResource> search(@PathVariable("id") Long id) {
		...
    }
    
    @Hidden
    @GetMapping("/hidden")
    public String hidden() {
        return "Swagger에 λ“±λ‘λ˜μ§€ μ•ŠλŠ” API";
    }

 

728x90
λ°˜μ‘ν˜•