Framework/Spring / / 2023. 8. 7. 13:32

[Spring] Spring REST Docs API 문서 만들기

반응형

아래 내용은 Spring REST Docs를 사용하면서 정리해둔 내용입니다.

 

API 문서 자동화

백엔드와 프론트엔드 개발자 사이의 원활한 협업을 위해서는 REST API 명세에 대한 문서화가 잘 되어있어야 한다.

 

물론 구글 독스, 스프레드 시트, 위키, 노션 등을 사용해서 직접 API 명세를 문서화할 수 있지만, 우리는 개발자 아니겠는가. 자동화를 해야지만 무언가 나도 편하고 간지도 난다.

 

API 문서 작성을 도와주는 자동화 도구가 많은데, Java Spring 진영에서 가장 많이 쓰이는 API 문서 자동화 도구는 크게 Swagger Spring REST Docs 이 두 가지가 있다.

 

  • API 문서 자동화 도구 : Spring REST Docs
  • 테스트 도구 : MockMvc(@WebMvcTest)
  • 문서 : Asciidoc

 

Swagger vs Spring REST Docs

  Swagger Spring REST Docs
설명 메소드 상단에 어노테이션을 통해 API 기술 테스토 코드를 작성하며 API를 명세하는 기술
장점 직관적, 간단

규모가 작고 간단한 프로젝트에서 강력한 도구
테스트가 성공하는 올바른 프로덕션 코드에 대해서만 문서 작성 가능

테스트 실패 시, 문서 생성 불가 (API 스펙과 일치하는 문서 작성 가능)
단점 코드에 문서화에 대한 코드가 포함 (가독성 떨어짐)

API 스펙이 변경되어도 어노테이션을 변경하지 않으면 API 문서의 수정이 불가능하다. (문서와 실제 API 스펙의 일치를 보장하지 않음)

규모가 커질수록 코드의 유지보수가성 감소 및 변경할게 많아짐.
----

 

Asciidoc

간결한 표현력을 가지고 있는 마크다운에 비해 Asciidoc는 전문적인 문서를 작성할 수 있는 강력한 표현력을 가지고 있다. 또한 앞서 이야기한 include 기능을 제공한다.

 

물론 마크다운도 Slate를 사용하면 import 기능을 사용할 수 있지만, 이동욱님 블로그에 따르면 굉장히 불편하다고 한다. Slate를 사용하려면, Ruby와 Gem을 설치해야하고, 생성되는 문서도 일반적인 스프링 문서와 달라 친숙하지 않다는 점, Ruby 의존성 때문에 빌드시간이 오래걸린다는 점 등이 있다고 한다.

 

API 문서 자동화 환경 구성

직접 빈 스프링 프로젝트에서 문서 자동화 환경을 구축해보자.

 

참조 : Spring REST Docs

Spring REST Docs 공식 문서를 참고.

 

build.gradle 설정

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

configurations {
    asciidoctorExt//2
}

ext {
    snippetsDir = file('build/generated-snippets')//5
}

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

tasks.named('test') {
    useJUnitPlatform()
}

test {
    outputs.dir snippetsDir//6
}

asciidoctor {//7
    inputs.dir snippetsDir//8
    configurations 'asciidoctorExt'//9
    dependsOn test//10
}

bootJar {
    dependsOn asciidoctor//11

    copy {//12
        from asciidoctor.outputDir
        into "src/main/resources/static/docs"
    }
}
  1. asciidoctor 플러그인을 적용한다.
  2. asciidoctor를 확장하는 asciidoctorExt 에 대한 종속성 구성을 선언한다.
  3. asciidoctorExt에 spring-restdocs-asciidoctor 의존성을 추가한다. 이 종속성이 있어야 build/generated-snippets 에 있는 .adoc 파일을 읽어들여 .html 파일로 만들어낼 수 있다.
  4. MockMvc를 사용하기 위한 의존성을 추가한다. MocMvc 대신에 WebTestClient를 사용하려면 spring-restdocs-webtestclient을, RestAssured를 사용하려면 spring-restdocs-restassured을 대신 사용한다.
  5. 생성되는 스니펫들이 생성되는 디렉토리를 설정한다.
  6. 스니펫을 snippetsDir로 생성하도록 test 태스크를 설정한다.
  7. asciidoctor 태스크에 대한 설정을 한다.
  8. 불러올 스니펫 위치를 snippetsDir로 설정한다.
  9. Asciidoctor 확장에 대한 설정을 한다.
  10. test 태스크 이후에 asciidoctor를 실행하도록 설정한다.
  11. jar가 빌드되기 전에 문서가 생성되었는지 확인한다.
  12. 생성된 문서를 jar static/docs 디렉토리에 복사한다.
11, 12는 생성된 문서를 프로젝트의 jar 파일에 패키징 할 수 있는 설정입니다.
asciidoctor는 adoc 파일을 html 등으로 변환해주는 도구이다.

 

JUnit 5 테스트 설정

JUnit 5를 사용할 때 문서 조각 생성의 첫 번째 단계는 RestDocumentationExtension테스트 클래스에 적용하는 것이라고 한다. (공식문서 참고)

@ExtendWith(RestDocumentationExtension.class)
public class JUnit5ExampleTests {

RestDocumentationExtension프로젝트의 빌드 도구에 따라 출력 디렉터리로 자동 구성됩니다.

 

빌드 도구출력 디렉토리

메이븐 target/generated-snippets
그레이들 build/generated-snippets

 

@BeforeEach다음으로 MockMvc 또는 WebTestClient 또는 REST Assured를 구성하는 방법을 제공해야 합니다 . 다음 목록은 그렇게 하는 방법을 보여줍니다.

private MockMvc mockMvc;

@BeforeEach
void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
	this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
			.apply(documentationConfiguration(restDocumentation)) 
			.build();
}
  • 인스턴스 MockMvc는 MockMvcRestDocumentationConfigurer. documentationConfiguration()의 정적 메소드 에서 이 클래스의 인스턴스를 얻을 수 있습니다

 

위의 설정을 참고하여 Test code 설정 및 작성 예시

//설정 시작
@SpringBootTest
@ExtendWith(RestDocumentationExtension.class)
public class PostControllerDocTest {

    private MockMvc mockMvc;

    @Autowired
    private PostRepository postRepository;

    @BeforeEach
    void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                .apply(documentationConfiguration(restDocumentation))
                .build();
    }

	//TEST Code 예시
    @Test
    @DisplayName("글 단건 조회 테스트")
    void test1() throws Exception {
        //given
        Post post = Post.builder()
                .title("제목")
                .content("내용")
                .build();

        postRepository.save(post);

        //expected
        this.mockMvc.perform(get("/posts/{postId}", 1L)
                        .accept(APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk())
                .andDo(document("index"));
    }

}

 

테스트 실행 및 adoc 스니펫 생성

방금 작성한 컨트롤러 테스트 코드를 실행해보자.

그리고 build > generated-snippets > {document() 에서 지정한 경로} 로 이동해보자.

 

위와 같이 .adoc 이라는 확장자로 여러 문서 조각들이 생성된 모습을 확인할 수 있다. 예로 http-response.adoc 파일을 열어보면 아래와 같은 결과물이 출력된 것을 확인할 수 있다.

[source,http,options="nowrap"]
----
GET /posts/1 HTTP/1.1
Accept: application/json
Host: localhost:8080

----

[source,http,options="nowrap"]
----
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 44

{"id":1,"title":"제목","content":"내용"}
----

 

asciidoc 문서 생성

테스트가 끝나고 생성된 adoc 스니펫을 모아 완전한 API 명세 문서로 만들어보자. 

 

src > docs > asciidoc 디렉토리를 생성(폴더 구조 똑같이 해줘야 함.)하고, .adoc 파일을 직접 생성하고 asciidoc 문서를 작성한다.

 

include 를 사용하여 전 단계에서 생성된 adoc 스니펫을 문서에 불러올 수 있다.

 


스니펫 사용

생성된 스니펫을 사용하기 전에 소스 파일을 생성(.adoc)해야 합니다.

 

빌드 도구소스 파일생성된 파일

메이븐 src/main/asciidoc/*.adoc target/generated-docs/*.html
그레이들 src/docs/asciidoc/*.adoc build/asciidoc/html5/*.html

그런 다음 포함 매크로를 사용하여 수동으로 생성된 Asciidoc 파일(이 섹션의 앞부분에서 설명)에 생성된 스니펫을 포함할 수 있습니다 . 

스니펫 출력 디렉토리를 참조하기 위해 빌드 구성 에서 구성 snippets에 의해 자동으로 설정되는 속성을 사용할 수 있습니다 . 

include::{snippets}/index/curl-request.adoc[]

 

아래는 위를 참고하여 src > docs > asciidoc > .adoc 파일을 작성한 예시이다.

#제목

:toc:

== 글 단건 조회

=== 요청
include::{snippets}/index/curl-request.adoc[]

=== 응답
include::{snippets}/index/http-request.adoc[]

=== CURL
include::{snippets}/index/http-response.adoc[]

 

html 문서 생성

우리는 위에서 jar 생성된 문서를 프로젝트의 jar 파일에 패키징 할 수 있는 설정을 했기 때문에, 프로젝트를 빌드 시키면 html 파일을 설정한 디렉토리에 생성시킬 수 있다.

 

build도 가능하고 asciidoctor만 실행해도 상관없다.

$ ./gradlew asciidoctor

 

빌드를 하면 문서는 우리가 src > docs > asciidoc 에 생성한 adoc 파일을 기반으로 생성된다.

 

정리하자면 gradle의 asciidoctor task는 test 실행 후 build에 snippet. index.html을 생성한다.

그리고 gradle build시 bootJar가 실행되면서 build 쪽에 있던 index.html을 /resources/static/docs로 복사한다.

 

http://localhost:8080/docs/{html 파일 이름}.html 으로 접속해보면,  HTML 파일이 정상적으로 보이는 것을 확인할 수 있을 것 이다.

 

참고

반응형

'Framework > Spring' 카테고리의 다른 글

[Spring] Transactional Propagation  (0) 2023.08.02
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유