ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] REST API 만들기(14) - Swagger 적용
    Spring Boot/2.4.x - REST API 만들기 2020. 5. 22. 19:27
    반응형

    API 애플리케이션을 구현할 때 API 목록을 HTML 화면으로 문서화하기 위해 방법으로 Swagger가 있습니다. SpringBoot에서 Swagger를 사용하면, 컨트롤러에 적용된 어노테이션을 해석하여 문서화할 수 있습니다.


    1. 의존성 추가

    pom.xml에 Swagger에 대한 의존성을 추가하세요.

    pom.xml

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.8</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.api</groupId>
        <artifactId>board</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>board</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
     
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-configuration-processor -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.4</version>
            </dependency>
             
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1 -->
            <dependency>
                <groupId>org.bgee.log4jdbc-log4j2</groupId>
                <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
                <version>1.16</version>
            </dependency>
            
            <!-- JUnit4 사용하기 위해서 Vintage Engine 모듈을 제외 -->
            <dependency>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.hamcrest</groupId>
                        <artifactId>hamcrest-core</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-oxm -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-oxm</artifactId>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-jaxb-annotations -->
            <dependency>
                <groupId>com.fasterxml.jackson.module</groupId>
                <artifactId>jackson-module-jaxb-annotations</artifactId>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
            
        </dependencies>
     
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
     
    </project>
    cs

     

    2. Swagger 설정 

    Swagger 설정을 위해 SwaggerConfig 클래스를 생성하세요.

    SwaggerConfig.java

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    package com.api.board.config;
     
    import java.util.HashSet;
    import java.util.Set;
     
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    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;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
     
    @EnableSwagger2
    @Configuration
    public class SwaggerConfig extends WebMvcConfigurationSupport {
     
        @Bean
        public Docket api() {
            return new Docket(DocumentationType.SWAGGER_2).consumes(getConsumeContentTypes())
                                                          .produces(getProduceContentTypes())
                                                          .apiInfo(getApiInfo())
                                                          .select()
                                                          .apis(RequestHandlerSelectors.basePackage("com.api.board.controller"))
                                                          .paths(PathSelectors.ant("/board/**"))
                                                          .build();
        }
     
        private Set<String> getConsumeContentTypes() {
            Set<String> consumes = new HashSet<>();
            consumes.add("application/json;charset=UTF-8");
            consumes.add("application/x-www-form-urlencoded");
            return consumes;
        }
     
        private Set<String> getProduceContentTypes() {
            Set<String> produces = new HashSet<>();
            produces.add("application/json;charset=UTF-8");
            return produces;
        }
     
        private ApiInfo getApiInfo() {
            return new ApiInfoBuilder().title("REST API 게시판 만들기")
                                       .description("API Docs")
                                       .version("1.0")
                                       .build();
        }
     
        /** 404 Not Found가 발생하는 경우 swagger-ui.html 위치를 추가 */
        @Override
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("swagger-ui.html**")
                    .addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**")
                    .addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
    }
    cs

    @EnableSwagger2

    Spring Boot에서 Swagger2를 사용하겠다는 어노테이션입니다.

    Docket

    Swagger 설정을 담당하는 Bean입니다.

    설정 정보

    .consume()과 .produces()는 각각 Request Content-Type, Response Content-Type에 대한 설정입니다. (선택)

    .apiInfo()는 Swagger API 문서에 대한 설명을 표기하는 메소드입니다. (선택)

    .apis()는 Swagger API 문서로 만들기 원하는 basePackage 경로입니다. (필수)

    .path()는 URL 경로를 지정하여 해당 URL에 해당하는 요청만 Swagger API 문서로 만듭니다.(필수)

     

    3. Swagger 어노테이션 설정

    @Api - 컨트롤러 단위로 API 메타데이터 표현할 때 사용합니다.

    @ApiOperation - 하나의 엔드포인트 단위로 메타데이터를 표현할 때 사용합니다.

    @ApiResponse - 엔드포인트에서 반환하는 HttpStatus와 Meessage를 표현할 때 사용합니다.

    @ApiModel - 모델을 표현할 때 사용합니다.

    @ApiParam, @ApiImplicitParam - API 호출 시 전달되는 파라미터를 표현할 때 사용합니다.

    @ApiModelProperty - 모델 내의 필드를 표현할 때 사용합니다.

    BoardController.java

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    package com.api.board.controller;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.DeleteMapping;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.PutMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.web.bind.annotation.RestController;
     
    import com.api.board.domain.Board;
    import com.api.board.domain.Boards;
    import com.api.board.exception.ResourceNotFoundException;
    import com.api.board.service.BoardService;
     
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
     
    @Api(tags = "게시글 관련한 API : /board")
    @RequestMapping("/board")
    @RestController
    public class BoardController {
     
        @Autowired
        private BoardService boardService;
     
        /** 게시글 목록 조회 */
        @ApiOperation(value = "게시글 목록 조회", notes = "게시글 목록을 조회합니다.")
        @GetMapping
        public Boards getBoardList() throws Exception {
            
            Boards boards = new Boards();
            boards.setBoards(boardService.getBoardList());
            
            return boards;
        }
     
        /** 게시글 상세 조회 */
        @ApiOperation(value = "게시글 상세 조회", notes = "게시글를 상세 조회합니다.")
        @GetMapping("/{board_seq}")
        public Board getBoardDetail(@PathVariable("board_seq"int board_seq) throws Exception {
            
            Board board = boardService.getBoardDetail(board_seq);
            
            if(board == null) {
                throw new ResourceNotFoundException();
            }
            
            return board; 
        }
     
        /** 게시글 등록  */
        @ApiOperation(value = "게시글 등록", notes = "게시글을 등록합니다.")
        @ResponseStatus(value = HttpStatus.CREATED)
        @PostMapping
        public Board insertBoard(@RequestBody Board board) throws Exception {
     
            boardService.insertBoard(board);
     
            int boardSeq = board.getBoard_seq();
     
            Board boardDetail = boardService.getBoardDetail(boardSeq);
     
            return boardDetail;
        }
     
        /** 게시글 수정 */
        @ApiOperation(value = "게시글 수정", notes = "게시글을 수정합니다.")
        @ResponseStatus(value = HttpStatus.OK)
        @PutMapping("/{board_seq}")
        public Board updateBoard(@PathVariable("board_seq"int board_seq, @RequestBody Board board) throws Exception {
     
            boardService.updateBoard(board);
     
            Board boardDetail = boardService.getBoardDetail(board_seq);
     
            return boardDetail;
        }
     
        /** 게시글 삭제 */
        @ApiOperation(value = "게시글 삭제", notes = "게시글을 삭제합니다.")
        @ResponseStatus(value = HttpStatus.OK)
        @DeleteMapping("/{board_seq}")
        public Board deleteBoard(@PathVariable("board_seq"int board_seq) throws Exception {
     
            boardService.deleteBoard(board_seq);
     
            Board deleteBoard = new Board();
            deleteBoard.setBoard_seq(board_seq);
     
            return deleteBoard;
        }
    }
    cs

     

    Boards.java

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package com.api.board.domain;
     
    import java.util.List;
     
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;
     
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
     
    @ApiModel(value = "게시글 목록 : Boards", description = "게시글 목록")
    @XmlRootElement(name = "boards")
    public class Boards {
     
        @ApiModelProperty(value = "게시글 목록")
        private List<Board> boards;
        
        public Boards() {
        }
     
        public Boards(List<Board> boards) {
            setBoards(boards);
        }
     
        @XmlElement(name = "board")
        public List<Board> getBoards() {
            return boards;
        }
     
        public void setBoards(List<Board> boards) {
            this.boards = boards;
        }
    }
    cs

     

    Board.java

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    package com.api.board.domain;
     
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.XmlType;
     
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
     
    @ApiModel(value = "게시글 정보 : Board", description = "게시글 정보")
    @XmlRootElement(name = "board")
    @XmlType(propOrder = {"board_seq""board_re_ref""board_re_lev""board_re_seq""board_writer""board_subject""board_content""board_hits""del_yn""ins_user_id""ins_date""upd_user_id""upd_date"})
    public class Board {
     
        @ApiModelProperty(value = "게시글 번호")
        int board_seq;
        @ApiModelProperty(value = "게시글 그룹 번호")
        int board_re_ref;
        @ApiModelProperty(value = "게시글 답변 글의 깊이")
        int board_re_lev;
        @ApiModelProperty(value = "게시글 답변 글의 순서")
        int board_re_seq;
        @ApiModelProperty(value = "게시글 작성자")
        String board_writer;
        @ApiModelProperty(value = "게시글 제목")
        String board_subject;
        @ApiModelProperty(value = "게시글 내용")
        String board_content;
        @ApiModelProperty(value = "게시글 조회수")
        int board_hits;
        @ApiModelProperty(value = "게시글 삭제 유무")
        String del_yn;
        @ApiModelProperty(value = "게시글 입력자 ID")
        String ins_user_id;
        @ApiModelProperty(value = "게시글 입력 일시")
        String ins_date;
        @ApiModelProperty(value = "게시글 수정자 ID")
        String upd_user_id;
        @ApiModelProperty(value = "게시글 수정 일시")
        String upd_date;
     
        public int getBoard_seq() {
            return board_seq;
        }
     
        public void setBoard_seq(int board_seq) {
            this.board_seq = board_seq;
        }
     
        public int getBoard_re_ref() {
            return board_re_ref;
        }
     
        public void setBoard_re_ref(int board_re_ref) {
            this.board_re_ref = board_re_ref;
        }
     
        public int getBoard_re_lev() {
            return board_re_lev;
        }
     
        public void setBoard_re_lev(int board_re_lev) {
            this.board_re_lev = board_re_lev;
        }
     
        public int getBoard_re_seq() {
            return board_re_seq;
        }
     
        public void setBoard_re_seq(int board_re_seq) {
            this.board_re_seq = board_re_seq;
        }
     
        public String getBoard_writer() {
            return board_writer;
        }
     
        public void setBoard_writer(String board_writer) {
            this.board_writer = board_writer;
        }
     
        public String getBoard_subject() {
            return board_subject;
        }
     
        public void setBoard_subject(String board_subject) {
            this.board_subject = board_subject;
        }
     
        public String getBoard_content() {
            return board_content;
        }
     
        public void setBoard_content(String board_content) {
            this.board_content = board_content;
        }
     
        public int getBoard_hits() {
            return board_hits;
        }
     
        public void setBoard_hits(int board_hits) {
            this.board_hits = board_hits;
        }
     
        public String getDel_yn() {
            return del_yn;
        }
     
        public void setDel_yn(String del_yn) {
            this.del_yn = del_yn;
        }
     
        public String getIns_user_id() {
            return ins_user_id;
        }
     
        public void setIns_user_id(String ins_user_id) {
            this.ins_user_id = ins_user_id;
        }
     
        public String getIns_date() {
            return ins_date;
        }
     
        public void setIns_date(String ins_date) {
            this.ins_date = ins_date;
        }
     
        public String getUpd_user_id() {
            return upd_user_id;
        }
     
        public void setUpd_user_id(String upd_user_id) {
            this.upd_user_id = upd_user_id;
        }
     
        public String getUpd_date() {
            return upd_date;
        }
     
        public void setUpd_date(String upd_date) {
            this.upd_date = upd_date;
        }
    }
    cs

     

    4. Swagger API 문서 확인 

    http://localhost:8080/swagger-ui.html 접속 시 다음과 같은 페이지가 출력되는지 확인하세요. 

    springfox의 swagger2 (2.9.x)를 사용하는데 404 Not Found 에러가 발생하는 경우 SwaggerConfig 클래스에서 WebMvcConfiguarationSupport 클래스를 상속받은 후 addResourceHandlers 메서드를 사용하여 swagger-ui.html의 위치를 설정하세요.

     

    소스 코드는 Github Repository - https://github.com/tychejin1218/api-board_v1 (branch : section14) 를 참조하세요.
    Github에서 프로젝트 가져오기 - 
    https://tychejin.tistory.com/33

    반응형

    댓글

Designed by Tistory.