[7/28 TIL] API문서화

yumyeonghan·2023년 7월 28일
0
post-custom-banner

🍃프로그래머스 백엔드 데브코스 4기 교육과정을 듣고 정리한 글입니다.🍃

Spring REST Docs

  • 스프링 프레임워크 기반에서 RESTful API 문서를 생성하는 도구
  • 테스트 기반으로 API를 문서화하기 위해 JUnit, Mockito와 함께 사용
  • 작성한 API 코드와 문서가 항상 일치하도록 하며, API에 대한 검증 및 최신 정보 유지
    • API 코드와 문서가 일치하지 않으면 테스트 실패

사용 방법

  1. 의존성 추가: spring-restdocs-mockmvc 의존성을 gradle에 추가
  2. 테스트 작성: JUnit과 MockMvc를 사용하여 API 엔드포인트를 테스트하고 문서화
  3. 문서 생성: 테스트를 실행하면, build/generated-snippets 디렉토리 아래에 해당 API에 대한 문서가 생성
  4. 문서 포맷 변환:
    • Rest-Docs는 src/docs/asciidoc/index.adoc에 직접 추가해서 문서를 작성한다.
    • 작성된 문서를 gradle 빌드 스크립트를 통해 asciidoctor 플러그인을 사용하여 HTML 형식으로 변환한다.
    • 위 그림의 index.adoc에서 작성된 :snippets: build/generated-snippets는 아래 gradle 파일의 해당 설정 경로가 된다.
  1. HTML 문서 확인: build/docs/asciidoc/index.html을 실행해서 API 문서 확인
    • 해당 파일은 asciidoctor 플러그인을 실행하면 자동으로 생긴다.

예시 코드

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@AutoConfigureRestDocs
@AutoConfigureMockMvc
@SpringBootTest
class OrderControllerTest {

    @Autowired
    private MockMvc mockMvc;

 	@Autowired
    ObjectMapper objectMapper;

	@Test
    void saveCallTest() throws Exception {
        // Given
        OrderDto orderDto = OrderDto.builder()
                .uuid(UUID.randomUUID().toString())
                .memo("문앞 보관 해주세요.")
                .orderDatetime(LocalDateTime.now())
                .orderStatus(OrderStatus.OPENED)
                .memberDto(
                        MemberDto.builder()
                                .name("강홍구")
                                .nickName("guppy.hong")
                                .address("서울시 동작구만 움직이면 쏜다.")
                                .age(33)
                                .description("---")
                                .build()
                )
                .orderItemDtos(List.of(
                        OrderItemDto.builder()
                                .price(1000)
                                .quantity(100)
                                .itemDtos(List.of(
                                        ItemDto.builder()
                                                .type(ItemType.FOOD)
                                                .chef("백종원")
                                                .price(1000)
                                                .build()
                                ))
                                .build()
                ))
                .build();

        // When // Then
        mockMvc.perform(post("/orders")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(orderDto)))
                .andExpect(status().isOk()) // 200
                .andDo(print())
                .andDo(document("order-save",
                        requestFields(
                                fieldWithPath("uuid").type(JsonFieldType.STRING).description("UUID"),
                            	fieldWithPath("memberDto").type(JsonFieldType.OBJECT).description("memberDto"),
                                fieldWithPath("memberDto.id").type(JsonFieldType.NULL).description("memberDto.id"),
                             	fieldWithPath("memberDto.age").type(JsonFieldType.NUMBER).description("memberDto.age"),
                            	fieldWithPath("orderItemDtos[]").type(JsonFieldType.ARRAY).description("memberDto.desc"),
                                fieldWithPath("orderItemDtos[].id").type(JsonFieldType.NULL).description("orderItemDtos.id"),
                                fieldWithPath("orderItemDtos[].price").type(JsonFieldType.NUMBER).description("orderItemDtos.price"),
                             	fieldWithPath("orderItemDtos[].itemDtos[]").type(JsonFieldType.ARRAY).description("orderItemDtos.itemDtos"),
                                fieldWithPath("orderItemDtos[].itemDtos[].price").type(JsonFieldType.NUMBER).description("orderItemDtos.itemDtos"),
                                fieldWithPath("orderItemDtos[].itemDtos[].chef").type(JsonFieldType.STRING).description("orderItemDtos.itemDtos")
                        ),
                        responseFields(
                                fieldWithPath("statusCode").type(JsonFieldType.NUMBER).description("상태코드"),
                                fieldWithPath("data").type(JsonFieldType.STRING).description("데이터"),
                                fieldWithPath("serverDatetime").type(JsonFieldType.STRING).description("응답시간")
                        )
                ));
	}
}
  • fieldWithPath("이름"): 문서에 표시할 필드의 실제 이름을 지정
  • type(JsonFieldType.타입): 필드의 데이터 유형 정의
  • description("설명"): 해당 필드에 대한 설명을 추가

Swagger

  • 프로덕트 코드(컨트롤러 레이어 코드)에 문서화를 하기위한 코드를 추가해서 문서화함
  • 프로덕트 코드와 문서화 코드가 섞이면서 유지보에 좋지 않음
  • API 테스트 코드를 작성하지 않아도 문서화하기 때문에 검증이 안됨
profile
웹 개발에 관심 있습니다.
post-custom-banner

0개의 댓글