1부에 이어서 2부 Swagger에 대해서 Demo을 제작해보고 장단점을 비교해보겠습니다.

모든 소스는 Github에서 확인 가능합니다.

 

※ 개발환경 :  JDK 11, STS 4.8.1,  Spring Boot 2.6.3, Maven 4

 

1. Spring Swagger Demo 프로젝트 생성

프로젝트, 패키지 명은 적절하게 선택하여 Spring Stater Project 생성합니다.

의존성 설정은 아래의 pom.xml 파일을 참고해주세요.

 

2. Spring Swagger  Demo 프로젝트 구조

아래 이미지의 구조를 가지게 됩니다.

3. pom.xml 파일 설정

원래는 springfox-boot-starter 3.0.0 버전을 의존성 설정해야 하지만

Spring Boot 2.6.x 버전에서 컴파일 에러는 아니지만 런타임 에러가 발생했고

찾아보니 2.6.x 버전에서 매우 많은 버그가 있다고 알려져 있어 거의 유사한 사용 방식을 가진

spring-docs-openapi 1.6.4로 의존성으로 마이그레이션을 했습니다.

실제로도 springfox-boot-starter → spring-docs-openapi 의존성을 옮겼을 때

Swagger 설정 빼고는 변경한 내용이 없었습니다.

<?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.6.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>SpringbootSwagger2Demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringbootSwagger2Demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</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>
		<!--  <dependency>
        	<groupId>io.springfox</groupId>
        	<artifactId>springfox-boot-starter</artifactId>
        	<version>3.0.0</version>
        	</dependency>-->
        	<dependency>
    			<groupId>org.springdoc</groupId>
    			<artifactId>springdoc-openapi-ui</artifactId>
    			<version>1.6.4</version>
			</dependency> 
         
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

 

4. Configuration - OpenApiConfig

OpenAPI 설정에 대한 커스텀 설정을 합니다.

다양한 설정이 가능하나 여기서는 문서가 생성될 때 도메인에 대해 설정합니다.

src/main/java > com.example.demo.config 패키지를 생성하고 OpenApiConfig 클래스를 작성합니다.

package com.example.demo.config;

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

//import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
//import io.swagger.v3.oas.models.info.Info;
//import io.swagger.v3.oas.models.info.License;
//import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;

@Configuration
public class OpenApiConfig {
	
	@Bean
	public OpenAPI customOpenAPI() {
		return new OpenAPI().addServersItem(new Server().url("http://localhost:8080"));
				//.components(new Components().addSecuritySchemes("basicScheme",
						//new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("basic")))
						//.info(new Info().title("Spring API").version("V1")
						//.license(new License().name("Apache 2.0").url("http://springdoc.org")));
	}
}

 

5. Entitiy - User 

src/main/java > com.example.demo.entity 패키지를 생성하고 User 클래스를 작성합니다.

DemoController에서 응답 객체로 HashMap을 사용하는데 현재로서는 HashMap이 포함하는 변수들에 대한
상세 설명을 직접 작성할 수 있는 방법을 찾을 수 없었습니다.

따라서 User 클래스를 작성하고 문서 상 설명을 보여줄 수 있게 작성합니다.

package com.example.demo.entity;

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "유저")
public class User{
	
	@Schema(description = "유저 고유번호")
	private String id;
	
	@Schema(description = "유저 명")
	private String name;
	
	@Schema(description = "유저 이메일")
	private String email;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}
}

 

 

6. DemoController 

src/main/java > com.example.demo.controller 패키지를 생성하고 DemoController 클래스를 작성합니다.

package com.example.demo.controller;

import java.util.HashMap;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.entity.User;

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.responses.ApiResponses;

@RestController
public class DemoController {

	   @Operation(summary = "getUserInfoById")
	   @ApiResponses(value = {
			   @ApiResponse(responseCode ="200", description="유저 정보 조회 성공", 
					   content = { @Content(mediaType = "application/json", schema = @Schema(implementation = User.class)) }),
	   })
	   @GetMapping("/demo/user/{id}")
	   public ResponseEntity<?> getUserInfoById(@Parameter(description="유저 고유번호") @PathVariable("id") String id){

		   HashMap<String, String> map = new HashMap<>();
		   map.put("id", id);
		   map.put("name", "jack");
		   map.put("email", "jack@abc.com");

	       return new ResponseEntity<>(map, HttpStatus.OK);
	   }

	   @Operation(summary = "registUserInfo")
	   @ApiResponses(value = {
			   @ApiResponse(responseCode ="200", description="유저 등록 성공", 
					   content = { @Content(mediaType = "application/json", schema = @Schema(implementation = User.class)) }),
	   })
	   @PostMapping("/demo/user")
	   public ResponseEntity<?> registUserInfo(@Parameter(schema = @Schema(implementation = User.class)) @RequestParam HashMap<String, String> map) {
		   return new ResponseEntity<>(map, HttpStatus.OK);
	   }
}

 

  • @Opertation
    HTTP 메서드에 대해 설명.
  • @ApiResponse
    응답에 대해 나타냅니다.

  • @Content
    특정 미디어 유형에 대한 스키마 및 예시를 제공.

  • @Scheme
    입력 및 출력 데이터의 정의.

  • @Parameter
    단일 매개변수에 대해 나타냅니다.

어노테이션에 대한 상세 설명과 예시는 아래를 링크를 참고해주세요.

Swagger 2.x wiki
Migrating from SpringFox

 

 

7. 테스트

Spring Boot Dashboard에서 Start 합니다.

정상적으로 Start 됐으면 브라우저에서 http://localhost:8080/swagger-ui.html 주소로 접근하여

아래와 같이 API 문서를 확인 가능합니다.

실제 서비스에서는 해당 경로에 대한 접근 보안 설정은 필수로 추가해야 합니다.

그렇지 않을 경우 누구나 접근 가능한 경로가 됩니다.

정의된 API를 클릭하여 어노테이션 코드로 작성한 설명들과 예시를 볼 수 있으며,

POST MAN 같은 API 툴처럼 값을 입력하여 API 테스트도 가능합니다.

 

8. 마치며

Spring Rest Docs와 Swagger Demo를 작성해봤습니다.

Swagger Demo를 작성하면서 느낀 장단점은 아래와 같습니다.

  • 장점
    테스트 코드의 작성 필요가 없다.
    API 테스트가 가능.

  • 단점
    테스트 코드를 통해 작성된 문서가 아니기 때문에 신뢰도가 상대적으로 떨어진다.
    프로덕션 코드에 작성 코드가 포함된다.
    Map의 경우 직접적으로 설명을 작성하기가 어렵다.
    Swagger 어노테이션으로 대부분 작성하므로 학습 필요.

Spring Rest Docs와 Swagger은 장단점은 극명한 것 같습니다.

하지만 어떤 것이 좋을지는 환경과 비즈니스 요구사항에 맞춰서 대응해야 한다고 봅니다.

 

  • 레거시 프로젝트가 있고, 많은 양의 API가 있는데 빠른 시간 내에 API 문서가 필요하다면?
    Spring Rest Docs를 적용하게 되면 전부 테스트 코드를 작성해줘야 하고
    따라서 시간이 매우 소모되는 작업이므로 적합하지 않을 수 있습니다.
    물론 둘 다 모르는 것에 학습은 해야 되지만 개인적으로는 Swagger가 훨씬 작성하기는 쉬워 보였습니다.

  • 처음 시작하는 프로젝트이고 신뢰성 있는 API 문서가 필요하다면?
    Spring Rest Docs를 적용하게 되면 전부 테스트 코드 작성해야 하지만 
    검증된 신뢰성 있는 API 문서를 얻을 수 있습니다.

  이외에도 프로덕션 코드에 코드 추가 여부나 작업자의 학습도, 역량에 따라서 달라질 수 있다고 봅니다.

  하지만 장기적으로 봤을 때는 Spring Rest Docs를 적용하는 게 TDD 개발을 하는 곳이라면 낫지 않을까 싶습니다.

 

※ 참고

https://springdoc.org/#migrating-from-springfox

https://github.com/springdoc/springdoc-openapi/issues/89

https://springdoc.org/#demos

https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations

https://github.com/swagger-api/swagger-core/issues/3080

https://www.baeldung.com/spring-rest-openapi-documentation

https://github.com/springfox/springfox-demos

http://springfox.github.io/springfox/docs/current/#springfox-spring-mvc-and-spring-boot

'Spring Boot Framework' 카테고리의 다른 글

Spring RestTemplate (1)  (0) 2022.05.23
Spring Boot Interceptor  (0) 2022.02.18
Spring Rest Docs 와 Swagger (1)  (0) 2022.02.11
Spring Boot Oauth 2.0 과 JWT Demo (5)  (0) 2022.02.08
Spring Boot Oauth 2.0 과 JWT Demo (4)  (0) 2022.02.06

+ Recent posts