[Spring] Spring에서 API 문서 자동화하기 (1) - Swagger
API 문서화 백엔드와 프론트엔드 개발자 사이의 원활한 협업을 위해서는 REST API 명세에 대한 문서화가 잘 되어있어야 한다. 현재 개인 프로젝트 (AImage)에서도 REST API를 구현하였고, 문서화를 진행
davidy87.tistory.com
[Spring] Spring에서 API 문서 자동화하기 (2) - Spring REST Docs
[Spring] Spring에서 API 문서 자동화하기 (1) - Swagger API 문서화 백엔드와 프론트엔드 개발자 사이의 원활한 협업을 위해서는 REST API 명세에 대한 문서화가 잘 되어있어야 한다. 현재 개인 프로젝트 (AI
davidy87.tistory.com
이전 글에서 Swagger와 Spring REST Docs를 모두 사용해 보았다. 이제 이 기능들을 비교해보고 최종적으로 어떤 도구를 써야 할지 결정해보자.
Swagger vs. Spring REST Docs
두 기술을 모두 사용해보고 내가 느꼈던 각 기술의 장단점이라고 생각되는 부분들을 다시 한 번 나열해 보았다.
Swagger
장점
- 어노테이션으로 문서를 생성하는 방법은 매우 간단하고 직관적이어서 좋았다.
- API를 테스트 해볼 수 있는 화면을 제공해서 더 편하게 사용할 수 있었다.
단점
- Controller나 DTO 객체 같은 프로덕션 코드에 문서화 코드를 붙이는 것이 좋은 방법인지는 의문이 든다.
- 가장 큰 문제점이라고 생각되는 것은 코드의 가독성이 떨어진다는 것이다.
- 만약, API에 변화가 생긴다면? -> 문서화 코드를 전부 다시 수정해야 하는 불편함이 생길 것이다.
Spring REST Docs
장점
- 프로덕션 코드를 건드릴 필요 없이, 테스트를 작성하는 것만으로 문서화를 할 수 있다.
- 테스트 코드를 작성하고 통과할 경우에만 문서로 작성되기 때문에 Swagger와는 다르게 API 스펙과 항상 일치하는 문서를 작성할 수 있다.
단점
- 설정하는 과정이 Swagger 보다 훨씬 복잡했다 (제일 큰 단점이라고 느껴졌다).
- 프로덕션 코드에는 영향을 주지 않지만, 문서화를 위한 테스트 코드가 장황해지고, 코드 반복으로 인해 생산성이 떨어질 수 있다.
선택
현재 진행 중인 프로젝트에는 Spring REST Docs를 사용해 API 문서화를 하기로 결정했다.
문서화 단계부터 API를 테스트 할 수 있다는 점, 그리고 테스트를 통과할 경우에만 문서로 작성되기 때문에 API 스펙과 항상 일치하는 문서를 작성할 수 있다는 점이 Spring REST Docs를 사용하게 된 결정적인 이유이다.
비록 설정은 까다롭지만, 개발하는 과정에서 테스트는 아주 중요한 부분이기 때문에 이를 강제화 할 수 있다는 부분에서 사용하기 좋다는 생각이 들었다.
적용
이렇게 모든 API를 문서에 명세하였다. 하지만 위에 나온 API 문서는 부족한 부분들이 많이 있다. 예를 들어, request body에 들어가는 필드의 정보가 없거나 응답 값은 무엇을 의미하는지 등 상세한 정보는 나와있지 않다. 이런 부분들은 기능을 추가하면서 수정해 보도록 하겠다.
기능 추가
prettyPrint()
위에는 이미 이쁘게 출력이 되어 있지만, 처음 문서화를 하게 되면 아래처럼 request body와 response body가 전부 한 줄로 작성되는 것을 볼 수 있다.
request body와 response body를 이쁘게 출력하기 위해서는 아래와 같이 기존 테스트 코드에
- preprocessRequest(prettyPrint())
- preprocessResponse(prettyPrint()))
이 둘을 붙여주면 된다.
// When & Then
mockMvc.perform(post("/api/users")
.content(content)
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andDo(document("signup",
preprocessRequest(prettyPrint()), // 추가
preprocessResponse(prettyPrint())) // 추가
)
.andExpect(status().isOk())
.andExpect(jsonPath("id").value(testUser.getId()))
.andExpect(jsonPath("username").value(testUser.getUsername()))
.andExpect(jsonPath("email").value(testUser.getEmail()));
requestFields() & responseFields()
Request와 response에 들어가는 필드의 정보를 명세하고 싶다면 아래와 같이 requestFields() 와 responseFields() 를 사용하면 된다.
// When & Then
mockMvc.perform(post("/api/users")
.content(content)
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andDo(document("signup",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
requestFields(
fieldWithPath("username").type(JsonFieldType.STRING).description("닉네임"),
fieldWithPath("email").type(JsonFieldType.STRING).description("이메일"),
fieldWithPath("password").type(JsonFieldType.STRING).description("비밀번호"),
fieldWithPath("confirmPassword").type(JsonFieldType.STRING).description("비밀번호 확인")
), // 추가
responseFields(
fieldWithPath("id").type(JsonFieldType.NUMBER).description("사용자 id"),
fieldWithPath("username").type(JsonFieldType.STRING).description("닉네임"),
fieldWithPath("email").type(JsonFieldType.STRING).description("이메일")
)) // 추가
)
.andExpect(status().isOk())
.andExpect(jsonPath("id").value(testUser.getId()))
.andExpect(jsonPath("username").value(testUser.getUsername()))
.andExpect(jsonPath("email").value(testUser.getEmail()));
각 필드에 맞게
- fieldWithPath("필드명").type(필드 타입).description("필드 설명")
을 설정하고 테스트를 실행하여 통과하면 아래와 같이 build/generated-snippets 에 request-fields.adoc 과 response-fields.adoc 이 추가 된 것을 확인할 수 있다.
이제 기존 문서에 새로운 snippet을 추가하면 아래와 같이 필드 정보가 담긴 테이블이 생긴 것을 확인할 수 있다.
위의 테이블을 커스터마이징하여 기존 항목의 명칭을 바꾸거나, 원하는 column을 추가할 수도 있다.
src/test/resources/org/springframework/restdocs/templates 경로에 request-fields.snippet 와 response-fields.snippet 을 생성하고 아래와 같이 작성해보자.
|===
|필드명|타입|설명
{{#fields}}
|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/fields}}
|===
그 후, 다시 테스트를 실행하고 완성된 문서를 확인해보면 아래와 같이 column header들이 변경된 것을 볼 수 있다.
만약 추가하고 싶은 column이 있다면 간단하게 .snippet 파일에 표시하고자 하는 column을 추가하면 된다.
|===
|필드명|타입|설명|추가|추가
{{#fields}}
|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
|{{#tableCellContent}}`+{{추가}}+`{{/tableCellContent}} // 추가
|{{#tableCellContent}}`+{{추가}}+`{{/tableCellContent}} // 추가
{{/fields}}
|===
마무리
Spring REST Docs와 asciidoc은 설정할 부분들도 많고 사용하기 까다롭지만, 자주 사용하고 익숙해지면 매우 유용한 기술이다.
처음 사용해 봤던 기술들이라 아직 부족한 부분이 많지만, API 문서화를 도입하는 방법을 알 수 있는 좋은 경험이 되었다고 생각한다.
References
Spring Rest Docs 적용 | 우아한형제들 기술블로그
{{item.name}} 안녕하세요? 우아한형제들에서 정산시스템을 개발하고 있는 이호진입니다. 지금부터 정산시스템 API 문서를 wiki 에서 Spring Rest Docs 로 전환한 이야기를 해보려고 합니다. 1. 전환하는
techblog.woowahan.com
'Spring' 카테고리의 다른 글
[Spring / S3] S3Mock을 사용하여 S3 테스트하기 (0) | 2023.12.24 |
---|---|
[Spring] Spring Data JPA의 Pagination (0) | 2023.12.24 |
[Spring] Spring에서 API 문서 자동화하기 (2) - Spring REST Docs (0) | 2023.12.24 |
[Spring] Spring에서 API 문서 자동화하기 (1) - Swagger (0) | 2023.12.24 |
[Spring] MockMVC를 이용한 Test (0) | 2023.12.24 |