API Docs 개선

DeadWhale·2023년 11월 30일
0

업로드중..
현재 프로젝트는 RestDocs 기반의 테스트 기반으로 만들어진 API 문서화를 진행했습니다.

rest docs 는 매우 치명적인 문제가 있는데 예쁘지가 않습니다.

img_2.png

물론 신뢰성이 있다는 장점이 있긴 하지만 그래도 아쉬운 부분이라

계속 신경 쓰고 있었는데.

마침 프로젝트 개선 과정을 시작하게 되서 같이 진행하게 되었습니다.


이 주제는 레퍼런스가 매우 많아서 진행하실분들은 참고하시면 좋을것 같습니다.

컬리 블로그 Spring REST Docs
SwaggerUI + Spring REST Docs 함께 사용하기(feat. Rest Assured)
우아한형제들 - Spring Rest Docs 적용

참조 레퍼런스들도 매우 많고 저도 이 레퍼런스들을 참고해서 진행했습니다.

워낙 좋은 자료들이 많아 적용하실 분들은 제 글이 아닌 다른 레퍼런스를 참조해서 하시는걸 추천 드리고

저의 경우의 글을 정리하겠습니다.

현재 프로젝트의 문서 정리의 기본적인 맥락은 매우 간단합니다

Restdocs 기반의 문서화는 테스트 후 → asciidoc 스닙핏 파일 생성 → 스닙핏 취항 정리 → html 파일 생성 → html 파일을 웹에 띄워서 확인

img_3.png

이러한 흐름으로 진행됩니다.

여기서 asciidoc 파일이 아닌 openAPI를 활용해 yaml 형태의 데이터를 추출하고

이를 활용해 swagger-ui에 적용하는 방식으로 진행하였습니다.

plugins {
    id "org.asciidoctor.jvm.convert" version "3.3.2"
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    asciidoctorExt
}

dependencies {
    // RestDocs
    asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}

ext { // 전역 변수
    snippetsDir = file('build/generated-snippets')
}

test {
    outputs.dir snippetsDir
}

asciidoctor {
    inputs.dir snippetsDir
    configurations 'asciidoctorExt'

    sources { // 특정 파일만 html로 만든다.
        include("**/index.adoc")
    }
    baseDirFollowsSourceFile() // 다른 adoc 파일을 include 할 때 경로를 baseDir로 맞춘다.
    dependsOn test
}

bootJar {
    dependsOn asciidoctor
    from("${asciidoctor.outputDir}") {
        into 'static/docs'
    }
}


tasks.register('cleanDocs', Delete) {
    delete 'docs'
}

tasks.register('copyRestDocs', Copy) {
    dependsOn asciidoctor, cleanDocs
    from file("build/docs/asciidoc/index.html") // 복사할 파일 위치
    into 'docs' // 경로 수정
}

build {
    dependsOn copyRestDocs
}

주석도 달아놓았지만 간단하게 설명하자면

asciidoc 파일을 html 파일로 변환하는 과정을 진행합니다.

또한 저는 github-pages를 통한 별도로 문서를 관리하고 있어서

build.gradle에 github-pages 관련된 내용도 있습니다.

${rootDir}/docs 폴더에 html 파일이 생성되고 github에 올라가면 자동적으로 배포되게 됩니다.

img_4.png

dependsOn 과 같은 문법은 gradle의 task 관련 문법입니다.

선행 task가 끝나야만 진행되는 task를 의미합니다.

예를 들어 test task가 끝나야만 asciidoctor task가 진행되고

asciidoctor task가 끝나야만 bootJar task가 진행됩니다.

plugins {
    id 'com.epages.restdocs-api-spec' version '0.18.2' //rest-docs-openapi3
    id 'org.hidetake.swagger.generator' version '2.18.2' //swagger - ui
}
swaggerSources {
    sample {
        setInputFile(file("docs/openapi3.yaml"))
    }
}

dependencies {
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' // mockmvc
    testImplementation "org.springdoc:springdoc-openapi-ui:1.6.11" // restdocs-openapi3
    testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.2' // restdocs-openapi3
    swaggerUI 'org.webjars:swagger-ui:5.9.0'
}

openapi3 {
    server = 'http://localhost:8080'
    title = 'Petlink API'
    description = 'Petlink API description'
    version = '0.1.0'
    format = 'yaml'
    outputDirectory = "docs"
}

tasks.withType(GenerateSwaggerUI).configureEach {
    dependsOn 'openapi3'
    doFirst {
        def swaggerUIFile = file("${openapi3.outputDirectory}/openapi3.yaml")

        def securitySchemesContent = "  securitySchemes:\n" +                           \
                                                               "    APIKey:\n" +                           \
                                                               "      type: apiKey\n" +                           \
                                                               "      name: Authorization\n" +                           \
                                                               "      in: header\n" +                          \
                                                               "security:\n" +
                "  - APIKey: []  # Apply the security scheme here"
        swaggerUIFile.append securitySchemesContent
    }
}

// 생성된 openapi3 스펙을 기반으로 SwaggerUISample 생성 및 static/docs 패키지에 복사
bootJar {
    dependsOn generateSwaggerUISample
    doLast {
        copy {
            from "${generateSwaggerUISample.outputDir}" // 'swagger-ui-sample' 디렉토리
            into "${project.rootDir}/docs"             // 프로젝트 루트의 'docs' 디렉토리로 복사
        }
    }
    println("Files copied from ${generateSwaggerUISample.outputDir} to ${project.rootDir}/docs")
}

변경된 내용입니다. 기본적으로 이전에 asciidoc 파일을 html 파일로 변환하는 과정을 전부 제거 후
openapi3를 활용해 yaml 파일을 생성하고 이를 기반으로 swagger-ui를 생성하는 방식으로 변경하였습니다.
이 과정중 매우 헷갈리는 부분이 경로였는데 저는 깃허브 페이지의 경로를 맞추기 위해
openapi3의 outputDirectory를 docs로 변경하였습니다.
이 경로는 저의 경우에는 ${rootDir}/docs로 설정되어 있습니다.
여러분도 하다가 뭔가 꼬이면 경로를 주의깊게 보는 것도 좋습니다.

별도로 테스트 코드경우 wapper를 활용해 선언부분만 변경하면 되어 큰 문제 없이 진행되었습니다.

관련해서 소스코드가 필요하신 분들은 아래의 링크를 참조하시면 됩니다.

petlink/pr/50 : https://github.com/f-lab-edu/petLink/tree/pr/50/restdocs-openapi3

완성된 api docs

img.png

성공적으로 예쁜 api docs를 만들었습니다.

아쉬운 점은 swagger-ui를 활용해 만든 api docs는 커스텀에 제약이 좀 많아 아쉬운것 같습니다.

그래도 테스트 기반의 신뢰성과 테스트까지 가능한 장점이 있어서

여러분들도 이 방식을 고려해보시는걸 추천드립니다.

0개의 댓글