kimyu0218
  • [etc] Spring REST Docs와 Swagger UI를 사용한 API 문서화
    2025년 01월 17일 15시 20분 49초에 업로드 된 글입니다.
    작성자: @kimyu0218

    프론트엔드(클라이언트)와 백엔드 사이 원활한 협업을 위해서는 API가 필수적으로 작성되어야 한다. 이때 중요한 역할을 하는 것이 바로 OpenAPI, Swagger, Spring REST Docs다.

    • OpenAPI : API를 정의하기 위한 표준 형식 (주로 JSON과 YAML 형식으로 작성)
    • Swagger : OpenAPI 스펙을 기반으로 API 문서를 시각화하는 도구
    • Spring REST Docs : 스프링에서 제공하는 API 문서화 도구


    Swagger vs. Spring REST Docs

    스웨거는 컨트롤러에 몇 가지 어노테이션만 달아주면 API 문서를 자동으로 만들어준다. 문서화가 간편하다는 장점도 있지만 단점도 가진다.

    • 스웨거 어노테이션과 비즈니스 로직이 섞인다 (침투적)
    • 테스트가 없어 문서의 신뢰성이 낮다 (실제 로직과 스웨거 어노테이션 사이 불일치 발생 가능!)

    반면, Spring REST Docs는 테스트를 통해 문서와 로직이 일치하도록 강제한다. 또한, 테스트에 API 문서에 필요한 코드가 작성되므로 비즈니스 로직과 분리된다. (하지만 UI는 스웨거가 좀 더 보기 좋다)

    💡 Spring REST Docs
    Spring REST Docs는 정확하고 읽기 쉬운 API 문서를 작성하는 데 도움을 주는 도구다. Spring REST Docs는 Asciidoctor를 사용하는데, 이는 일반 텍스트를 HTML로 변환해준다.
    • 테스트가 실행되면 API에 대한 정보를 수집하여 snippet을 생성한다 (gradle의 경우, `build/generated-snippets`에 저장)
    • snippet은 Asciidoctor로 문서화되고, API 문서의 적절한 위치에 삽입된다
    • API 구현이 변경되어 더 이상 snippet이 유효하지 않으면 테스트가 실패하므로 문서와 코드 간 일관성을 유지할 수 있다

     

    Swagger와 OpenAPI

    스웨거에 대해 조금 더 자세히 살펴보자. 스웨거는 내부적으로 Swagger UI를 사용한다. Swagger UI는 OpenAPI Specification(OAS)로 작성된 문서를 브라우저 상에 표현하기 위해 HTML로 변환한다.

    Spring REST Docs와 Swagger UI로 신뢰성 높고 깔끔한 API 문서 만들기

    Spring REST Docs와 Swagger UI를 사용하면 두 문서화 도구의 장점만 더하여 신뢰성 있고 예쁜 API 문서를 만들 수 있다. Swagger UI로 HTML 파일을 생성하기 위해서는 Spring REST Docs로 OAS 파일을 만들어야 한다. 이를 도와주는 것이 restdocs-api-spec이다. 해당 프로젝트 안에는 다음 주요 컴포넌트들이 있다.

    • restdocs-api-spec : Spring REST Docs 관련 코드를 가진 프로젝트
    • restdocs-api-spec-mockmvc : spring-rest-docs-mockmvc를 사용하는 `MockMvc`가 restdocs-api-spec으로 마이그레이션할 수 있도록 지원


    API 문서화를 하기 위한 주요 작업은 크게 두 가지다.

    1. restdocs-api-spec을 활용하여 OAS 파일을 생성한다
    2. Swagger UI로 OAS 파일을 HTML 파일로 변환한다


    먼저, 프로젝트에 restdocs-api-spec 의존성을 추가한다.

    plugins {
        ...
        id("com.epages.restdocs-api-spec") version "0.19.4"
    }
    ...
    dependencies {
        ...
        testImplementation("com.epages:restdocs-api-spec-mockmvc:0.19.4")
        testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc")
        ...
        )
    }
    ...
    tasks.named("build") {
        finalizedBy("openapi3")
    }
    
    openapi3 {
        this.setServer("http://localhost:8080")
        title = "Jaknaeso API"
        description = "Jaknaeso API description"
        version = "0.1.0"
        format = "yaml"
    }

    `restdocs-api-spec-mockmvc`를 의존성으로 추가하여 `MockMVC`와 함께 문서화를 진행한다. 빌드가 완료되면 openapi3 작업을 실행하여 OAS가 만들어지도록 구현했다.

    프로젝트를 빌드하면 `build/api-spec` 하위에 `openapi3.yaml`이 생기는 것을 확인할 수 있다. 이 파일을 Swagger UI에게 전달하여 예쁜 API 문서로 만들어보자.

    Swagger UI는 API 문서를 시각화하는 도구다. swaggerapi/swagger-ui 이미지는 swagger-ui를 호스팅하는 간단한 도커 컨테이너로, 나는 해당 이미지에 OAS 파일을 마운트하여 기존 설정을 대체하여 프로젝트 API를 문서화할 것이다.

    swaggerapi/swagger-ui의 도커 파일을 보면, 환경 변수를 설정하고 8080번 포트를 노출하는 것을 볼 수 있다. 나는 문서를 참고하여 환경 변수를 변경하고, 우리 프로젝트의 OAS 파일을 가리키도록 수정했다.

    sudo docker run -d -p 80:8080 \
        -v /build/api-spec:/usr/share/nginx/html/docs \
        -e URL="/docs/openapi3.yaml" \
        --name api-doc swaggerapi/swagger-ui:latest

    매번 컨테이너를 종료하고 다시 띄우는 과정이 번거로울 것 같아 task를 작성하기로 결정했다. 1) 컨테이너가 띄워져있는지 확인하고, 2) 컨테이너가 없는 경우에만 위 도커 CLI를 실행할 것이다.

    val containerName = "api-doc"
    
    tasks.register<Exec>("runSwagger") {
        commandLine("/usr/local/bin/docker", "ps", "--filter", "name=$containerName", "-qa")
        standardOutput = ByteArrayOutputStream()
    
        doLast {
            val containerId = standardOutput.toString().trim()
            if (containerId.isEmpty()) {
                exec {
                    commandLine(
                        "/usr/local/bin/docker",
                        "run", "-d",
                        "-p", "8088:8080",
                        "--name", containerName,
                        "-v", "./build/api-spec:/usr/share/nginx/html/docs",
                        "-e", "URL=/docs/openapi3.yaml",
                        "swaggerapi/swagger-ui:latest"
                    )
                }
            }
        }
    }

    이제 `./gradlew runSwagger`만 실행하면 생성한 OAS 파일이 도커 컨테이너 마운트되어 실행된다. 더 이상 스웨거를 수정해서 컨테이너를 종료 후 다시 실행하지 않아도 된다!

    http://localhost:8088로 들어가면 내가 작성한 API 문서를 볼 수 있다 👍


    참고자료

    댓글