🍃프로그래머스 백엔드 데브코스 4기 교육과정을 듣고 정리한 글입니다.🍃
Spring REST Docs
- 스프링 프레임워크 기반에서 RESTful API 문서를 생성하는 도구
- 테스트 기반으로 API를 문서화하기 위해 JUnit, Mockito와 함께 사용
- 작성한 API 코드와 문서가 항상 일치하도록 하며, API에 대한 검증 및 최신 정보 유지
- API 코드와 문서가 일치하지 않으면 테스트 실패
사용 방법
- 의존성 추가: spring-restdocs-mockmvc 의존성을 gradle에 추가
- 테스트 작성: JUnit과 MockMvc를 사용하여 API 엔드포인트를 테스트하고 문서화
- 문서 생성: 테스트를 실행하면, build/generated-snippets 디렉토리 아래에 해당 API에 대한 문서가 생성
- 문서 포맷 변환:
- Rest-Docs는 src/docs/asciidoc/index.adoc에 직접 추가해서 문서를 작성한다.
- 작성된 문서를 gradle 빌드 스크립트를 통해 asciidoctor 플러그인을 사용하여 HTML 형식으로 변환한다.
- 위 그림의 index.adoc에서 작성된 :snippets: build/generated-snippets는 아래 gradle 파일의 해당 설정 경로가 된다.
- 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 {
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();
mockMvc.perform(post("/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(orderDto)))
.andExpect(status().isOk())
.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 테스트 코드를 작성하지 않아도 문서화하기 때문에 검증이 안됨