[SpringBoot] Swagger ui 3.0 ์—ฐ๋™ํ•˜๊ธฐ

    ๋ฐ˜์‘ํ˜•

    1. Swagger 3.0 ์„ธํŒ…ํ•˜๊ธฐ.

    ๊ตฌ๊ธ€์— ๊ฒ€์ƒ‰ํ•ด์„œ ๋‚˜์˜จ ์Šค์›จ๊ฑฐ ์„ธํŒ… ๊ด€๋ จ ํฌ์ŠคํŠธ๋Š” ๋Œ€๋ถ€๋ถ„ 2.X ๋ฒ„์ „๋Œ€ ์ด๊ฑฐ๋‚˜, 3.0 ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ฐ›์•˜์Œ์—๋„, ์Šค์›จ๊ฑฐ ์„ธํŒ…์—์„œ SWAGGER2๋ฒ„์ „์œผ๋กœ ๋‚ฎ์ถ”์–ด ์‚ฌ์šฉํ•˜๋Š” ๊ธ€์ด ํ˜ผ์žฌํ•˜์—ฌ ์ •๋ฆฌํ•˜๊ณ ์ž ๊ธ€์„ ์ž‘์„ฑํ•œ๋‹ค.

    ์šฐ์„  ๋‚ด๊ฐ€ ์ ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ๋ฒ„์ „์€ 2.7.1 ๋ฒ„์ „์ด๋ฉฐ, ์Šค์›จ๊ฑฐ๋Š” open api 3.0 OAS3 ๋ฌธ์„œ ๋ฒ„์ „์„ ๋งŒ๋“ค๊ณ ์ž ํ•œ๋‹ค.

    - build.gradle 

    // https://mvnrepository.com/artifact/io.springfox/springfox-boot-starter
    implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0'
    // https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui
    implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '3.0.0'
    // https://mvnrepository.com/artifact/io.springfox/springfox-oas
    implementation group: 'io.springfox', name: 'springfox-oas', version: '3.0.0'

    ์œ„ 3๊ฐ€์ง€ ๋ฒ„์ „์„ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ, ์ •์ƒ ๊ตฌ๋™์ด ๊ฐ€๋Šฅ ํ–ˆ๋‹ค. ๋ณดํ†ต์€ springfox-boot-starter๋งŒ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค๋Š” ์–˜๊ธฐ๊ฐ€ ๋งŽ์•˜๋Š”๋ฐ, ๋‚ด ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜ 2๊ฐ€์ง€๋„ ์žˆ์–ด์•ผ ๊ตฌ๋™์ด ์ •์ƒ์ ์œผ๋กœ ๊ฐ€๋Šฅํ–ˆ๋‹ค.

    - SwaggerConfig.java

    package com.sample.swagger.config;
    
    import java.util.Arrays;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    
    @EnableWebMvc
    @Configuration
    public class SwaggerConfig extends WebMvcConfigurationSupport {
    
        @Bean
        public Docket api(TypeResolver typeResolver) {
            return new Docket(DocumentationType.OAS_30) // 3.0 ๋ฌธ์„œ๋ฒ„์ „์œผ๋กœ ์„ธํŒ…
                    .useDefaultResponseMessages(true)
                    .apiInfo(apiInfo())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.sample.swagger.controller"))
                    .paths(PathSelectors.any())
                    .build();
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("Swagger 3.0 Api Sample")
                    .description("This is Sample")
                    .version("1.0")
                    .build();
        }
    }

    ์œ„์˜ ์„ค์ •์„ ์›น ์„ค์ •์— ์ถ”๊ฐ€ํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ, MVC์„ค์ •์„ ๋”ฐ๋กœ ํ•ด์ฃผ์ง€ ์•Š๋Š” ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์•„๋ž˜์˜ ์„ค์ •๋„ ๊ฐ™์ด ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

    - WebConfig.java

    package com.sample.swagger.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/swagger-ui/**")
                    .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
                    .resourceChain(false);
        }
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/swagger-ui/")
                    .setViewName("forward:/swagger-ui/index.html");
        }
    
    }

    ์œ„ ์„ค์ •์ด ์—†๋‹ค๋ฉด, ์•„๋ฌด๋ฆฌ ํ”„๋กœ์ ํŠธ๋ฅผ ๋Œ๋ ค๋„ ์Šค์›จ๊ฑฐ ํŒŒ์‹ฑ ํŽ˜์ด์ง€์— ์ ‘๊ทผ ํ•  ์ˆ˜ ๊ฐ€ ์—†๋‹ค. (๋ฆฌ์†Œ์Šค๊ฐ€ ์—†๋‹ค๊ณ  ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.)

    2. @Schema ์ž‘์„ฑ

    Swagger 3.0 ๋ฒ„์ „์—์„œ๋Š” ApiModelProperty ๋Œ€์‹  Schema ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋ชจ๋ธ์„ ์ž‘์„ฑํ•œ๋‹ค.
    ํŒŒ๋ผ๋ฏธํ„ฐ / ์‹ค์ œ ์‘๋‹ต ๋ฐ์ดํ„ฐ / ์‘๋‹ต ๋‚ด ๋ฐ์ดํ„ฐ 3๊ฐ€์ง€ ์ข…๋ฅ˜์— ๋Œ€ํ•ด ์˜ˆ์ œ๋ฅผ ์ œ๊ณตํ•  ์ƒ๊ฐ์ด๋ฏ€๋กœ, ์„ธ๊ฐœ๋ฅผ ๋‹ค ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.
    ์˜ˆ์ œ ๊ตฌ์กฐ๋Š” dataParam / dataRes / data ๋กœ ์žก์•˜์œผ๋ฉฐ, dataRes ๋‚ด๋ถ€์— List<data> ๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์„ธํŒ…ํ•˜์˜€๋‹ค.

    @Schema 
    - name : ๋ชจ๋ธ๋ช…
    - description : ํ•ด๋‹น ๋ชจ๋ธ์— ๋Œ€ํ•œ ์„ค๋ช…
    - defaultValue : ๊ธฐ๋ณธ ๊ฐ’(์žˆ๋‹ค๋ฉด)
    - allowableValues : ํ—ˆ์šฉ ๊ฐ’ ๋ฒ”์œ„(์žˆ๋‹ค๋ฉด)
    - example : ์˜ˆ์ œ ๋ฐ์ดํ„ฐ (์ž‘์„ฑํ•ด์•ผ ์Šค์›จ๊ฑฐ ๋ฌธ์„œ์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ผ๋ฐ˜ ํƒ€์ž…์ด ์•„๋‹Œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚จ)

    - dataParam.java

    package com.sample.swagger.data;
    
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Getter;
    import lombok.Setter;
    
    @Schema(name = "dataParam", description = "์œ ์ € ์กฐํšŒ ํŒŒ๋ผ๋ฏธํ„ฐ")
    @Getter
    @Setter
    public class dataParam {
        @Schema(description = "์š”์ฒญ ๊ฐœ์ˆ˜", defaultValue = "", allowableValues = {}, example = "1")
        private int num;
    }

    - data.java

    package com.sample.swagger.data;
    
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Getter;
    import lombok.Setter;
    
    @Schema(name = "data", description = "์œ ์ € ๋ฐ์ดํ„ฐ")
    @Getter
    @Setter
    public class data {
        @Schema(description = "์œ ์ € ๋ฒˆํ˜ธ", defaultValue = "", allowableValues = {}, example = "1")
        private int userNo;
        @Schema(description = "์œ ์ € ์ด๋ฆ„", defaultValue = "", allowableValues = {}, example = "user1")
        private String username;
    }

    - dataRes.java

    package com.sample.swagger.data;
    
    import java.util.List;
    
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Getter;
    import lombok.Setter;
    
    @Schema(name = "dataRes", description = "์œ ์ € ์กฐํšŒ ์‘๋‹ต ๋ฐ์ดํ„ฐ")
    @Getter
    @Setter
    public class dataRes {
        @Schema(description = "์œ ์ € ๋ฆฌ์ŠคํŠธ", defaultValue = "", allowableValues = {}, example = "")
        private List<data> dataList;
    }

     

    ๊ทธ๋ฆฌ๊ณ , ์‘๋‹ต์„ ์ •์˜ํ•  ํด๋ž˜์Šค๋„ ์ž‘์„ฑํ•˜์˜€๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ๋ชฉํ•  ์ ์€ ๊ธ€๋กœ๋ฒŒ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํ˜น์€ ๊ธฐํƒ€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋กœ ์ธํ•ด ๋ฆฌํ„ด๋˜๋Š” ๋ฐ์ดํ„ฐ ํ˜•ํƒœ๋Š” ์Šค์›จ๊ฑฐ์—์„œ ์žก์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ๋ณ„๋„๋กœ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ์˜ˆ์™ธ ์ผ€์ด์Šค์˜ ๊ฒฝ์šฐ(์˜ˆ๋ฅผ ๋“ค์–ด, 200 ์ •์ƒ ๋ฆฌํ„ด ์™ธ, 400-500๋Œ€ ๋“ฑ์˜ ์—๋Ÿฌ๋ฅผ ์Šค์›จ๊ฑฐ์—์„œ ์ œ๊ณตํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ.) ๋ณ„๋„ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜์—ฌ @Schema ์–ด๋…ธํ…Œ์ด์…˜์„ ์ค€ ํ›„, ์‘๋‹ต์— ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

    ์šฐ์„ , ์‘๋‹ต ํด๋ž˜์Šค 3๊ฐ€์ง€๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

    - ApiRes : ์ •์ƒ ์‘๋‹ต ์‹œ, ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ฆฌํ„ดํ•˜๋Š” ํด๋ž˜์Šค

    package com.sample.swagger.response;
    
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Getter;
    import lombok.Setter;
    
    @Schema(name = "ApiRes", description = "Api Response Format")
    @Getter
    @Setter
    public class ApiRes<T> {
        @Schema(description = "์š”์ฒญ๊ฒฐ๊ณผ ์ƒํƒœ", nullable = false, example = "success")
        private String result_status;
        @Schema(description = "์š”์ฒญ๊ฒฐ๊ณผ", nullable = false, example = "")
        private T result;
    
        public ApiRes(T result) {
            this.result_status = "success";
            this.result = result;
        }
    
    }

    - ApiErr : ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ๋ณด์—ฌ์ค„ ์—๋Ÿฌ

    package com.sample.swagger.response;
    
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Getter;
    import lombok.Setter;
    
    @Schema(name = "ApiErr", description = "Api Response Err")
    @Getter
    @Setter
    public class ApiErr {
        @Schema(description = "์š”์ฒญ๊ฒฐ๊ณผ ์ƒํƒœ", nullable = false, example = "fail")
        private String error_status;
        @Schema(description = "์š”์ฒญ๊ฒฐ๊ณผ ๋ฉ”์„ธ์ง€", nullable = false, example = "์š”์ฒญ์ด ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.")
        private String error_message;
    
        public ApiErr(String error_status, String error_message) {
            this.error_status = error_status;
            this.error_message = error_message;
        }
    }

    - ApiNoAuth : ๊ถŒํ•œ ์—†๋Š” ๊ฒฝ์šฐ ๋ณด์—ฌ์ค„ ์—๋Ÿฌ

    package com.sample.swagger.response;
    
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Getter;
    import lombok.Setter;
    
    @Schema(name = "ApiNoAuth", description = "Api Response No Auth")
    @Getter
    @Setter
    public class ApiNoAuth {
        @Schema(description = "์š”์ฒญ๊ฒฐ๊ณผ ์ƒํƒœ", nullable = false, example = "fail")
        private String error_status;
        @Schema(description = "์š”์ฒญ๊ฒฐ๊ณผ ๋ฉ”์„ธ์ง€", nullable = false, example = "๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.")
        private String error_message;
    }

     

    ๋ฐ์ดํ„ฐ ๋ฐ ์‘๋‹ต์— ๋Œ€ํ•œ ์Šคํ‚ค๋งˆ๋ฅผ ์ž‘์„ฑํ•˜์˜€์œผ๋ฉด, ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์ด๋™ํ•˜์—ฌ ์ปจํŠธ๋กค๋Ÿฌ์— ๋Œ€ํ•ด ์ •์˜ํ•œ๋‹ค.

    3. @ApiResponse, @Tag, @ApiOperation

    @Tag : ๊ฐ™์€ ํƒœ๊ทธ๋กœ ์ž‘์„ฑ๋œ ์ปจํŠธ๋กค๋Ÿฌ๋“ค์„ ๋ฌถ์–ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
    - name : ํƒœ๊ทธ๋ช… ์ž‘์„ฑ
    - description : ํƒœ๊ทธ์— ๋Œ€ํ•œ ์„ค๋ช…
    @ApiResponse : ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์ •์˜ํ•œ๋‹ค.
    - ์ƒ์œ„์— @ApiResponses(values={}) ๋กœ ๋ฌถ์ธ๋‹ค.
    - responseCode : ์‘๋‹ต์ฝ”๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค.
    - description : ํ•ด๋‹น ์‘๋‹ต์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์ •์˜ํ•œ๋‹ค.
    - content : ํ•ด๋‹น ์‘๋‹ต์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ •์˜ํ•œ๋‹ค. @Content๋ฅผ ํ†ตํ•ด ๋‚ด์šฉ์„ ๋”ฐ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ณดํ†ต @Schema implemention์„ ํ†ตํ•ด ๋ฏธ๋ฆฌ ์ •์˜๋œ ํด๋ž˜์Šค ์Šคํ‚ค๋งˆ๋ฅผ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
    @ApiOperation: API ๋™์ž‘์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์ •์˜ํ•œ๋‹ค.
    - value : ๋™์ž‘ ๊ฐ’์„ ์ •์˜ํ•œ๋‹ค.
    - notes : ๋™์ž‘์— ๋Œ€ํ•œ ์„ค๋ช…์„ ์ •์˜ํ•œ๋‹ค.
    - authorizations : ๋’ค์—์„œ ์–ธ๊ธ‰ํ•  ์ธ์ฆ ๊ด€๋ จ ๊ฐ’์„ ์ •์˜ํ•œ๋‹ค. (ํ‚ค ๊ด€๋ จ)

    - ApiController.java

    package com.sample.swagger.controller;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.sample.swagger.data.data;
    import com.sample.swagger.data.dataParam;
    import com.sample.swagger.data.dataRes;
    import com.sample.swagger.response.ApiErr;
    import com.sample.swagger.response.ApiNoAuth;
    import com.sample.swagger.response.ApiRes;
    
    import io.swagger.annotations.ApiOperation;
    import io.swagger.annotations.Authorization;
    import io.swagger.v3.oas.annotations.media.Content;
    import io.swagger.v3.oas.annotations.media.Schema;
    import io.swagger.v3.oas.annotations.responses.ApiResponse;
    import io.swagger.v3.oas.annotations.responses.ApiResponses;
    import io.swagger.v3.oas.annotations.tags.Tag;
    
    @Tag(name = "user", description = "get user list")
    @RestController
    @RequestMapping("/api")
    public class ApiController {
    
        @Tag(name = "user")
        @ApiOperation(value = "์œ ์ € ๋ฆฌ์ŠคํŠธ", notes = "ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ ์ˆ˜ ๋งŒํผ ์œ ์ €๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.", authorizations = {
                @Authorization(value = "apiKey") })
        @ApiResponses(value = {
                @ApiResponse(responseCode = "200", description = "์š”์ฒญ ์„ฑ๊ณต", content = @Content(schema = @Schema(implementation = ApiRes.class))),
                @ApiResponse(responseCode = "201", description = "500๊ณผ ๋™์ผ"),
                @ApiResponse(responseCode = "401", description = "๊ถŒํ•œ ์—†์Œ(ํ‚ค๋ˆ„๋ฝ)", content = @Content(schema = @Schema(implementation = ApiNoAuth.class))),
                @ApiResponse(responseCode = "403", description = "500๊ณผ ๋™์ผ"),
                @ApiResponse(responseCode = "404", description = "500๊ณผ ๋™์ผ"),
                @ApiResponse(responseCode = "500", description = "์š”์ฒญ ์‹คํŒจ", content = @Content(schema = @Schema(implementation = ApiErr.class)))
        })
        @GetMapping(value = "/getUser", produces = "application/json")
        public ResponseEntity<ApiRes<dataRes>> getUser(@RequestParam int num) {
            dataRes res = new dataRes();
            List<data> result = new ArrayList<data>();
            for (int i = 0; i < num; i++) {
                data d = new data();
                d.setUserNo(i);
                d.setUsername("user" + i);
                result.add(d);
            }
            res.setDataList(result);
            return new ResponseEntity<ApiRes<dataRes>>(new ApiRes<dataRes>(res), HttpStatus.OK);
        }
    
        @Tag(name = "user")
        @ApiOperation(value = "์œ ์ € ๋ฆฌ์ŠคํŠธ", notes = "ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ ์ˆ˜ ๋งŒํผ ์œ ์ €๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.", authorizations = {
                @Authorization(value = "apiKey") })
        @ApiResponses(value = {
                @ApiResponse(responseCode = "200", description = "์š”์ฒญ ์„ฑ๊ณต", content = @Content(schema = @Schema(implementation = ApiRes.class))),
                @ApiResponse(responseCode = "201", description = "500๊ณผ ๋™์ผ"),
                @ApiResponse(responseCode = "401", description = "๊ถŒํ•œ ์—†์Œ(ํ‚ค๋ˆ„๋ฝ)", content = @Content(schema = @Schema(implementation = ApiNoAuth.class))),
                @ApiResponse(responseCode = "403", description = "500๊ณผ ๋™์ผ"),
                @ApiResponse(responseCode = "404", description = "500๊ณผ ๋™์ผ"),
                @ApiResponse(responseCode = "500", description = "์š”์ฒญ ์‹คํŒจ", content = @Content(schema = @Schema(implementation = ApiErr.class)))
        })
        @PostMapping(value = "/getUser", produces = "application/json")
        public ResponseEntity<ApiRes<dataRes>> postUser(@RequestBody dataParam param) {
            dataRes res = new dataRes();
            List<data> result = new ArrayList<data>();
            for (int i = 0; i < param.getNum(); i++) {
                data d = new data();
                d.setUserNo(i);
                d.setUsername("user" + i);
                result.add(d);
            }
            res.setDataList(result);
            return new ResponseEntity<ApiRes<dataRes>>(new ApiRes<dataRes>(res), HttpStatus.OK);
        }
    }

     

    ์‘๋‹ต์— ์ž‘์„ฑ๋œ ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•˜๊ณ  ๋Œ๋ ค๋ณด๋ฉด ๊ฐ€๋” ์˜ค๋ฅ˜๋กœ ์ฐพ์ง€ ๋ชปํ•œ๋‹ค๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ๋œฐ ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋Ÿด๋•Œ๋Š” ์„ค์ • ์ชฝ์„ ๊ฑด๋“œ๋ ค์„œ ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. 

    ์ถ”์ธก์œผ๋กœ๋Š” ์‹ค์ œ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ฆฌํ„ดํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์žก์ง€ ๋ชปํ•˜๋Š”๊ฒŒ ์•„๋‹Œ๊ฐ€ ํ•œ๋‹ค.

    - SwaggerConfig.java

    @Bean
    public Docket api(TypeResolver typeResolver) {
    	return new Docket(DocumentationType.OAS_30)
    	// * ์‹ค์ œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋กœ ๋ฆฌํ„ดํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ๋ช…์‹œํ•˜๊ณ ์ž ํ•  ๋•Œ ํ•ด๋‹น ๋ชจ๋ธ์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.
    	.additionalModels(
    	typeResolver.resolve(ApiRes.class),
    	typeResolver.resolve(ApiErr.class),
    	typeResolver.resolve(ApiNoAuth.class))
    	// * ์Šคํ‚ค๋งˆ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ์ค‘, Date ๊ด€๋ จ ๋ณ€์ˆ˜ ๋ฌธ์ œ๋กœ ์ธํ•ด ์„ค์ • ์ถ”๊ฐ€
    	.directModelSubstitute(LocalDate.class, java.sql.Date.class)
    	.directModelSubstitute(LocalDateTime.class, java.util.Date.class)
    	.useDefaultResponseMessages(true)
    	.apiInfo(apiInfo())
    	.select()
    	.apis(RequestHandlerSelectors.basePackage("com.sample.swagger.controller"))
    	.paths(PathSelectors.any())
    	.build();
    }


    ์ด๋ ‡๊ฒŒ ๊นŒ์ง€๋งŒ ์ž‘์„ฑํ•˜๊ณ  ๋กœ์ปฌ ๊ตฌ๋™ํ•œ ๋’ค, http://localhost:8080/swagger-ui/index.html ์„ ์ ‘์†ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ™”๋ฉด์„ ๋งŒ๋‚˜๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    4. ํ‚ค ๊ด€๋ จ ์„ค์ • 

    Swagger์—์„œ ๊ฐ€์žฅ ํฐ ํŠน์ง• ์ค‘ ํ•˜๋‚˜๋Š” ํ‚ค ๊ด€๋ จ ์„ค์ •์„ ์ œ๊ณตํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ฐพ์•„๋ณด๋ฉด ์‹ค์ œ ํ† ํฐ ๊ฐ’ ๋˜๋Š” ์ฒ˜๋ฆฌ๋˜๋Š” ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

    ์šฐ์„  ํ˜„์žฌ ์ƒ˜ํ”Œ๋กœ ์ž‘์„ฑํ•œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋‹จ์ˆœํžˆ ํ—ค๋”์— ํŠน์ • ๊ฐ’์ด ์žˆ๋Š”์ง€๋ฅผ ์ฒดํฌํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์ˆœํ•˜๊ฒŒ ์ง„ํ–‰ํ–ˆ๋‹ค. ์„ค์ •์€ ๋‚จ๊ฒจ๋†“์•˜์œผ๋‹ˆ ์ฐธ๊ณ  ํ•˜๋ฉด ๋œ๋‹ค.

    - SwaggerConfig.java

    package com.sample.swagger.config;
    
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.util.Arrays;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    import com.fasterxml.classmate.TypeResolver;
    import com.sample.swagger.response.ApiErr;
    import com.sample.swagger.response.ApiNoAuth;
    import com.sample.swagger.response.ApiRes;
    
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.ApiKey;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    
    @EnableWebMvc
    @Configuration
    public class SwaggerConfig extends WebMvcConfigurationSupport {
    
        @Bean
        public Docket api(TypeResolver typeResolver) {
            return new Docket(DocumentationType.OAS_30)
                    // * ์‹ค์ œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋กœ ๋ฆฌํ„ดํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ๋ช…์‹œํ•˜๊ณ ์ž ํ•  ๋•Œ ํ•ด๋‹น ๋ชจ๋ธ์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.
                    .additionalModels(
                            typeResolver.resolve(ApiRes.class),
                            typeResolver.resolve(ApiErr.class),
                            typeResolver.resolve(ApiNoAuth.class))
                    // * ์Šคํ‚ค๋งˆ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜ ์ค‘, Date ๊ด€๋ จ ๋ณ€์ˆ˜ ๋ฌธ์ œ๋กœ ์ธํ•ด ์„ค์ • ์ถ”๊ฐ€
                    .directModelSubstitute(LocalDate.class, java.sql.Date.class)
                    .directModelSubstitute(LocalDateTime.class, java.util.Date.class)
                    .useDefaultResponseMessages(true)
                    .apiInfo(apiInfo())
                    // ์ธ์ฆ ํ† ํฐ ๋ฐฉ์‹์ด ์žˆ์„๋•Œ๋งŒ ์‚ฌ์šฉ.
                    // .securityContexts(Arrays.asList(securityContext()))
                    .securitySchemes(Arrays.asList(apiKey()))
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.sample.swagger.controller"))
                    .paths(PathSelectors.any())
                    .build();
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("Swagger 3.0 Api Sample")
                    .description("This is Sample")
                    .version("1.0")
                    .build();
        }
    
        // ์ธ์ฆ ํ† ํฐ ๋ฐฉ์‹์ด ์žˆ์„๋•Œ๋งŒ ์‚ฌ์šฉ.
        /*
         * private SecurityContext securityContext() {
         * return SecurityContext.builder()
         * .securityReferences(defaultAuth())
         * .build();
         * }
         */
    
        // ์ธ์ฆ ํ† ํฐ ๋ฐฉ์‹์ด ์žˆ์„๋•Œ๋งŒ ์‚ฌ์šฉ.
        /*
         * private List<SecurityReference> defaultAuth() {
         * AuthorizationScope authorizationScope = new AuthorizationScope("global",
         * "accessEverything");
         * AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
         * authorizationScopes[0] = authorizationScope;
         * return Arrays.asList(new SecurityReference("Authorization",
         * authorizationScopes));
         * }
         */
    
        private ApiKey apiKey() {
            return new ApiKey("apiKey", "apiKey", "header");
        }
    }

     

    ์œ„์—์„œ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ apiKey() ๋ถ€๋ถ„์ธ๋ฐ, ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆœ์„œ๋Œ€๋กœ name, keyname, passAs ์ด๋‹ค. ์ด๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ž‘์„ฑํ•˜๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ์ปจํŠธ๋กค๋Ÿฌ์˜ @ApiOperation ๋ถ€๋ถ„์— ๋‚ด๋ถ€ ์˜ต์…˜์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค

    - ApiController.java

    @ApiOperation(value = "์œ ์ € ๋ฆฌ์ŠคํŠธ", notes = "ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ ์ˆ˜ ๋งŒํผ ์œ ์ €๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.", authorizations = {@Authorization(value = "apiKey") })

     

    ํ•ด๋‹น ์ž‘์—…์„ ์™„๋ฃŒํ•˜๊ณ  ๋‹ค์‹œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋Œ๋ ค ํ™•์ธํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฒ„ํŠผ์ด ์ถ”๊ฐ€๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    ํ•ด๋‹น ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํŒ์—…์ด ๋œฌ๋‹ค.

    ์—ฌ๊ธฐ์— ๊ฐ’์„ ์ž…๋ ฅ ํ›„, Authorize๋ฅผ ํด๋ฆญํ•˜๋ฉด, ํ•ด๋‹น ๊ฐ’์ด ์ €์žฅ๋˜์–ด ํ…Œ์ŠคํŠธ ์‹œ ๊ฐ™์ด ์ „๋‹ฌ ๋œ๋‹ค.

     

    ๋˜๊ฒŒ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•œ๋‹ค๊ณ  ์‹œ์ž‘ํ–ˆ๋Š”๋ฐ ์ •๋ฆฌํ•˜๋Š”๋ฐ ์ƒ๊ฐ๋ณด๋‹ค ์‹œ๊ฐ„์ด ๋งŽ์ด ๊ฑธ๋ ธ๋‹ค. ๋ฌด์—‡๋ณด๋‹ค ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์ž‘์—…ํ•˜๋Š” ์˜ˆ์ œ๊ฐ€ ๋งŽ์ด ์—†์–ด์„œ ๊ทธ๋ ‡๊ธฐ๋„ ํ•˜๊ณ , ๋ฒ„์ „์— ๋”ฐ๋ผ ์˜ต์…˜ ๊ฐ’์ด๋‚˜ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ผ์ง€๋Š” ๋ถ€๋ถ„๋„ ์žˆ๋Š”๋ฐ ์ฐพ๊ธฐ๊ฐ€ ๋งค์šฐ ํž˜๋“ค์—ˆ๋‹ค.

    Swagger๋Š” ์ด๋ ‡๊ฒŒ ํ•œ ๋ฒˆ ์ •๋ฆฌ ํ•ด๋†“์œผ๋ฉด ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ํ•œ ๋ฒˆ ์ฏค์€ ์‹œ๋„ํ•ด๋ณผ ๋งŒ ํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ๋ณธ๋‹ค.

     

    ํ•ด๋‹น ํฌ์ŠคํŠธ์˜ ์˜ˆ์ œ ์†Œ์Šค๋Š” ์•„๋ž˜์—์„œ ๋ณด์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    https://github.com/Chiptune93/spring-example/tree/main/Swagger/3.0/swagger

     

    GitHub - Chiptune93/spring-example: Spring Example

    Spring Example. Contribute to Chiptune93/spring-example development by creating an account on GitHub.

    github.com

     

     

    728x90
    ๋ฐ˜์‘ํ˜•

    ๋Œ“๊ธ€