Skip to content

Issue #4

gaya edited this page Oct 23, 2023 · 7 revisions

문서 자동화

개요

문서화는 중요합니다.
개발자들간의 소통 그리고 새로 합류할 개발자들에게 프로젝트에 대한 설명을 가장 잘 전달할 수 있는 것은, 프로젝트에 대해 잘 정리해둔 문서라고 생각합니다.

저희는 프로젝트를 진행하면서 정리된 API 문서를 작성할 필요가 있음을 느꼈고,
API 문서를 작성함에 앞서서는, 기능에 관한 내용을 엑셀에 써서 관리 하고 있었습니다.

엑셀 또는 워드 문서로 작성되어진 API 문서를 몇번 접해본 경험상,
개발 내용이 반영되지 않는 문서로 API 문서를 관리하는 경우에 나중에 가서는 실제 기능과 문서에 적혀진 기능이 동떨어져있는 경우를 보았습니다.

따리서 문서를 만들어주는 문서자동화 기능을 통해 API를 문서화 하기로 하였습니다.

문서화 도구에 도구에 대해 알아보고, 설정해보겠습니다.

문서화 도구

Java Spring 진영에서 가장 많이 쓰이는 API 문서 자동화 도구는 크게 SwaggerSpring REST DOCS 두가지가 있습니다.
이 두가지를 비교해보겠습니다.

Spring Rest Doc vs Swagger

Spring Rest Doc

테스트 코드를 작성하여 API를 명세하는 방식

아래는 Rest Doc 을 사용한 테스트 코드 중 일부 입니다.

@Test
@DisplayName("결제 확인")
void confirmReceipt() throws Exception {

     //생략

    .andDo(document("confirmReceipt",
            //Json 포매팅
            preprocessRequest(prettyPrint()),
            preprocessResponse(prettyPrint()),
            requestFields(
                fieldWithPath("paymentId").description("결제 ID"),
                fieldWithPath("reservationId").description("예약 ID"),
                fieldWithPath("amount").description("가격")
            ),
            responseFields(
                fieldWithPath("code").type(JsonFieldType.NUMBER).description("결과코드"),
                fieldWithPath("message").type(JsonFieldType.STRING).description("결과 메세지"),
                fieldWithPath("data.orderId")
                    .type(JsonFieldType.STRING)
                    .description("주문 ID")
            )
        )
    );
}

테스트 코드를 작성하여 API를 명세하기 때문에 로직 코드에는 문서를 위한 별도의 코드를 작성할 필요가 없습니다.
또한 테스트 코드가 성공해야 문서화가 만들어지다 보니 API스펙과 일치하는 문서를 작성할 수 있다는 장점이 있습니다.

문서 생성을 위해 반드시 테스트 코드가 필요함으로 API가 많으면 작성해야할 테스트 코드가 많아진다는 단점이 있을 수 있겠습니다.

테스트 코드가 성공하면 문서화 job을 실행시켜서 API에 대한 명세인 snippet이 생성되게 됩니다.
snippet들을 모아 하나의 문서로 작성할 수 있습니다.

img
▲ 다음과 같이 생성된 snippet을 모아

img ▲ 문서를 작성할 수 있다.

Swagger

코드에 어노테이션을 적어 명세하는 방식

swagger를 적용한 컨트롤러 코드의 일부분 입니다.

@Operation(summary = "Get a product by id", description = "Returns a product as per the id")
@ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "Successfully retrieved"), 
        @ApiResponse(responseCode = "404", description = "Not found - The product was not found")
    })
@GetMapping("/products/{id}")
public ResponseEntity<Product> getProduct(@PathVariable("id") @Parameter(name = "id", description = "Product id", example = "1") Long id) {
    //retrieval logic
    return ResponseEntity.ok(new Product(1, "Product 1", "$21.99"));
}

코드에 어노테이션을 붙여서 설명을 쓰면 API 문서를 생성해주는 방식입니다.

생성된 문서에서는 API를 직접 실행해 볼 수도 있다는 장점이 있습니다.

img ▲ 생성된 문서에서는 API를 직접 실행해 볼 수 있다.

단점도 있습니다.

  • 코드에 문서화관련 코드가 포함되면서, 가독성이 떨어질 수 있습니다.
  • 문서에 쓰여지는 내용은 실제 코드의 내용이 아닌 어노테이션에 적은 내용이기 때문에, 코드의 내용과 문서의 내용이 같다는 것을 보장 할 수 없습니다.

저희는 Spring Rest Doc 를 선택하였습니다.
로직 코드에 문서화 코드가 섞이지 않는 다는 점이 마음에 들었고,
테스트 코드를 통해서 API 스펙과 일치하는 문서가 생성된다는 점이 매력적으로 다가왔기 때문입니다.

Rest Doc에서 테스트 코드를 작성할 때 작성할 수 있는 테스트 도구로는 MockMvcRestAssured 가 있습니다.

Rest Doc 옵션

Rest Doc에서 테스트 도구로는 MockMvc 또는 RestAssured을 골라 사용할 수 있습니다. 문서를 작성하는데 사용하는 문법 양식으로는 Markdonwascii doc을 사용할 수 있습니다.

MockMvc vs RestAssured

MockMvc

MockMvc는 모의 HTTP 서블릿을 요청하는 유틸리티 클래스 입니다.
어플리케이션을 서버에 배포하지 않고도 스프링 MVC의 동작을 재현할 수 있습니다. Controller 테스트에 많이 이용 됩니다.
실제 환경과 동일한 @SpringBootTest를 사용할 필요가 없으므로 @WebMvcTest를 통해 controller 계층의 Bean들만 불러오고, 나머지 Bean들은 Mock(가짜)객체를 생성해서 사용함으로써 순수한 controller 로직을 테스트 할 수 있습니다.

RestAssured

RestAssured는 REST 웹 서비스를 검증하기 위한 라이브러리이며, 대부분 End-to-End Test(전 구간 테스트)에 사용됩니다. @SpringBootTest로 실제 요청을 보내서 전체적인 로직을 테스트하게 됩니다.


@SpringBootTest를 테스트를 수행하면 등록된 Spring Bean을 전부 로드하기 때문에 시간이 오래 걸립니다. 반면에 @WebMvcTest는 controller 계층의 Bean들만 로드하기 때문에 시간이 상대적으로 빠릅니다.

저희는 테스트 실행 비용이 높아지는 것을 원하지 않았기 때문에 MockMvc을 사용하였습니다.

Markdown vs Asciidoc

Markdown

특수기호와 문자만으로 간결하게 글을 작성할 수 있는 마크업 언어입니다. github의 readme도 markdown으로 작성할 수 있고, 개발자들에게는 여러모로 친근한 언어입니다.

문법이 간결하고 다양한 플랫폼이 지원한다는 장점은 있으나, 표준이 없기 때문에 도구에 따라선 동작하지 않거나 다르게 보이는 단점이 있습니다. 간결하기 때문에 모든 HTML 마크업을 대신하지 못한다는 한계점도 있습니다.

AsciiDoc

Markdown과 마찬가지로 글자로 문서를 작성할 수 있는 마크업 언어입니다. 전문적인 문서를 쓸 수 있도록 문법과 표현이 늘어난 것이 Markdown과의 차이점이라고 할 수 있습니다. 또한 include 기능을 지원합니다.

저희도 Markdown이 익숙하였지만, 문법이 강력하고 include기능이 있다는 점에서 AsciiDoc를 채택하였습니다.

build.gadle 환경 구성

사용하기로 결정한 Rest doc과 AsciiDoc으로 문서를 생성할 수 있도록 build.gradle 설정을 해보겠습니다.

공식 문서에서 보다 자세한 내용을 확인할 수 있습니다.
https://docs.spring.io/spring-restdocs/docs/2.0.8.BUILD-SNAPSHOT/reference/html5/

plugins {

    //ascii doc 플러그인
    id "org.asciidoctor.jvm.convert" version "3.3.2" 
}

configurations {

    //Asciidoctor를 확장하는 종속성에 대한 구성을 선언합니다
    asciidoctorExt
}

dependencies {

    //asciidoctorExt에 spring-restdocs-asciidoctor  의존성을 추가합니다.
    asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'

    //테스트 도구로 mockmvc로 사용하기 위한 의존성을 추가합니다. 
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'

    ext {
        //snippet 문서가 생성되는 장소를 지정해 줍니다
        snippetsDir = file('build/generated-snippets')
    }

    test {
        // 지정한 장소에 스니펫들이 생성되도록 test 태스크를 설정합니다.
        outputs.dir snippetsDir
    }

    //asciidoctor job을 구성합니다
    asciidoctor {

         //test 작업이 실행되면  snippetsDir을 입력받습니다.
        inputs.dir snippetsDir

        //asciidoctorExt 확장에 대한 설정을 합니다.
        configurations 'asciidoctorExt'

        //테스트 이후에 asciidoctor가 실행됩니다.
        dependsOn test
    }

}


문서 자동화를 위한 설정을 해보았습니다. 사용하기로 한 Rest Doc에서는 이제 테스트 코드를 통해 API명세를 작성하고, asciidoctor이라는 Job을 실행하게 되면 자동으로 문서가 작성됩니다.

저희가 작성한 테스트 코드는 👉 깃헙 링크에서 보실 수 있습니다.

Rest doc을 사용해 작성된 At_ticket API 명세는 (추가 예정) 에서 보실 수 있습니다.


참고

https://docs.spring.io/spring-restdocs/docs/2.0.8.BUILD-SNAPSHOT/reference/html5/
https://www.baeldung.com/swagger-set-example-description
https://hudi.blog/spring-rest-docs/
https://tecoble.techcourse.co.kr/post/2020-08-19-rest-assured-vs-mock-mvc/
https://woowabros.github.io/experience/2018/12/28/spring-rest-docs.html