- RESTful 웹 서비스를 설계, 구축, 문서화 및 사용할 수 있는 오픈 소스 소프트웨어 프레임워크를 의미합니다. - 이를 통해 API의 구조와 동작을 정의하기 위한 도구와 사양 세트를 제공하여 API를 개발하고 유지하는 것을 더욱 쉽게 만들어줍니다. API의 엔드포인트, 매개변수, 응답 등을 정의하고 문서화할 수 있습니다.
- 어노테이션을 기반으로 작동하고 사용이 되며 API의 구조와 동작에 대해 정의를 합니다. 또한 Swagger를 이용하기 위해서는 라이브러리인 SpringFox나 SpringDoc OpenAPI를 사용해야 합니다. - Rest API에서 설계 과정 중 'API 문서화 단계'에서 이를 사용합니다.
- 기존에 접속이 잘 되던 Swagger-ui를 Spring Boot 3.x 버전으로 마이그레이션 함에 따라서 경로를 찾지 못하는 오류(404)가 발생함을 확인하였습니다.
💡공식사이트에 접속
- 아래와 같이 springdoc-openapi v1.8.0 최신 버전의 경우 공식 사이트에서 아래와 같이 이야기하고 있습니다.
💡springdoc-openapi v1.8.0 is the latest Open Source release supporting Spring Boot 2.x and 1.x. An extended support for springdoc-openapi v1 project is now available for organizations that need support beyond 2023. For more details, feel free to reach out: sales@springdoc.org
- springdoc-openapi v1.8.0의 경우 Boot 2.x 및 1.x를 지원하는 최신 오픈 소스 릴리스입니다. 2023년 이후에도 지원이 필요한 조직을 위해 springdoc-openapi v1 프로젝트에 대한 확장 지원이 이제 제공됩니다. 자세한 내용은 sales@springdoc.org로 문의하세요.
https://springdoc.org/#getting-started
2. 해결 방법
💡해결 방법
- 아래와 같이 springdoc-openapi-starter-webmvc-ui 라이브러리를 사용하기를 권장하고 있습니다.
- Spring, Spring Boot 환경에서 적용이 되었던 Swagger 라이브러리의 종류들을 확인하고, 각각 적용이 가능한 버전들에 대해서 알아봅니다.
라이브러리
특징
Spring Boot 호환성
OpenAPI 버전
SpringFox Swagger2
- Spring 기반 애플리케이션용 - 자동 Swagger 2.0 문서 생성 - 최근 업데이트 중단
Spring Boot 2.x까지
Swagger2
SpringDoc OpenAPI UI
- Spring Boot 애플리케이션용 - 자동 OpenAPI 3 문서 생성 - 활발한 유지보수
Spring Boot 1.x, 2.x
Swagger3
SpringDoc OpenAPI Starter WebMVC UI
- Spring Boot 3.x용 - WebMVC 애플리케이션 지원 - Swagger UI 통합
Spring Boot 3.x
Swagger3
[ 더 알아보기] 💡 Swagger와 Swagger 라이브러리의 차이점은 무엇일까?
- Swagger: API 설계, 문서화, 테스트를 위한 오픈소스 프레임워크 및 사양을 의미합니다. - Swagger 라이브러리: Swagger 사양을 구현하고 애플리케이션에 통합하기 위한 도구를 의미합니다.
- 즉, Swagger는 개념과 사양을 정의하고, Swagger 라이브러리는 이를 실제로 구현하여 개발자가 쉽게 사용할 수 있게 해주는 도구입니다.
1. SpringFox Swagger2
💡SpringFox Swagger2
- Spring 기반 애플리케이션에서 Swagger 2.0 문서를 자동으로 생성하기 위한 라이브러리를 의미합니다. - SpringFox Swagger2는 최근 업데이트가 2020년 이후 중단되어 Spring Boot 3.x 버전과 호환성 문제가 있을 수 있습니다. 따라서 최신 프로젝트에서는 SpringDoc OpenAPI를 사용하는 것이 권장됩니다.
💡SpringDoc OpenAPI UI - Spring Boot 애플리케이션에서 OpenAPI 3 (이전의 Swagger) 문서를 자동으로 생성하기 위한 라이브러리입니다. - 해당 라이브러리의 경우 Spring Boot 2.x 및 1.x에 대해서 지원하는 라이브러리입니다. 즉, 최신 Spring Boot 3.x 버전을 사용하는 경우 지원을 하지 않습니다.
💡 SpringDoc OpenAPI Starter WebMVC UI - Spring Boot 3.x 버전과 호환되는 OpenAPI (Swagger) 문서 생성 라이브러리입니다. - Spring Boot 3.x에서 WebMVC 애플리케이션을 위한 OpenAPI 3 (Swagger) 지원을 제공합니다. - 자동으로 API 문서를 생성하고 Swagger UI를 통해 시각화합니다.
- Swagger로 구성하는 API 문서의 제목, 버전, 설명에 대해서 설정합니다. - 즉, "데모 프로젝트 API Document"라는 제목, "v0.0.1" 버전, 그리고 "데모 프로젝트의 API 명세서입니다."라는 설명을 가진 API 문서를 생성합니다.
package com.adjh.springboot3tierform.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Swagger springdoc-ui 구성 파일
*/
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI openAPI() {
Info info = new Info()
.title("데모 프로젝트 API Document")
.version("v0.0.1")
.description("데모 프로젝트의 API 명세서입니다.").
return new OpenAPI()
.components(new Components())
.info(info);
}
}
4. application.properties 파일 지정
💡application.properties 파일 지정
- SpringDoc OpenAPI Starter WebMVC UI를 구성하기 위한 환경 파일입니다.
- REST API Controller 내에서 사용할 수 있는 Swagger Annotation을 적용하여 구성하였습니다.
💡예시에서 사용한 Swagger 주요 어노테이션입니다.
Annotation
설명
@Tag
API 그룹을 정의합니다. 여기서는 "Code-Controller"라는 이름으로 코드 관리 API 엔드포인트를 그룹화했습니다.
@Operation
API 작업의 설명을 제공합니다. 각 메서드에 대한 요약과 상세 설명을 포함합니다.
@ApiResponse
API 응답에 대한 정보를 제공합니다. 응답 코드, 설명, 응답 내용 등을 정의합니다.
@Parameter
API 메서드의 파라미터에 대한 설명을 추가합니다.
@Schema
API 요청 또는 응답의 스키마를 정의합니다. 주로 DTO 클래스에 사용됩니다.
@Hidden
특정 API 엔드포인트를 Swagger 문서에서 숨깁니다.
@SecurityScheme
API 보안 스키마를 정의합니다. 주로 인증 방식(예: JWT)을 설정할 때 사용됩니다.
@SecurityRequirement
특정 API 작업에 보안 요구사항을 적용합니다. @SecurityScheme와 함께 사용하여 인증이 필요한 엔드포인트를 지정합니다.
💡구성 예시 설명
1. @Tag
- 해당 어노테이션을 통해서 Controller에 대한 설명을 작성하였습니다. - name 속성을 통해 Controller 이름을 정의하였고, description 속성을 통해 Controller의 상세 설명을 정의하였습니다.
2. @Operation
- 해당 어노테이션을 통해서 메서드에 대한 설명을 작성하였습니다. - summary 속성을 통해 메서드의 요약 정보를 정의하였고 , description 속성을 통해 메서드의 상세 설명을 정의하였습니다.
3. @ApiResponse
- 해당 어노테이션을 통해 메서드에 대한 API 응답 값을 정의하였습니다. - responseCode 속성을 통해 정상 통신을 하였을 때 받는 응답 코드 값을 정의하고 , description 속성을 통해 통신 응답에 대한 설명을 정의하고, content 속성을 통해 통신 성공 시 정상적 통신일 때 받을 값을 정의하였습니다.
4. @RequestParam @Parameter
- @Parameter 어노테이션을 통해, @RequestParam으로 전달받은 값에 대해서 API 통신에서 파라미터로 전달받을 값에 대해 정의하였습니다. - description 속성을 통해서 파라미터에 대한 상세 설명을 정의하였습니다.
5. @RequestBody @Schema
- @Schema 어노테이션을 통해, @RequestBody으로 전달받을 객체 값에 대해 API 통신에서 받을 값에 대해 정의하였습니다. - implementation 속성을 통해, 전달받을 객체에 대한 상세 정의를 하였습니다.
6. @Hidden
- @Hidden 어노테이션을 통해 Swagger API 정의 내용에서 제외를 하도록 정의하였습니다.
[ Spring Security를 사용하는 경우 ] 7. @SecurityScheme
- 해당 어노테이션을 통해서 Controller를 이용할 때 JWT를 통해서 bearer 인증 방식으로 접근이 가능함을 정의하였습니다. - name 속성을 통해 인증 방식을 정의하고, type 속성을 통해 통신 타입을 지정하였습니다. - scheme 속성을 통해 인증 방식을 정의하고 , bearerFormat 속성을 통해 어떤 인증 방식을 이용할지 정의하였습니다.
8. @SecurityRequirement
- 해당 어노테이션을 통해 메서드에서 인증이 필요함을 정의하였습니다. - 해당 인증으로는 name을 bearerAuth로 지정하여 @SecurityScheme 정의한 name과 매핑이 되도록 구성하였습니다.
package com.adjh.springboot3tierform.controller;
import com.adjh.springboot3tierform.model.dto.CodeDto;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.tags.Tag;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* 코드를 관리하는 Controller
*
* @author : jonghoon
* @fileName : TestController
* @since : 9/7/24
*/
@Tag(name = "Code-Controller", description = "Code 관리 API 엔드포인트")
//@SecurityScheme(name = "bearerAuth", type = SecuritySchemeType.HTTP, scheme = "bearer", bearerFormat = "JWT")
@RestController
public class CodeController {
/**
* 코드 단건 조회
*
* @param codeSq
* @param codeNm
* @return
*/
@GetMapping("/code")
@Operation(summary = "코드 단건 조회", description = "코드 단건을 조회합니다.")
// @SecurityRequirement(name = "bearerAuth")
@ApiResponse(responseCode = "200", description = "성공",
content = @Content(schema = @Schema(implementation = CodeDto.class))
)
public ResponseEntity<List<CodeDto>> selectCode(
@RequestParam @Parameter(description = "코드 시퀀스") int codeSq,
@RequestParam @Parameter(description = "코드 이름") String codeNm
) {
List<CodeDto> temp = new ArrayList<>();
return new ResponseEntity<>(temp, HttpStatus.OK);
}
/**
* 코드 리스트 조회
*
* @param codeDto
* @return
*/
@PostMapping("/code")
@Operation(summary = "코드 리스트 조회", description = "코드 리스트를 조회합니다.")
// @SecurityRequirement(name = "bearerAuth")
@ApiResponse(
responseCode = "200",
description = "성공",
content = @Content(schema = @Schema(implementation = CodeDto.class))
)
public ResponseEntity<CodeDto> selectCodeList(
@RequestBody @Schema(implementation = CodeDto.class) CodeDto codeDto
) {
CodeDto result = CodeDto.builder().build();
return new ResponseEntity<>(result, HttpStatus.OK);
}
/**
* 코드 전체 수정
*
* @param codeDto
* @return
*/
@PutMapping("/code")
@Operation(summary = "코드 전체 수정", description = "코드를 전체 수정 합니다.")
// @SecurityRequirement(name = "bearerAuth")
@ApiResponse(responseCode = "200", description = "성공")
public ResponseEntity<Integer> updateCode(
@RequestBody @Schema(implementation = CodeDto.class) CodeDto codeDto
) {
int result = 0;
return new ResponseEntity<>(result, HttpStatus.OK);
}
/**
* 코드 일부 수정
*
* @param codeDto
* @return
*/
@PatchMapping("/code")
@Operation(summary = "코드 일부 수정", description = "코드를 일부 수정 합니다.")
// @SecurityRequirement(name = "bearerAuth")
@ApiResponse(responseCode = "200", description = "성공")
public ResponseEntity<Integer> patchCode(
@RequestBody @Schema(implementation = CodeDto.class) CodeDto codeDto
) {
int result = 0;
return new ResponseEntity<>(result, HttpStatus.OK);
}
/**
* 코드 삭제
*
* @param codeDto
* @return
*/
@DeleteMapping("/code")
@Operation(summary = "코드 삭제", description = "코드를 삭제합니다.")
// @SecurityRequirement(name = "bearerAuth")
@ApiResponse(responseCode = "200", description = "성공")
public ResponseEntity<Integer> deleteCode(
@RequestBody @Schema(implementation = CodeDto.class) CodeDto codeDto
) {
int result = 0;
return new ResponseEntity<>(result, HttpStatus.OK);
}
/**
* 사용자 코드 조회 (API Document) 제외
*
* @param codeDto
* @return
*/
@PostMapping("/codes")
@Hidden
@Operation(summary = "사용자 코드 조회", description = "사용자 코드를 조회합니다.")
// @SecurityRequirement(name = "bearerAuth")
@ApiResponse(responseCode = "200", description = "성공")
public ResponseEntity<Integer> selectUserCode(
@RequestBody @Schema(implementation = CodeDto.class) CodeDto codeDto
) {
int result = 0;
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
6. Dto 구성 예시
💡Dto 구성 예시
- REST API에서 데이터를 주고받을 수 있는 DTO에 대해 Swagger Annotation을 적용하여 구성하였습니다.
Annotation
설명
@Schema
API 요청 또는 응답의 스키마를 정의합니다. 주로 DTO 클래스에 사용됩니다.
💡구성 예시 설명
1. @Schema
- 해당 어노테이션을 통해서 DTO 전체에 대한 설명과 각각 컬럼이 되는 멤버변수에 대해서 설명을 정의하였습니다. - description 속성을 이용하여서 각각 전체에 대한 설명과 멤버 변수에 대해서 설명을 지정하였습니다.