RestDocs

김민창·2022년 7월 27일
0
post-thumbnail
post-custom-banner

postman으로 API 설계를 했는데, API의 변경이 발생할 때마다 동기화를 손수 해주는 게 굉장히 힘들었다
문서를 항상 최신화할 순 없을까

REST Docs가 뭐죠

  • 테스트 코드 기반으로 Restful API 문서를 돕는 도구

  • 테스트 코드가 일치하지 않으면 테스트 빌드가 실패하기 때문에 테스트 코드로 검증된 문서가 보장된다

  • Asciidoctor를 이용하여 HTML등 다양한 포맷으로 문서를 자동으로 출력

  • OAS(OpenAPI Specification)를 활용하여 Swagger를 쉽게 만들수 있다

출처 및 자세한 내용

build.gradle 설정


plugins {
	// AsciiDoc 파일을 컨버팅 후, Build 폴더에 복사하기 위한 플러그인
    id "org.asciidoctor.convert" version "1.5.9.2"
}

repositories {
    mavenCentral()
}

dependencies {
    // Ascii Docs
    asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor'
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}

ext {
    // Snippet 의 생성 위치를 지정
    snippetsDir = file('build/generated-snippets')
}

test {
	// test시 JUnit을 사용
    useJUnitPlatform()
    outputs.dir snippetsDir
}

asciidoctor {
    // Snippets 디렉토리를 Input 디렉토리로 설정
    inputs.dir snippetsDir
    // 문서 생성 전 테스트가 실행되도록 test 에 종속 설정
    dependsOn test
}

bootJar {
    // 빌드 전 문서 생성 확인
    dependsOn asciidoctor
    // 생성된 문서를 static/docs 에 복사
    from ("${asciidoctor.outputDir}/html5") {
        into 'static/docs'
    }
}

테스트 만들기

FriendController.java

  • 편의상 요청을 보내면 항상 4개의 데이터를 배열로 보내주도록 만들었다
@RestController
public class FriendController {
    @GetMapping("/friends")	
    public ResponseEntity<List<FriendDto>> friendList(
            @RequestParam Long userNo
    ){
        List<FriendDto> res = new ArrayList<>();
        for (int i = 1; i < 5; i++){
            res.add(
                    FriendDto
                            .builder()
                            .id((long) i)
                            .name("name" + i)
                            .isAlumni(i%2==0)
                            .build()
            );
        }
        return new ResponseEntity<>(res, HttpStatus.OK);
    }
}

FriendControllerTest.java

@WebMvcTest
@AutoConfigureRestDocs
class FriendControllerTest {

    @Autowired
    private MockMvc mockMvc;
    
    @Autowired
    private ObjectMapper objectMapper;						// (0)

    @Test
    public void friendListTest() throws Exception {
        ResultActions resultActions = mockMvc.perform(
                get("/friends")								// (1)
                        .param("userNo","10")				// (2)
                        .contentType(MediaType.APPLICATION_JSON));

        resultActions
                .andExpect(status().isOk())					// (3)
                .andDo(print())
                .andDo(document("friends-list",				// (4)
                        preprocessRequest(prettyPrint()),	// (5)
                        preprocessResponse(prettyPrint()),
                        requestParameters(					// (6)
                                parameterWithName("userNo").description("친구 찾을 사람 ID")
                        ),
                        responseFields(						// (7)
                                fieldWithPath("[].id").type(NUMBER).description("태그 아이디"),
                                fieldWithPath("[].name").type(STRING).description("태그 제목"),
                                fieldWithPath("[].isAlumni").type(BOOLEAN).description("매니저 유무"))
                ));
    }
}
  1. RequestBody 요청을 처리하고 싶을때 추가
  2. 맵핑할 URI 기재
    • RestDocumentationRequestBuilders 사용
    • PathVariable이 있다면 차례대로 가변배열처럼 넣어준다
  3. 더미로 값을 넣어준다
    • RequestBody이 있다면 .content(objectMapper.writeValueAsString(dto)) 형태로 넣어준다
  4. .andExpect로 해당 테스트의 결과를 예상하고 비교할 수 있다.
    하나라도 일치하지 않는다면 fail
  5. 어떤 이름으로 문서를 만들것인지 정한다.
    MockMvcRestDocumentationWrapper 사용
  6. 결과값을 json의 형태로 알아보기 쉽게 해준다
  7. 파라미터로 들어오는 값 이름 비교 및 설명
    • PathVariable이 있다면 이름을 pathParameters로 사용
  8. 응답의 이름, 타입 비교 및 설명
  • prettyPrint() 사용
  • prettyPrint() 미사용

ascii docs 조립하기

테스트 코드를 동작하면 build/generated-snippets 폴더 하위로 friends-list 가 생기는데, 내가 docs에 넣고싶은 파일들을 넣어준다

Docs 폴더를 만들어서 (이름).adoc 파일을 만든다

= Rest Docs
:doctype: book
:icons:font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:

ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]

=== 친구 리스트 반환

=== REQUEST
include::{snippets}/friends-list/http-request.adoc[]
include::{snippets}/friends-list/request-parameters.adoc[]

=== RESPONSE
include::{snippets}/friends-list/http-response.adoc[]
include::{snippets}/friends-list/response-fields.adoc[]

완성


OAS 적용

build.gradle에 추가

plugins {
    id 'com.epages.restdocs-api-spec' version '0.16.0'
}

dependencies {
    testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.16.2'
}


openapi3 {
    server = 'http://localhost:8080'
    title = 'Board API'
    description = 'Board API description'
    version = '0.1.0'
    format = 'json'
    outputDirectory = "./"
    outputFileNamePrefix = 'swagger-board-api-spec'
}

RestDocumentationRequestBuilders, MockMvcRestDocumentationWrapper 를 사용했다면 문제없이 사용 가능하다

터미널에서 gradle openapi3

profile
개발자 팡이
post-custom-banner

0개의 댓글