SPRINGBOOT_SWAGGER
๐ SPRINGBOOT
๐ SWAGGER
SWAGGER ๋
- ๊ฐ๋จํ ์ค์ ์ผ๋ก ํ๋ก์ ํธ์ API ๋ชฉ๋ก์ ์น์์ ํ์ธ ๋ฐ ํ ์คํธ ํ ์ ์๊ฒ ํด์ฃผ๋ Library
- Swagger๋ฅผ ์ฌ์ฉํ๋ฉด Controller์ ์ ์๋์ด ์๋ ๋ชจ๋ URL์ ๋ฐ๋ก ํ์ธํ ์ ์๋ค.
- API ๋ชฉ๋ก ๋ฟ๋ง ์๋๋ผ API์ ๋ช ์ธ ๋ฐ ์ค๋ช ๋ ๋ณผ ์ ์์ผ๋ฉฐ ๋ํ API๋ฅผ ์ง์ ํ ์คํธ ํด ๋ณผ ์๋ ์๋ค.
- RestController์์๋ง ํ์ธ ๊ฐ๋ฅํ๋ค.
SWAGGER ์ฌ์ฉ ์ด์
- ํ๋ก์ ํธ ๊ฐ๋ฐ ์ ์ผ๋ฐ์ ์ผ๋ก FrontEnd ๊ฐ๋ฐ์์ BackEnd ๊ฐ๋ฐ์๊ฐ ๋ถ๋ฆฌ๋๋ค.
- FrontEnd ๊ฐ๋ฐ์์ ๊ฒฝ์ฐ ํ๋ฉด๊ณผ ๋ก์ง์ ์ง์คํ๊ณ BackEnd ๊ฐ๋ฐ์๊ฐ ๋ง๋ ๋ฌธ์ API๋ฅผ ๋ณด๋ฉฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ํ๊ฒ ๋๋ค.
- ์ด๋ ๊ฐ๋ฐ ์ํฉ์ ๋ณํ์ ๋ฐ๋ฅธ API์ ์ถ๊ฐ ๋๋ ๋ณ๊ฒฝํ ๋๋ง๋ค ๋ฌธ์์ ์ ์ฉํ๋ ๋ถํธํจ์ด ๋ฐ์ํ๋ค.
- ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Swagger๋ฅผ ์ฌ์ฉํ๋ค.
- ํ ๋ง๋๋ก FrontEnd ๊ฐ๋ฐ์์ BackEnd ๊ฐ๋ฐ์์ ์์ฌ์ํต์ ๋๋๋ค.
- Swagger๋ฅผ ๋ณด๋ฉฐ ๋ณ๊ฒฝ๋ ๊ฒ์ ๋ณด๊ณ ๊ฑฐ๊ธฐ์ ๋ฐ๋ผ ๊ฐ๋ฐ์ ์งํํ๋ค.
SWAGGER ์ ์ฉ
pom.xml์ dependency ์ถ๊ฐ
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
-
Swagger3
- Swagger3 ๋ถํฐ๋ 1๊ฐ๋ก ๋ค ์ค์ ๋๋ค.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
application.properties
- SpringBoot 2.6์ด์ Swagger3.0 ์ ์ฉ์ ์๋ฌ ํด๊ฒฐ์ ์ํด ์ค์ ์ ํด์ผํ๋ค.
- SpringBoot 2.6.0 ๋ถํฐ ์์ฒญ ๊ฒฝ๋ก๋ฅผ ControllerHandler์ ๋งค์นญ์ํค๊ธฐ ์ํ ์ ๋ต์ ๊ธฐ๋ณธ ๊ฐ์ด ant_path_matcher ์ ๋ต์์ path_pattern_parser ์ ๋ต์ผ๋ก ๋ณ๊ฒฝ ๋์๋ค.
- ๊ทธ๋์ application.properties์์ spring.mvc.pathmatch.matching-strategy=ant_path_matcher๋ก default ๊ฐ์ ๋ณ๊ฒฝํ๋ค.
#Failed to start bean 'documentationPluginsBootstrapper'; error
spring.mvc.pathmatch.matching-strategy = ANT_PATH_MATCHER
SwaggerConfiguration
package com.ssafy.guestbook.config;
import static springfox.documentation.builders.PathSelectors.regex;
import java.util.HashSet;
import java.util.Set;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.*;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
// swagger ์ค์ ํ์ผ
public class SwaggerConfiguration {
// Swagger-UI 2.x ํ์ธ
// http://localhost:8080/{your-app-root}/swagger-ui.html
// Swagger-UI 3.x ํ์ธ
// http://localhost:8080/{your-app-root}/swagger-ui/index.html
private String version = "V1";
private String title = "SSAFY GuestBook API " + version;
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).consumes(getConsumeContentTypes()).produces(getProduceContentTypes())
.apiInfo(apiInfo()).groupName(version).select()
// RestApi๊ฐ ์ด๋์ ์๋์ง ์ค์
.apis(RequestHandlerSelectors.basePackage("com.ssafy.guestbook.controller"))
// ์์น ์ค์ ( Controller ์์น )
.paths(regex("/admin/.*")).build()
.useDefaultResponseMessages(false);
}
private Set<String> getConsumeContentTypes() {
Set<String> consumes = new HashSet<>();
consumes.add("application/json;charset=UTF-8");
// consumes.add("application/xml;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;
}
// ์ค์ง์ ์ผ๋ก swagger๋ฅผ ๊ด๋ฆฌํ๋ ์ญํ
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title(title)
.description("<h3>SSAFY API Reference for Developers</h3>Swagger๋ฅผ ์ด์ฉํ GuestBook API<br><img src=\"/img/ssafy_logo.png\" width=\"150\">")
.contact(new Contact("SSAFY", "https://edu.ssafy.com", "ssafy@ssafy.com"))
.license("SSAFY License")
.licenseUrl("https://www.ssafy.com/ksp/jsp/swp/etc/swpPrivacy.jsp")
.version("1.0").build();
}
}
Controller, DTO
- Annotation ์ค๋ช
- @ApiOperation
- ํด๋น Controller ์์ method์ ์ค๋ช ์ ์ถ๊ฐ
- values : ๊ฐ
- notes : ๋ด์ฉ
- @ApilmplicitParmam
- ํด๋น API Method ํธ์ถ์ ํ์ํ Parameter๋ค์ ์ค๋ช ์ ์ถ๊ฐ
- @ApiResponse
- ํด๋น method์ Response์ ๋ํ ์ค๋ช ์ ์์ฑ
- ๋ณต์๋ก ์์ฑํ๊ณ ์ถ์ผ๋ฉด @ApiResponses ์ฌ์ฉ, ๋ฐฐ์ด
- @Apiparam
- DTO Field ์ค๋ช
- @ApiModel
- Model(DTO)์ ๋ํ ์ค๋ช
- @ApiOperation
package com.ssafy.word.controller;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.ssafy.word.model.WordDto;
import com.ssafy.word.model.service.WordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
@RestController
@CrossOrigin("*")
@Api("์ด๋๋ฏผ ์ปจํธ๋กค๋ฌ V1")
public class WordCloudController {
private static final Logger logger = LoggerFactory.getLogger(WordCloudController.class);
@Autowired
private WordService wordService;
@ApiOperation(value = "๋ฆฌ์คํธ๋ชฉ๋ก", notes = "ํ์์ <big>์ ์ฒด ๋ชฉ๋ก</big>์ ๋ฆฌํด.")
@ApiResponses({
@ApiResponse(code=404, message="์ฃผ์ ์๋ฅ !!"),
@ApiResponse(code=500, message="์๋ฒ ์๋ฌ!!"),
@ApiResponse(code=200, message="ํ์ ๋ชฉ๋ก ์ ์ ์ฒ๋ฆฌ!!")
})
@GetMapping("/word")
public ResponseEntity<List<WordDto>> listWord() {
logger.debug("listWord - ํธ์ถ");
return new ResponseEntity<>(wordService.listWord(), HttpStatus.OK);
}
@ApiOperation(value = "๊ด์ฌ์ฌ๋ฑ๋ก", notes = "๊ด์ฌ์ฌ ์
๋ ฅ")
@PostMapping("/word")
public ResponseEntity<List<WordDto>> registWord(@RequestParam(value = "concerns[]") List<String> concerns) {
logger.debug("registWord - ํธ์ถ");
wordService.registWord(concerns);
return new ResponseEntity<>(wordService.listWord(), HttpStatus.OK);
}
@ApiOperation(value = "๊ด์ฌ์ฌ ์ ํ", notes = "๊ด์ฌ์ฌ์ ํ ํ ์ ์๋ฅผ ์ฌ๋ฆผ")
@PostMapping("/word/{word}")
public ResponseEntity<List<WordDto>> updateWordCount(@PathVariable("word") String word) {
logger.debug("updateWordCount - ํธ์ถ");
wordService.updateCount(word);
return new ResponseEntity<>(wordService.listWord(), HttpStatus.OK);
}
}
package com.ssafy.word.model;
import io.swagger.annotations.ApiModel;
@ApiModel(value = "WordDto (๊ด์ฌ์ฌ ์ ๋ณด)", description = "๊ด์ฌ์ฌ ๋จ์ด, ๋น์ค ์ ๋ณด๋ฅผ ๊ฐ์ง ํด๋์ค")
public class WordDto {
private String text;
private double weight;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
SWAGGER ์ถ๋ ฅ
- Swagger2
- Swagger3
-
method ์คํ ( GET )
- try it out -> execute
- ์์ฒญ๋ URL ๊ณผ ์๋ต๋ body๊ฐ ๋์จ๋ค.
- ์ด๊ฒ์ ๋ณด๊ณ ์ด ๋ฉ์๋ ์คํ์ ์ด๋ค ๊ฐ์ด ์ถ๋ ฅ๋๋์ง ์ ์ ์๋ค.
-
method ์คํ ( POST )
- try it out -> ๊ฐ ์ ๋ ฅ -> execute
- required๋ผ๊ณ ๋์ด์๋ ๋ถ๋ถ์ ๋ฐ๋์ ์ ๋ ฅํด์ผ ํ๋ ๊ฐ์ด๋ค.
- โJSโ ๊ฐ ๋ค์ด๊ฐ ๊ฒ์ ๋ณผ ์ ์๋ค.
- ์ ์์ ์ผ๋ก ์๋ํ๋ค๋ ์๋ฏธ
- ์ด๊ฒ์ ๋ณด๊ณ ์ด๋ค ๊ฐ์ ๋ฃ์ผ๋ฉด ์ด๋ป๊ฒ ์ถ๋ ฅ๋๊ณ ์ ์ฅ๋๋์ง ์ ์ ์๋ค.
-
model
- Dto์์ @ApiModel์์ ์ค์ ํด์ค ๊ฒ๊ณผ ๊ฐ์ด ํ์ธํ ์ ์๋ค.
- ์ด๊ฒ์ ํตํด Dto ๊ตฌ์กฐ๋ฅผ ํ์ ํ ์ ์๋ค.