본문 바로가기
Spring

[Spring Rest Docs] 테스트 코드를 통한 API 문서 만들기

by 덩라 2023. 10. 20.

API 를 개발하면 가장 중요한 부분이 바로 API 사용법 일 것 입니다. 그리고 다른 사람이 이 API 사용법을 빠르게 파악하기 위해서는 API 에 관련된 내용(URL, 요청에 필요한 데이터, 응답 등)을 잘 정리해놓는 것이 중요합니다. 

Spring 에서는 이러한 부분을 Spring Rest Docs 로 해결합니다.

본 포스팅은 아래 환경을 기준으로 작성되었습니다.
개발환경 : SpringBoot 3.1.5 / Java 17

1. Spring Rest Docs 설정하기

spring rest docs 를 사용하기 위해선 build.gradle 에 아래와 같은 설정이 필요합니다.

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.5'
    id 'io.spring.dependency-management' version '1.1.3'

    // 1) asciidoctor 플러그인 추가
    id 'org.asciidoctor.jvm.convert' version '3.3.2'
}

// == 중략 == //

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }

    // 2) asciidoctor 의존성 설정 선언
    asciidoctorExt
}

// == 중략 == //

dependencies {
    // == 중략 == //

    // 3) Spring Rest Docs 관련 test implement 추가 및 asciidoctor 의존성 추가
    asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor:3.0.0'
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc:3.0.0'
}

// 4) Spring Rest Docs 관련 gradle 명령어 추가
// Start
ext {
    snippetsDir = file('build/generated-snippets')
}

test {
    outputs.dir snippetsDir
}

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

bootJar {
    dependsOn asciidoctor
    copy {
        from asciidoctor.outputDir
        into 'src/main/resources/static/docs'
    }
}
// End

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

 

 

위 설정이 각각 뭘 의미하는지는 아래 공식문서를 참고하면 됩니다.

본 포스팅은 자세한 설명보단 사용방법 위주로 설명합니다.

https://docs.spring.io/spring-restdocs/docs/current/reference/htmlsingle/#getting-started

 

Spring REST Docs

Document RESTful services by combining hand-written documentation with auto-generated snippets produced with Spring MVC Test or WebTestClient.

docs.spring.io

 

위와 같이 설정을 완료한 후 gradle 을 reload 했다면, IDE 에서 아래와 같이 bootjar 명령어가 존재하는지 확인해야 합니다.

 

 

2. Mvc Test 에 Rest Docs 적용해보기

이제 실제로 테스트 코드에 Rest Docs 설정을 추가해보도록 하겠습니다.

 

먼저, 테스트 클래스에 annotation 을 추가합니다.

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@ExtendWith(RestDocumentationExtension.class)
public class UserControllerTest {

    @Autowired
    MockMvc mockMvc;
    
    @MockBean
    UserService userService;
    
    @Autowired
    ObjectMapper objectMapper;
}

RestDocs 사용을 위해 @AutoConfigureRestDocs @ExtendWith(RestDocumentationExtension.class) 를 추가합니다. 

그 다음, 테스트 케이스 작성 시에 필요한 MockMvc 와 ObjectMapper 를 주입받고, Mocking 하고자 하는 Service 가 있다면 MockBean 으로 선언해줍니다.

 

그 다음, 테스트 케이스에는 아래와 같이 API 테스트를 위한 코드를 작성합니다.

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@ExtendWith(RestDocumentationExtension.class)
public class UserControllerTest {

    // == 중략 == //

    @Test
    @DisplayName("사용자가 자신의 회원정보를 조회한다.")
    void find_user() throws Exception {
        // given
        when(userService.findUser(any())).thenReturn(
                new UserDto(
                        100L, "사용자100",
                        "user100@test.com", "010-1234-2345",
                        LocalDateTime.of(2022, 12, 22, 11, 34, 19)
                )
        );

        // when & then
        mockMvc.perform(RestDocumentationRequestBuilders.get("/users")
                        .header("X-GATEWAY-AUTH-HEADER", 100L)
                        .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("id", is(100)))
                .andExpect(jsonPath("signUpDate", is("2022-12-22")))
                .andDo(document("find_user",
                        responseFields(
                                fieldWithPath("id").description("사용자 고유 ID"),
                                fieldWithPath("name").description("사용자 이름"),
                                fieldWithPath("email").description("사용자 Email"),
                                fieldWithPath("telNo").description("사용자 연락처"),
                                fieldWithPath("signUpDate").description("회원가입 일자")
                        )
                ))
        ;
    }
}

위 테스트 코드에서 주목해야 하는 부분은 마지막 andDo()의 내용입니다.

 

먼저, document() 메서드의 "find_user"API 테스트를 통해 Spring Rest Docs 가 만드는 adoc 파일을 포함할 폴더의 이름 입니다.

Spring Rest Docs 는 설정한 후에 테스트를 실행하면, 테스트 성공 시 특정 패키지에 adoc 파일을 생성합니다. 개발자는 해당 adoc 파일들을 적절하게 조합하여 원하는 형태의 API 문서를 html 로 생성할 수 있게되는 원리입니다.
(해당 내용은 좀 더 뒤에서 결과를 보면서 추가로 확인해보겠습니다.)

 

그리고  responseFields() 메서드를 통해, 응답 받고자 하는 body의 정보들이 각각 어떤 값이고, 어떤 의미인지를 명시해줍니다.

위 코드를 예로 들면, 응답으로 넘어오는 항목들은 아래와 같다는 의미입니다.

 

requestFields() 의 경우, 여러 request 형태에 따라 다른 메서드들을 제공하는데, 대표적으로 3가지만 다뤄보겠습니다.

 

1. RequestBody 

request body 를 spring rest docs 로 처리하는 예시는 다음과 같습니다.

public class UserControllerTest {

    // == 중략 == //

    @Test
    @DisplayName("사용자가 회원가입에 성공한다.")
    void sign_up() throws Exception {
        // given
        SignUpRequest signUpRequest = new SignUpRequest(
                "신규 가입자", "new@test.com", "new1!", "new1!", "010-1234-1234"
        );
        String content = objectMapper.writeValueAsString(signUpRequest);

        when(userService.signUp(any())).thenReturn(
                UserDto.builder()
                        .id(1L)
                        .name("신규 가입자")
                        .email("new@test.com")
                        .telNo("010-1234-1234")
                        .signUpDate(LocalDateTime.of(2023, 8, 25, 12, 1, 2))
                        .build()
        );

        // when & then
        mockMvc.perform(post("/sign-up")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(content))
                .andDo(print())
                .andExpect(status().isCreated())
                .andExpect(jsonPath("name", is("신규 가입자")))
                .andDo(document("signUp",
                        requestFields(
                                fieldWithPath("name").description("이름"),
                                fieldWithPath("email").description("가입 이메일"),
                                fieldWithPath("password").description("비밀번호"),
                                fieldWithPath("confirmPassword").description("비밀번호 확인"),
                                fieldWithPath("telNo").description("연락처")
                        ),
                        responseFields(
                                fieldWithPath("id").description("사용자 고유 ID"),
                                fieldWithPath("name").description("이름"),
                                fieldWithPath("email").description("가입 이메일"),
                                fieldWithPath("telNo").description("가입 연락처"),
                                fieldWithPath("signUpDate").description("가입일자")
                        )
                ));
    }
}

 

일반적인 회원가입 API 의 성공 사례를 테스트하는 코드입니다. request body 에 포함되는 정보를 Spring Rest Docs 에 표시하기 위해 requestFields() 메서드의 파라미터fieldWithPath().description() 메서드를 사용했습니다.

fieldWithPath() 메서드의 파라미터로는 Request Body 에 포함되는 필드명을 명시하고, description() 메서드의 파라미터로는 해당 field 가 어떤 필드인지에 대한 설명을 넣습니다.

 

위 코드를 예시로 들면, 회원가입을 위해 어떤 필드가 필요로 하고, 각 필드에 대한 설명을 아래와 같이 나타내게 됩니다.

 

 

2. RequestParam

RequestParam 을 spring rest docs 로 처리하는 예시는 다음과 같습니다.

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@ExtendWith(RestDocumentationExtension.class)
class UserMicroServiceControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    UserQueryDAO userQueryDAO;

    @Test
    @DisplayName("특정 회원등급 이상의 사용자를 조회한다.")
    void get_user_ids_above_grade() throws Exception {
        // given
        when(userQueryDAO.getUserIdsAboveGrade(any())).thenReturn(
                Arrays.asList(1L, 2L, 3L)
        );

        // when & then
        mockMvc.perform(get("/users/above-grade?targetGrade=REGULAR")
                        .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk())
                .andDo(document("get_user_above_grade",
                                queryParameters(
                                        parameterWithName("targetGrade").description("회원 등급")
                                )
                        )
                )
        ;
    }
}

RequestParam 의 경우, RequestDocumentation.queryParameter() 를 통해 어떤 쿼리 파라미터가 넘어가는지 명시할 수 있습니다. parameterWithName() 으로 파라미터의 key 값을 지정해주고, description() 을 통해 해당 파라미터가 무엇을 의미하는지 명시합니다.

 

3. PathVariable

PathVariable 을 spring rest docs 로 처리하는 예시는 다음과 같습니다.

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@ExtendWith(RestDocumentationExtension.class)
class UserControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    UserService userService;

    @Test
    @DisplayName("로그인 사용자가 자신의 등급정보를 조회한다.")
    void find_user_grade_info() throws Exception {
        // given
        when(userService.getUserGradeInfo(any())).thenReturn(
                new UserGradeInfoDto(
                        1000L, "사용자1000",
                        LocalDateTime.of(2022, 12, 22, 0, 0, 0),
                        UserGrade.REGULAR, UserGrade.VIP,
                        50, 10000
                )
        );

        // when & then
        mockMvc.perform(RestDocumentationRequestBuilders.get("/users/{id}/grade-info", 1000L)
                        .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("userId", is(1000)))
                .andExpect(jsonPath("userName", is("사용자1000")))
                .andExpect(jsonPath("signUpDate", is("2022-12-22")))
                .andExpect(jsonPath("currentUserGrade", is("단골회원")))
                .andExpect(jsonPath("nextUserGrade", is("VIP")))
                .andDo(document("find_user_grade_info",
                        pathParameters(
                                parameterWithName("id").description("사용자 고유 ID")
                        ),
                        responseFields(
                                fieldWithPath("userId").description("사용자 고유 ID"),
                                fieldWithPath("userName").description("사용자 이름"),
                                fieldWithPath("signUpDate").description("가입일자"),
                                fieldWithPath("currentUserGrade").description("현재 회원등급"),
                                fieldWithPath("gradeDiscountRate").description("등급 할인율"),
                                fieldWithPath("nextUserGrade").description("다음 회원등급"),
                                fieldWithPath("remainedOrderCountForNextGrade").description("다음 회원등급 승급까지 남은 주문 수"),
                                fieldWithPath("remainedAmountsForNextGrade").description("다음 회원등급 승급까지 남은 주문 금액")
                        )
                ))
        ;
    }
}

 

PathVariable 같은 경우, RequestDocumentation.pathParameters() 을 사용해 path 변수를 명시합니다.

parameterWithName() 에는 path 에 사용되는 변수를 명시하고, description() 은 해당 path 에 어떤 값이 들어가는지 명시합니다.

 

 

3. API 문서 만들기

spring rest docs 를 포함시켜 테스트 코드를 완성하면 아래와 같은 절차를 통해 API 문서를 생성할 수 있습니다.

  1. 테스트 코드를 실행한다.
  2. build.gradle 에서 설정한 경로 하위에 각 테스트 케이스 별로 설정한 이름을 가진 폴더 밑에 여러 adoc 파일이 생성된다.
  3. 해당 adoc 파일들을 조합하여 실제 API 문서로 변환될 adoc 파일을 만든다.
  4. build.gradle 에서 설정한 bootjar 명령어를 실행한다.
  5. resources 폴더 하위에 3번 과정에서 만든 adoc 파일과 동일한 이름을 가지는 html 파일이 생성된다.
  6. 서비스를 실행 후, 5번에서 만들어진 html 파일을 브라우저로 확인한다.

 

1. 테스트 코드를 실행한다.

아래 작성된 테스트 코드를 API 문서로 만들기 위해 테스트를 실행합니다.

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@ExtendWith(RestDocumentationExtension.class)
public class UserControllerTest {
    @Autowired
    MockMvc mockMvc;

    @MockBean
    UserService userService;

    @Autowired
    ObjectMapper objectMapper;

    @Test
    @DisplayName("사용자가 회원가입에 성공한다.")
    void sign_up() throws Exception {
        // given
        SignUpRequest signUpRequest = new SignUpRequest(
                "신규 가입자", "new@test.com", "new1!", "new1!", "010-1234-1234"
        );
        String content = objectMapper.writeValueAsString(signUpRequest);

        when(userService.signUp(any())).thenReturn(
                UserDto.builder()
                        .id(1L)
                        .name("신규 가입자")
                        .email("new@test.com")
                        .telNo("010-1234-1234")
                        .signUpDate(LocalDateTime.of(2023, 8, 25, 12, 1, 2))
                        .build()
        );

        // when & then
        mockMvc.perform(post("/sign-up")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(content))
                .andDo(print())
                .andExpect(status().isCreated())
                .andExpect(jsonPath("name", is("신규 가입자")))
                .andDo(document("signUp",
                        requestFields(
                                fieldWithPath("name").description("이름"),
                                fieldWithPath("email").description("가입 이메일"),
                                fieldWithPath("password").description("비밀번호"),
                                fieldWithPath("confirmPassword").description("비밀번호 확인"),
                                fieldWithPath("telNo").description("연락처")
                        ),
                        responseFields(
                                fieldWithPath("id").description("사용자 고유 ID"),
                                fieldWithPath("name").description("이름"),
                                fieldWithPath("email").description("가입 이메일"),
                                fieldWithPath("telNo").description("가입 연락처"),
                                fieldWithPath("signUpDate").description("가입일자")
                        )
                ));
    }

    @Test
    @DisplayName("로그인 사용자가 자신의 등급정보를 조회한다.")
    void find_user_grade_info() throws Exception {
        // given
        when(userService.getUserGradeInfo(any())).thenReturn(
                new UserGradeInfoDto(
                        1000L, "사용자1000",
                        LocalDateTime.of(2022, 12, 22, 0, 0, 0),
                        UserGrade.REGULAR, UserGrade.VIP,
                        50, 10000
                )
        );

        // when & then
        mockMvc.perform(get("/users/{id}/grade-info", 1000L)
                        .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("userId", is(1000)))
                .andExpect(jsonPath("userName", is("사용자1000")))
                .andExpect(jsonPath("signUpDate", is("2022-12-22")))
                .andExpect(jsonPath("currentUserGrade", is("단골회원")))
                .andExpect(jsonPath("nextUserGrade", is("VIP")))
                .andDo(document("find_user_grade_info",
                        pathParameters(
                                parameterWithName("id").description("사용자 고유 ID")
                        ),
                        responseFields(
                                fieldWithPath("userId").description("사용자 고유 ID"),
                                fieldWithPath("userName").description("사용자 이름"),
                                fieldWithPath("signUpDate").description("가입일자"),
                                fieldWithPath("currentUserGrade").description("현재 회원등급"),
                                fieldWithPath("gradeDiscountRate").description("등급 할인율"),
                                fieldWithPath("nextUserGrade").description("다음 회원등급"),
                                fieldWithPath("remainedOrderCountForNextGrade").description("다음 회원등급 승급까지 남은 주문 수"),
                                fieldWithPath("remainedAmountsForNextGrade").description("다음 회원등급 승급까지 남은 주문 금액")
                        )
                ))
        ;
    }
    
    @Test
    @DisplayName("특정 회원등급 이상의 사용자를 조회한다.")
    void get_user_ids_above_grade() throws Exception {
        // given
        when(userService.getUserIdsAboveGrade(any())).thenReturn(
                Arrays.asList(1L, 2L, 3L)
        );

        // when & then
        mockMvc.perform(get("/users/above-grade?targetGrade=REGULAR")
                        .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk())
                .andDo(document("get_user_above_grade",
                                queryParameters(
                                        parameterWithName("targetGrade").description("회원 등급")
                                )
                        )
                )
        ;
    }

}

 

 

2. adoc 파일 확인

테스트를 실행 후, 모든 테스트가 성공했다면 build.gradle 에 설정해놓은 snippetsDir 경로에 adoc 파일 여부를 확인합니다.

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

 

 

3. API 문서로 변환될 adoc 파일 생성

2번에서 생성된 adoc 파일들을 활용해 실제로 API 문서로 만들 adoc파일을 생성합니다.

생성 경로src/docs/asciidoc 폴더 하위 입니다.

 

그 후, 해당 adoc 파일 내에 2번에서 생성된 adoc 파일을 Include 하여 API 문서에 담을 내용을 지정합니다.

```user.adoc

= User Micro Service API of Shopping Mall
:toc:

== 회원가입
=== 요청
include::{snippets}/signUp/http-request.adoc[]
include::{snippets}/signUp/request-fields.adoc[]
=== 응답
include::{snippets}/signUp/http-response.adoc[]
include::{snippets}/signUp/response-fields.adoc[]

== 회원등급 정보 조회
=== 요청
include::{snippets}/find_user_grade_info/http-request.adoc[]
include::{snippets}/find_user_grade_info/path-parameters.adoc[]
=== 응답
include::{snippets}/find_user_grade_info/http-response.adoc[]
include::{snippets}/find_user_grade_info/response-fields.adoc[]

== 특정 회원등급 이상의 사용자 조회
=== 요청
include::{snippets}/get_user_above_grade/http-request.adoc[]
include::{snippets}/get_user_above_grade/query-parameters.adoc[]

=== 응답
include::{snippets}/get_user_above_grade/http-response.adoc[]

 

4. bootjar 명령 실행

3번에서 생성한 adoc 파일을 html 로 변환하기 위해 build.gradle 에서 명시한 bootjar 명령어를 실행해야 합니다.

bootJar {
    dependsOn asciidoctor
    copy {
        from asciidoctor.outputDir
        into 'src/main/resources/static/docs'
    }
}

해당 명령어를 쉽게 말하자면, 3번에서 생성한 adoc 파일을 html로 변환하여 src/main/resources/static/docs 라는 경로에 생성한다는 의미입니다. 

html 생성을 위해 resources/static 하위에 docs 폴더를 생성합니다.

 

그 후, build.gradle 파일에 bootjar 좌측에 버튼을 클릭하여 gradle 명령을 실행합니다.

 

성공 시, 아래 콘솔창에 아래와 같은 로그가 발생합니다.

6:45:09 PM: Executing 'bootJar'...

Starting Gradle Daemon...
Gradle Daemon started in 707 ms
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses

> Task :test
// 기타 로그는 길어서 생략

> Task :asciidoctor
2023-12-03T18:45:27.028+09:00 [main] WARN FilenoUtil : Native subprocess control requires open access to the JDK IO subsystem
Pass '--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED' to enable.

> Task :resolveMainClassName UP-TO-DATE
> Task :bootJar UP-TO-DATE

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.2.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 18s
7 actionable tasks: 3 executed, 4 up-to-date
6:45:28 PM: Execution finished 'bootJar'.

 

5. html 파일 생성 확인

4번에서 생성한 resources/static/docs 파일 하위에 user.html 파일의 생성 여부를 확인합니다.

간혹, 한 번에 생성이 되지 않는 경우가 있는데 이 때는 시간을 좀 두었다가 다시 실행하면 정상적으로 생성됩니다.

 

 

6. API 문서 확인

이제 서비스를 실행하여 해당 파일을 브라우저에서 호출해보면, 아래와 같이 API 문서가 조회되는 것을 확인할 수 있습니다.

 

 

각 기능별로 확인해보면 아래와 같이 조회됩니다.

 

 

4. 마치며

spring rest docs 를 통해 API 문서를 만들어봤습니다.

spring rest docs 은 아래와 같은 특징을 가집니다.

  1. 테스트 코드를 기반으로 문서를 생성하기 때문에, 운영코드에 영향을 주지 않는다.
  2. 기능 및 스펙 변경에 비교적 잘 대응된다.

 

spring rest docs 를 사용하는 가장 큰 이유라면 아마 1번의 운영코드에 영향을 주지 않는다. 는 점일 것 입니다.

대표적으로 비교되는 swagger 의 경우, 실제 운영코드에 swagger 관련 설정이 영향을 주게 되어 실제로 서비스에 문제가 발생하면, 점검해야 할 포인트가 될 수 있는 점에 반해, 테스트 코드에서만 처리되는 spring rest docs 은 꽤 매력적으로 다가온다고 생각합니다.

 

하지만, 어디까지나 테스트 코드를 기반으로 API 문서를 생성하기 때문에, 운영 코드만 수정해놓고 테스트 코드에 반영을 해놓지 않는다면 API 문서가 실제 운영 환경과 다른 정보를 표현하기 때문에 테스트 코드를 작성하는데 개발자가 더 신경을 기울어야 한다는 점은 고려해야할 점이 될 수 있을 것 같습니다.

 

오히려, 여러 클라이언트에서 사용하는 서비스에서 API 문서를 만든다면, 실제 운영 환경과의 스팩을 그때 그때 바로 API 문서에 반영할 수 있는 swagger 가 협업 시에는 더 유리할 수 도 있을 것 같다는 생각이 듭니다.

 

이러한 특징들을 비교하여 각자에게 최선의 선택을 하는 것이 좋겠습니다.


참고 및 출처.

https://docs.spring.io/spring-restdocs/docs/current/reference/htmlsingle/#introduction

 

Spring REST Docs

Document RESTful services by combining hand-written documentation with auto-generated snippets produced with Spring MVC Test or WebTestClient.

docs.spring.io

https://www.inflearn.com/course/%ED%98%B8%EB%8F%8C%EB%A7%A8-%EC%9A%94%EC%A0%88%EB%B3%B5%ED%86%B5-%EA%B0%9C%EB%B0%9C%EC%87%BC/dashboard

 

호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS) - 인프런 | 강의

단순히 애플리케이션 하나를 만드는데 끝나지 않습니다. Spring Boot를 활용한 백엔드부터 Vue.js 모던 프론트엔드 스택을 연동한 서비스 완성 A-Z를 보여드립니다., 🐯이제는 실전이다!🐯이 강의를

www.inflearn.com

 

댓글