Swagger 란 ? (이론 + Spring boot 적용)

mallin·2022년 1월 15일
2
post-thumbnail
post-custom-banner

swagger 관련 모든 소스 코드는 🌟링크🌟 에서 확인주세요.

원래 우리 회사에서는 API 를 각자 포스트맨으로 관리하거나, 개발한 사람한테 물어물어 보는 형식으로 개발을 했다.

일단
① 포스트맨은 API 가 수정되거나 만들어질 때마다 추가적으로 리소스가 너무 많이 들고 관리가 힘들며,
② 개발한 사람한테 물어물어 보는 형식은 기억이 완전하지 않고 리소스가 너무 많이 들었다. 심지어 그 사람이 퇴사한 경우에는 .. 🥵
그래서 새롭게 진행하는 프로젝트에서는 API 관리 툴인 Swagger 을 적용하기로 했다.

Swagger 란 ?

REST API 스펙 명세 및 관리가 목적으로 하는 Open Api Specification(OAS)를 위한 프레임워크

공식 홈페이지

간단하게 서버에 있는 REST API 들을 HTML 화면으로 노출해주는 툴이라고 생각하면 된다.
하드코딩으로 API 들을 명시해주는게 아니고 서버에 있는 API 들을 바로 노출해주기 때문에 동기화에 대한 걱정을 하지 않아도 되고 API 가 추가되더라도 문서를 수정하지 않아도 된다.


swagger 가 적용된 샘플 화면은 위와 같다. 서버에 있는 API 가 노출되고, request 를 담아서 보내면 response 를 보여준다. (샘플 URL)

springfox-swagger vs springdoc-openapi

springfox-swaggerspringdoc
http://springfox.github.io/springfox/docs/current/https://springdoc.org/

두개 모두 Swagger 를 사용하되 API 문서를 쉽게 쓸 수 있도록 해주는 라이브러리다.
Springfox-Swagger 가 선발 주자, Springdoc 가 후발주자 이다.

Springfox-Swagger 는 2018년 까지 많은 사람들이 사용하다가 2018년 6월을 마지막으로 업데이트가 중지되었고 그 사이인 2019년 7월에 springdoc 가 나와서 많은 사람들이 springdoc 를 많이 사용하기 시작한다. 그러던 중 2020년 6월에 Springfox 가 신 버전을 내놓아서 현재는 두개를 혼용해서 사용하고 있다.

해당 실습에선 springdoc 를 사용한다. 가장 큰 이유는 그룹핑 때문인데 내가 사용할 때에는 API 별로 그룹핑이 필요했는데 springfox 에서는 지원을 하지 않는건지 사용방법을 찾기 못했다.
springdoc-openapi 의 오버뷰는 다음과 같다.

출처 : https://springdoc.org/

여기서 내가 제일 헷갈렸던 건 실제 사용할 때 springdoc 만 import 하는데 swagger 는 .. 뭐지 .. ? 두개가 다른 건가 라는 의문이 들었다.

springdoc 를 추가하고 dependencies 를 보면 swagger-ui 가 함께 의존성 추가되는 걸 볼 수 있다 !

요약하자면,

핵심 로직 = swagger-ui
Spring 에서 핵심 로직을 활용해서 더 잘 쓸 수 있게 해주는 라이브러리 = springdoc-openapi

라고 생각하면 될 것 같다.

Spring Boot 에 Swagger 적용하기

해당 적용 방식은 Gradle 을 기반으로 하고 있다.

① build.gradle 에 springdoc 의존성 추가

implementation "org.springdoc:springdoc-openapi-ui:1.5.9"

② SwaggerConfig 로 swagger configuration 관리

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@OpenAPIDefinition(
        info = @Info(title = "API 명세서",
                description = "API 명세서 테스트 입니다.",
                version = "v1"))
@Configuration
@RequiredArgsConstructor
public class SwaggerConfig {

    @Bean
    public GroupedOpenApi customTestOpenAPi() {
    	// /test 로 시작하는 API 들을 테스트 관련 API 로 그룹핑 
    	// member 로 시작하는 API 를 그룹핑 하고 싶다 라고 하면 메소드 이름을 변경하고 하나 더 만들어서 설정하면 됨 
   		
        String[] paths = {"/test/**"};
        
        return GroupedOpenApi
                .builder()
                .group("테스트 관련 API")
                .pathsToMatch(paths)
                .addOpenApiCustomiser(buildSecurityOpenApi()).build();
    }

    public OpenApiCustomiser buildSecurityOpenApi() {
    	// jwt token 을 한번 설정하면 header 에 값을 넣어주는 코드, 자세한건 아래에 추가적으로 설명할 예정 
        return OpenApi -> OpenApi.addSecurityItem(new SecurityRequirement().addList("jwt token"))
                .getComponents().addSecuritySchemes("jwt token", new SecurityScheme()
                        .name("Authorization")
                        .type(SecurityScheme.Type.HTTP)
                        .in(SecurityScheme.In.HEADER)
                        .bearerFormat("JWT")
                        .scheme("bearer"));
    }

}

SwaggerConfig.java 파일을 생성해주고 위 코드를 넣어준다.
1. /test 로 시작하는 API들을 테스트 관련 API 라는 그룹으로 그룹핑하고
2. header 에 jwt token 을 넣어주는 코드다.

3. 테스트 API 추가

package velog.soyoen.swagger.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/test")
@RestController
public class TestController {

    @Operation(summary = "test hello", description = "hello 샘플 예제입니다. ")
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "OK !!"),
            @ApiResponse(responseCode = "400", description = "BAD REQUEST !!"),
            @ApiResponse(responseCode = "404", description = "NOT FOUND !!"),
            @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR !!")
    })
    @GetMapping("/hello")
    public ResponseEntity<String> hello(
            @Parameter(description = "이름", required = true, example = "박소연") @RequestParam String name,
            @Parameter(description = "나이", example = "21") @RequestParam int age
    ) {
        return ResponseEntity.ok("안녕하세요. "+age+"살 "+name+"님");
    }

    @GetMapping("/ok")
    public ResponseEntity<String> okTest() {
        return ResponseEntity.ok("okTest");
    }
}

TestController 를 만들고
/test/hello, /test/ok API 2개를 만들어준 다음 swagger-ui 에서 보이기 위해서 어노테이션을 추가해준다. swagger 관련 추가된 어노테이션은 다음과 같다. ⬇️

이름설명
@Operation해당 API 에 대한 정보
@ApiReposnsesresponse 에 대한 정보로 여러개의 @ApiResponse 를 가지고 있다
@ApiResponseresponse 코드와 설명을 설정
@Parameter파라미터에 대한 정보

화면 적용 예시

위의 코드가 모두 적용되었을 때 전체 화면이다
1, 2, 3, 4 로 부분별로 나눠서 코드와 함께 살펴보자 !

1 ➡️ swagger 문서에 대한 간략한 설명

@OpenAPIDefinition(
        info = @Info(title = "API 명세서",
                description = "API 명세서 테스트 입니다.",
                version = "v1"))

SwaggerConfig 에 @OpenAPIDefinition 어노테이션을 사용해서 swagger 문서에 대한 설명을 추가할 수 있다.
title 은 제목, description 은 설명, version 은 swagger 문서에 대한 버전

2 ➡️ 실제 API 호출

@Operation(summary = "test hello", description = "hello 샘플 예제입니다. ")
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "OK !!"),
            @ApiResponse(responseCode = "400", description = "BAD REQUEST !!"),
            @ApiResponse(responseCode = "404", description = "NOT FOUND !!"),
            @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR !!")
    })
    @GetMapping("/hello")
    public ResponseEntity<String> hello(
            @Parameter(description = "이름", required = true, example = "박소연 ") @RequestParam String name,
            @Parameter(description = "나이", example = "21") @RequestParam int age
    ) {
        return ResponseEntity.ok("안녕하세요. "+age+"살 "+name+"님");
    }

캡처에 나와있듯,
@Operation summary 는 API 에 대한 간단 설명
@Operation description 은 API 에 대한 설명
이고,

@Parameter 어노테이션을 사용해서 파라미터 별 설명을 넣어줄 수 있고,
@ApiRespnose 어노테이션을 통해 response 에 대해 지정해줄 수 있다.

API 를 실제로 호출하고 싶다면 Parameters 에 원하는 값을 넣어주고 Try it out 을 실행 해주면 된다.

3 ➡️ API 별 그룹핑

@Bean
public GroupedOpenApi customTestOpenAPi() {
	String[] paths = {"/test/**"};

	return GroupedOpenApi
		.builder()
		.group("테스트 관련 API")
		.pathsToMatch(paths)
		.addOpenApiCustomiser(buildSecurityOpenApi()).build();
}

GroupedOpenApi 를 통해 API 별로 그룹핑 해줄 수 있고,
select 박스를 통해 다른 그룹으로 변경해서 볼 수 있다.

4 ➡️ JWT token 설정


@Bean
public GroupedOpenApi customTestOpenAPi() {
	String[] paths = {"/test/**"};

	return GroupedOpenApi
		.builder()
		.group("테스트 관련 API")
		.pathsToMatch(paths)
		.addOpenApiCustomiser(buildSecurityOpenApi())
        .build();
}


public OpenApiCustomiser buildSecurityOpenApi() {
        return OpenApi -> OpenApi.addSecurityItem(new SecurityRequirement().addList("jwt token"))
                		.getComponents()
                		.addSecuritySchemes("jwt token", new SecurityScheme()
                        	.name("Authorization")
                        	.type(SecurityScheme.Type.HTTP)
                        	.in(SecurityScheme.In.HEADER)
                        	.bearerFormat("JWT")
                        	.scheme("bearer"));
    }

JWT token 설정이 필요한 경우 API 마다 Authorization 으로 넘겨줘야 하기 때문에 여간 귀찮은게 아닌데, 한 번 설정하면 계속 사용할 수 있도록 설정해줄 수 있다 !!

addOpenApiCustomiser 라는 설정을 통해서 커스텀을 해줄 수 있는데,
jwt token 으로 받은 토큰을 Header 에 Authorization 이라는 이름으로 prefix 로 bearer 을 붙여서 사용할 수 있도록 한다.

레퍼런스

Swagger. Springfox-Swagger 그리고 Springdoc
Swagger로 API 문서 자동화하기

post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 7월 12일

의존성 다른거 추가없이 implementation "org.springdoc:springdoc-openapi-ui:1.5.9" 만 추가하셨나요 ?

답글 달기