신비한 개발사전

Spring REST Docs 적용기 본문

Backend

Spring REST Docs 적용기

jbilee 2025. 6. 7. 16:17

Spring REST Docs는 API 문서를 작성할 때 필요한 각종 코드 스니펫을 자동으로 생성해주는 도구다. 생성된 스니펫을 조합해 자유도 높은 형식으로 API 문서를 만들어낼 수 있다.

 

순수한 문서이기 때문에 Swagger처럼 API 호출을 시뮬레이팅할 순 없지만, 테스트 코드를 기반으로 문서화를 지원하기 때문에 겸사겸사 테스트 코드를 작성하는 기회를 마련해주기도 한다.

 

Spring REST Docs는 AsciiDocMustache 문법을 사용한다.

 

초기 세팅

build.gradle에 REST Docs 관련 설정을 먼저 추가해준다. REST Docs가 생성해주는 스니펫을 어디에 저장할지, 그리고 빌드할 때 어디에 있는 asciidoc으로 최종 HTML 문서를 만들지 등을 설정한다.

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

configurations {
    asciidoctorExt
}

dependencies {
    asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor:3.0.3'
    testImplementation 'org.springframework.restdocs:spring-restdocs-restassured:3.0.3'
}

// 스니펫을 저장할 디렉토리 명시
ext {
    snippetsDir = file('build/generated-snippets')
}

test {
    useJUnitPlatform()
    outputs.dir snippetsDir
}

asciidoctor {
    baseDirFollowsSourceDir()
    inputs.dir snippetsDir
    configurations 'asciidoctorExt'
    dependsOn test
}

// asciidoctor 작업이 끝난 후에 outputDir에 생성된 문서 파일을 정적 폴더로 이동시키는 작업
bootJar {
    dependsOn asciidoctor
    from ("${asciidoctor.outputDir}") {
        into 'static/docs'
    }
}

 

테스트 코드 작성

REST Docs는 테스트 코드를 먼저 실행한 다음 테스트가 통과해야 문서(스니펫)를 생성할 수 있다. 성공한 테스트 결과를 기준으로 생성되는 스니펫들을 활용해 문서를 작성하는 것이다.

 

이 스니펫에는 실제로 테스트에 사용된 요청과 응답 값이 들어가게 된다. 테스트가 성공해야 스니펫을 생성해주는 것이 당연하다.

 

테스트 코드로 스니펫을 생성하도록 설정하기 위해 테스트 클래스에도 약간의 준비 작업이 필요하다. 클래스 단위에 @ExtendWith 애노테이션을 추가하고, @BeforeEach 메서드에서 RequestSpecification 스펙을 초기화한다.

@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
@ExtendWith({SpringExtension.class, RestDocumentationExtension.class})
class ReservationControllerTest {

    private RequestSpecification spec;
    
    @BeforeEach
    void setUp(RestDocumentationContextProvider restDocumentation) {
        this.spec = new RequestSpecBuilder()
                .addFilter(documentationConfiguration(restDocumentation))
                .build();
    }
}

 

이제 엔드포인트 테스트에서 발생하는 HTTP 요청과 응답 처리 과정을 문서화할 수 있게 됐다. Spring REST Docs는 MockMvc, WebTestClient, RestAssured를 지원하는데, 기존에 모든 컨트롤러 테스트가 이미 RestAssured로 짜여져 있어서 RestAssured에 적용해봤다.

@Test
@DisplayName("지나간 날짜와 시간에 대한 예약 생성은 불가능하다.")
void createReservationIsPastDateExceptionTest() {
    // ...중략...
    RestAssured.given(this.spec).log().all() // RequestSpecification 전달
              .filter(document("hello")) // "build/generated-snippets" 하위 "hello" 폴더에 스니펫 생성
              .when().post("/reservations")
              .then().log().all();
}

 

문서화를 하기 위해 최소한으로 필요한 코드다. RestAssured.given()에 RequestSpecification을 전달하고 filter 메서드를 체이닝해 RestAssuredRestDocumentation.document 정적 메서드를 호출하면, 설정 파일에서 명시한 폴더에 아래와 같이 .adoc 파일들이 생성된다.

Spring REST Docs는 디폴트로 6가지 스니펫을 생성해준다.

 

파일을 하나씩 열어보면 내가 테스트 코드에 사용한 값들 그대로 curl 요청, HTTP 메세지 본문 등의 데이터가 생성된 것을 확인할 수 있다.

 

HTTP 요청이 성공했으면 성공 결과가, 실패했으면 실패 응답이 그대로 문서화된다. 따라서 테스트 코드를 꼼꼼하게 짜면 짤 수록 문서 작성에 필요한 스니펫들을 한 번에 최대한 많이 얻을 수 있다.

 

문서화 작업

테스트 코드를 작성했으면, 이제 자동으로 생성된 스니펫들을 조합해야 한다. 공식 문서에 나와있는 대로, 환경에 따라 올바른 디렉토리에 asciidoc 폴더를 생성해 원하는 이름의 .adoc 파일을 만든다. 이 파일은 API 문서를 최종적으로 빌드할 때 .html 파일로 변환되기 때문에, 내가 문서화하고자 하는 주요 컨텐츠를 여기에 작성하면 된다.

 

생성된 스니펫을 문서로 가져오려면 include::{snippets} 또는 operation:: 문법을 사용한다. 스니펫을 생성하는 output 디렉토리 내 폴더 경로와 파일명을 명시하면 된다. 둘의 차이는 파일명이 스니펫 블록 위에 헤더로 들어가느냐 아니냐다. (코드 밑 이미지 참고)

// operation:: 사용 예시
operation::login-controller-test/login-check-test[snippets='http-request']
// 복수 스니펫 명시 가능
operation::login-controller-test/login-check-test[snippets='curl-request,http-response,response-fields']


// include:: 사용 예시
include::{snippets}/login-controller-test/login-check-test/http-request.adoc[]

(좌) 코드, (우) 미리보기

 

include:: 문법에서 {snippets}/ 부분을 제외해 다른 .adoc 문서를 임베드할 수도 있다. 한 파일에 전체 문서를 작성하지 않고, 필요에 따라 컨텐츠를 분리해서 조합할 수 있다.

= 방탈출 예약 서비스 API

방탈출 예약 페이지에서 호출하는 API 목록을 정리한 문서입니다.

// member.adoc에 member API 내용만 작성해 불러올 수 있다
include::member.adoc[]

include::reservation.adoc[]

include::theme.adoc[]

include::time.adoc[]

 

스니펫 커스터마이징

테스트 코드에 특정 메서드를 호출해 스니펫의 모양새도 커스터마이징할 수 있다.

 

예를 들어 응답 본문이 JSON일 때 REST Docs는 기본적으로 한 줄로만 작성해주는데, prettyPrint() 메서드를 통해 line break를 추가해줄 수 있다.

RestAssured.given(this.spec).log().all()
          .filter(document("{class-name}/{method-name}",
                  preprocessRequest(prettyPrint()),
                  preprocessResponse(prettyPrint())))
                  // 이하 코드 생략

 

이외에도 공식 문서에 다양한 커스터마이징 방법이 나와있다.

 

 

참고: