구현하기

구현 순서는 아래와 같다.

  1. restdocs-api-spec을 이용한 OAS 파일을 생성하는 빌드 환경 구축
  2. Swagger-UI standalone, Static Routing 세팅
  3. 생성된 OAS 파일을 Swagger 디렉터리로 복사(copyOasToSwagger)
  4. SampleController 및 MockMvc REST Docs Test 코드 작성
  5. 결과 확인

사용 기술

  • Spring Boot 3.3.0 / gradle-kotlin
  • Java 17
  • restdocs-api-spec

1. restdocs-api-spec을 이용한 OAS 파일을 생성하는 빌드 환경 구축

1-1. 의존성 추가

아래처럼 의존성을 추가해준다.

// build.gradle.kts
plugins {
    ... 생략
    id("com.epages.restdocs-api-spec") version "0.15.3"
}

repositories {
    mavenCentral()
}

dependencies {
    ... 생략
    // Rest Docs
    testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc")
    testImplementation("org.springframework.restdocs:spring-restdocs-asciidoctor")
    testImplementation("com.epages:restdocs-api-spec-mockmvc:0.17.1")
    ... 생략
}

openapi3 {
    this.setServer("https://localhost:8080") // list로 넣을 수 있어 각종 환경의 URL을 넣을 수 있음!
    title = "My API"
    description = "My API description"
    version = "0.1.0"
    format = "yaml" // or json
}

1-2. task 확인

의존성을 추가하고 gradle의 tasks를 보면 아래 사진처럼 openapi3 과 같은 task가 추가돼있다.

 

여기서 openapi3 task를 실행해보면 아래 사진처럼 build/api-spec 경로에 openapi3.yaml 파일이 생성된다.

 

openapi3.yaml 파일을 보면 아래와 같다.


2. Swagger-UI standalone, 리소스 핸들러 설정

2-1. Swagger-ui 정적 파일 설치

위 사진은 Swagger-ui 정적 파일 설치 사이트 하단의 설명이다.

위 페이지에서 latest release 를 다운 받는 링크로 이동하여 압축 파일을 받아준다.

2-2. 스프링부트의 정적 파일 경로에 추가

압축을 해제하면 아래 사진처럼 나오는데, 여기서 /dist 의 내용물만 필요하다.

 

/dist 내용물을 아래 사진처럼 스프링부트의 /resources/static/swagger-ui 로 옮겨준다.

2-3. 파일명 변경 및 불필요한 파일 제거

index.html 은 swagger-ui.html 로 파일명을 변경하였다.

또한 불필요한 파일들은 삭제해주었다.

  • oauth2-redirect.html
  • swagger-ui.js
  • swagger-ui-es-bundle-core.js
  • swagger-ui-es-bundle.js

2-4. swagger-initializer.js 파일의 url 수정

swagger-initializer.js의 코드를 보면 아래 사진과 같다.

1-2 에서는 openapi3 task 를 돌리면 OAS 파일이 생성됐지만, 최종적으로는

Spring Rest Docs와 연동하여 빌드 시 테스트 코드를 돌릴 때 OAS 파일을 만들게 될것이다.

swagger-initializer.js에서 OAS 파일을 읽어 API 시각화를 하기 때문에 이를 우리가 만들 OAS 파일을 참조하도록 수정한다.

여기서 url 부분을 수정하여 우리가 만들 OAS 파일을 참조하도록 한다.

Swagger-initializer.js

2-5. 리소스 핸들러 설정

브라우저에서 스프링부트 애플리케이션 서버의 /static/swagger-ui 경로에 있는 정적 파일에 접근하기 위해서는

리소스 핸들러 설정을 바꿔줘야 한다.

(사실 이 부분은 2-4 에서 url 을 /static/swagger-ui/openapi3.yaml 으로 했을때는 필요했는데

본인은 url을 /swagger-ui/openapi3.yaml로 설정해서 굳이 필요 없을것 같기는 하다.)

@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }
}

3. 생성된 OAS 파일을 Swagger 디렉터리로 복사(copyOasToSwagger)

1-2에서 openapi3 task를 실행하면 build/api-spec 경로에 openapi3.yaml 파일이 생성되는걸 확인했다.

이걸 2에서 생성한 /static/swagger-ui 경로에 넣고 swagger-ui.html로 읽을 것이다.

build.gradle.kts에 아래 코드를 추가하여 이를 위한 task를 추가한다.

// build.gradle.kts

tasks.withType<Test> {
    useJUnitPlatform()
    // 기존에 있는 test 쪽에 이 코드를 추가
    finalizedBy("copyOasToSwagger")  // test 후 copyOasToSwagger task 진행
}

val buildDir = layout.buildDirectory.get().asFile
tasks.register<Copy>("copyOasToSwagger") {

    dependsOn("openapi3") // openapi3 Task가 먼저 실행되도록 설정
    delete("src/main/resources/static/swagger-ui/openapi3.yaml") // 기존 OAS 파일 삭제
    from("$buildDir/api-spec/openapi3.yaml") // 복제할 OAS 파일 지정
    into("src/main/resources/static/swagger-ui/") // 타겟 디렉터리로 파일 복제
}

tasks {
    bootJar {
        // openapi3.yaml 파일을 /static/swagger-ui 경로로 복사에 성공한 후 bootJar 진행
        dependsOn("copyOasToSwagger")
        from("build/api-spec") {
            into ("BOOT-INF/classes/static/swagger-ui")
        }
    }
}

 

위 코드를 추가하고 빌드나 테스트를 돌려서 로그를 살펴보면 아래처럼 task 를 진행함을 확인할 수 있다.

test -> openapi3 -> copyOasToSwagger -> bootJar -> build 의 순서로 task 를 진행한다.


4. Sample Controller 코드 및 Rest Docs 테스트 작성

간단하게 테스트용으로 컨트롤러를 작성하였다.

TestController

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

    @GetMapping("/hi1")
    public String test() {
        return "hi1";
    }

    @GetMapping("/hi2")
    public String test2() {
        return "hi2";
    }
}

TestControllerTest

Spring Rest Docs는 MockMvc 와 결합해서 동작한다.

컨트롤러 테스트 코드를 작성할 때 아래 두 가지를 목적에 맞게 사용해야 한다.

  • Rest Docs 사용- MockMvcRestDocumentation.document 
  •   Swagger 사용 - MockMvcRestDocumentationWrapper.document

본인은 Rest Docs는 사용하지 않고 Swagger 용도로만 사용하였다.

어떤 메서드를 사용하는지 참고하기 쉽도록 패키지까지 올려두었다.

package com.example.dream;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.test.web.servlet.MockMvc;

import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@ExtendWith(RestDocumentationExtension.class)
class TestControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @DisplayName("")
    @Test
    void test() throws Exception {
        mockMvc.perform(get("/hi/hi1")
                .accept(MediaType.APPLICATION_JSON)
            )
            .andDo(print())
            .andExpect(status().isOk())
            .andDo(document("나는짱1"));
    }

    @DisplayName("")
    @Test
    void test2() throws Exception {
        mockMvc.perform(get("/hi/hi2")
                .accept(MediaType.APPLICATION_JSON)
            )
            .andDo(print())
            .andExpect(status().isOk())
            .andDo(document("나는짱2"));
    }
}

 


5. 결과 확인

아래 사진처럼 Production 코드를 침투하지 않고도 Swagger 의 API Test가 가능하다.

또한 Spring Rest Docs 의 테스트 코드를 통한 유지 보수성 또한 확보 되었다.

 

위 결과는 빌드하고 java -jar 명령어로 실행해본 결과이다.

배포 환경에서는 서버 URL 설정을 변경해줘야할 수 있다.

그때는 build.gradle.kts 의 openapi3 쪽의 서버 URL을 변경해주자.

혹은 주석 달려있는것 처럼 List 형태로 넣어서 여러개의 URL을 사용해도 될듯 하다.

yml 파일로 동적으로 이용할 수 있는 방법도 있을듯 하니 추후 사용할 때 가볍게 정리할것 같다.

 

참고자료

 

OpenAPI Specification을 이용한 더욱 효과적인 API 문서화 | 카카오페이 기술 블로그

사실상의 표준으로 발돋움 중인 OpenAPI Specification을 이용한 API 문서화 방법(Swagger와 Spring REST Docs의 장점을 합치는 방법)을 공유드립니다.

tech.kakaopay.com

 

 

내가 만든 API를 널리 알리기 - Spring REST Docs 가이드편

'추석맞이 선물하기 재개발'에 차출되어 API 문서화를 위해 도입한 Spring REST Docs 를 소개합니다.

helloworld.kurly.com

 

 

[리팩토링] Swagger UI + Spring RestDocs 적용기

Swagger UI 와 Spring RestDocs 의 장 단점의 비교Swagger UI 의 장점직관적인 UI스웨거는 API 에 대한 요청과 응답 등을 시각적으로 표현하여 사용자가 쉽게 이해할 수 있습니다실시간 테스트API 엔드포인트

oth3410.tistory.com

 

+ Recent posts