[SpringBoot] 게시판 구현하기 19 (Swagger)
FRAMEWORK/Spring

[SpringBoot] 게시판 구현하기 19 (Swagger)

반응형

들어가기 전에

하기 포스팅은 "스프링부트 시작하기(김인우 저)" 책을 공부하며 적은 포스팅입니다. 이번 포스팅에서는 스웨거(Swagger)에 대해 알아보도록 하겠습니다.

REST API 문서화하기

이번 포스팅에서 다뤄볼 것은 Swagger를 이용한 REST API 문서화입니다. 일반적으로 여러 명이 개발하는 프로젝트에서는 front 개발자와 back-end 개발자가 나뉘어 있습니다. front는 웹이나 안드로이드 앱과 같이 사용자가 실제로 사용하는 부분을 의미합니다. back-end는 front에서 발생한 사용자의 요청을 실제로 처리하는 서버쪽 어플리케이션을 의미합니다. front 개발자는 사용자에게 보여지는 화면 및 내부 로직에 집중하고, 데이터의 처리는 back-end 개발자가 개발한 API를 사용합니다. 이런 환경에서 API 규격에 대한 문서가 없다면 서로 의사소통을 할 때 어려움을 겪을 수 있습니다. 이러한 문서를 만들 때 도움을 주는 것이 Swagger입니다.

스웨거(Swagger)란?

Swagger는 간단한 설정으로 프로젝트의 API 목록을 웹에서 확인 및 테스트할 수 있게 해주는 라이브러리입니다. Swagger를 사용하면 컨트롤러에 정의되어 있는 모든 URL을 바로 확인할 수 있습니다. API 목록 뿐 아니라 API의 명세 및 설명도 확인할 수 있고, API를 테스트 해 볼 수 도 있습니다.

스웨거(Swagger) 적용하기

먼저 build.gradle에 swagger 라이브러리를 추가합니다.

dependencies {
...
	implementation 'io.springfox:springfox-swagger2:2.9.2'
	implementation 'io.springfox:springfox-swagger-ui:2.9.2'
...
}

Refresh Gradle Project

 

위와 같이 dependency를 추가하고, Refresh gradle project를 하면 제대로 라이브러리가 추가됨을 확인할 수 있습니다. 라이브러리 추가를 완료하면 하기와 같이 configuration 패키지 밑에 SwaggerConfiguration 클래스를 생성하고 코드를 작성합니다.

SwaggerConfiguration 클래스 생성

package board.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

		@Bean
		public Docket api() {
			return new Docket(DocumentationType.SWAGGER_2)
					.select()
					.apis(RequestHandlerSelectors.basePackage("board"))
					.paths(PathSelectors.any())
					.build();
		}
}
  • .apis(RequestHandlerSelectors.basePackage("board")): board 패키지 내 RequestMapping으로 할당된 모든 모든 URL 선택합니다.
  • .paths(PathSelectors.any()): .path(PathSelectors.any("/api/**")와 같이 사용하면 특정 URI를 가진 주소만 선택할 수 있습니다. 여기서는 특정 경로 선택하지 않고 모든 URL을 선택했습니다.
  • Swagger 관련 코드는 이름이 잘 지어져 있습니다. 어노테이션이나 메소드 이름만 봐도 어떠한 역할을 하는지 쉽게 유추가 가능합니다. 

이제 어플리케이션을 실행시키고 localhost:8080/swagger-ui.html을 호출하면 swagger 문서를 확인할 수 있습니다. 기존에 board 패키지 내 모든 URL을 선택했으므로 어플리케이션 내 모든 컨트롤러의 목록을 확인할 수 있습니다. 여기서 각 컨트롤러를 클릭하면 해당 컨트롤러 내 API 목록이 나옵니다.

Swagger 확인

API 각각의 자세한 정보를 알고 싶으면, API를 클릭해보면 됩니다. 아래와 같이 API에서 사용하는 파라미터와 파라미터의 설명, 응답 코드와 응답 코드에 따른 결과까지 확인할 수 있습니다.

swagger 상세 정보

추가적으로, Try it out 버튼을 클릭하면 API 호출에 필요한 파라미터를 입력하는 창이 나옵니다. 해당 창에 파라미터를 입력하고 Excute 버튼을 클릭하면 API 테스트가 가능합니다.

swagger API 호출 테스트

위와 같이 API 테스트를 진행하면 아래와 같은 결과를 확인할 수 있습니다. 아래에 있는 Response body 외에도 Response header 역시 확인이 가능합니다.

API 테스트 결과 예

스웨거(Swagger)에 설명 추가하기

앞선 설정까지 마치면 API 호출에 필요한 파라미터와 결과값을 알 수 있습니다. 하지만, 아직 문서가 정리되었다고 할 수는 없습니다. 아래 사진에서와 같이 Models을 클릭하면 Dto나 View 정보를 확인할 수 있습니다. 해당 정보에는 각 변수가 의미하는 바가 적혀있지 않아 한눈에 찾아보기 어렵습니다. 현재는, Dto나 View가 총 4개이지만 만약 큰 프로젝트라고 하면 문제가 커질 수 있습니다. 따라서 swagger에 설명을 추가해야 합니다.

swagger models

Swagger에 설명을 추가하기 위해 RestBoardApiController를 아래와 같이 변경합니다.

...
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Tag;
...
@Api(description="게시판 REST API")
//@Api(tags = {"Swagger Resource"})
//@SwaggerDefinition(tags = {
//		@Tag(name = "Swagger Resource", description = "게시판 REST API")
//})
@RestController
public class RestBoardApiController {
...
	@ApiOperation(value = "게시글 목록 조회")
	@RequestMapping(value="/api/board", method=RequestMethod.GET)
	public List<BoardDto> openBoardList() throws Exception {
		return boardService.selectBoardList();
	}
	
	@ApiOperation(value = "게시글 작성")
	@RequestMapping(value="/api/board/write", method=RequestMethod.POST)
	public void insertBoard(@RequestBody BoardDto board) throws Exception {
		boardService.insertBoard(board, null);
	}
	
	@ApiOperation(value = "게시글 상세 내용 조회")
	@RequestMapping(value="/api/board/{boardIdx}", method=RequestMethod.GET)
	public BoardDto openBoardDetail(@PathVariable("boardIdx") @ApiParam(value="게시글 번호") int boardIdx) throws Exception {
		return boardService.selectBoardDetail(boardIdx);
	}
	
	@ApiOperation(value = "게시글 상세 내용 수정")
	@RequestMapping(value="/api/board/{boardIdx}", method=RequestMethod.PUT)
	public String updateBoard(@RequestBody BoardDto board) throws Exception {
		boardService.updateBoard(board);
		return "redirect:/board";
	}
	
	@ApiOperation(value = "게시글 삭제")
	@RequestMapping(value="/api/board/{boardIdx}", method=RequestMethod.DELETE)
	@DeleteMapping(value="/board/{boardIdx}")
	public String deleteBoard(@PathVariable("boardIdx") @ApiParam(value="게시글 번호") int boardIdx) throws Exception {
		boardService.deleteBoard(boardIdx);
		return "redirect:/board";
	}
...
  • @Api 어노테이션을 이용해 컨트롤러에 설명을 추가합니다.
  • @ApiOperation, @ApiParam 어노테이션을 이용해 API에 대한 설명 및 API의 파라미터에 대한 설명을 추가합니다.
  • 책에서는, @Api(description="게시판 REST API")로 되어있었으나, Api의 descrption은 deprecated된 속성값입니다.

@Api의 description 속성이 deprecated되어 다른 방식을 찾아보았으나, 해당 방식 역시 제대로 작동이 되지 않음을 확인했습니다. 일단, deprecated된 속성이나, 적용은 되는 것으로 확인되어 책 방식을 사용했습니다.

Deprecated 속성

stackoverflow.com/questions/38074936/api-annotations-description-is-deprecated

 

Api annotation's description is deprecated

In Swagger, the @Api annotation's description element is deprecated. Deprecated. Not used in 1.5.X, kept for legacy support. Is there a newer way of providing the description?

stackoverflow.com

Controller에 설명을 적은 것과 같이 BoardDto 클래스에도 설명을 추가해보겠습니다.

package board.board.dto;

import java.time.LocalDateTime;
import java.util.List;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel(value="BoardDto : 게시글 내용", description="게시글 내용")
@Data
public class BoardDto {
	@ApiModelProperty(value="게시글 번호")
	private int boardIdx;
	
	@ApiModelProperty(value="게시글 제목")
	private String title;
	
	@ApiModelProperty(value="게시글 내용")
	private String contents;
	
	@ApiModelProperty(value="조회수")
	private int hitCnt;
	
	@ApiModelProperty(value="작성자 아이디")
	private String creatorId;
	
	@ApiModelProperty(value="작성시간")
	private LocalDateTime createdDatetime;
	
	@ApiModelProperty(value="수정자 아이디")
	private String updaterId;
	
	@ApiModelProperty(value="수정시간")
	private LocalDateTime updatedDatetime;
	
	@ApiModelProperty(value="첨부파일 목록")
	private List<BoardFileDto> fileList;
}
  • @ApiModel 어노테이션으로 모델에 설명을 추가합니다.
  • @ApiModelProperty 어노테이션으로 모델의 요소에 설명을 추가합니다.

설명 추가한 컨트롤러, Dto 확인

컨트롤러와 Dto에 대해 설명을 추가한 결과 위와 같이 설명이 나옴을 확인할 수 있었습니다. View 클래스나 다른 컨트롤러, Dto에 대해도 위 방식과 동일하게 설정하면 Swagger에서 설명까지 확인할 수 있을 것입니다.

반응형