-
Kotlin에서 Rest Docs 문서화 코드 개선하기Backend/Spring 2023. 5. 29. 18:08
기존에는 테스트 결과를 문서화 하기 위해, Rest Docs에서 제공하는 FieldDescriptor를 다음과 같이 생성하였다.
그러다 보니 Field가 많으면 엄청난 양의 코드를 작성해야 하고 이 부분의 가독성이 떨어져 개선하고자 한다.
❌ 문제의 코드
restDocMockMvc.post("/test") { jsonContent(request) }.andExpect { status { isOk() } }.andDo { handle( MockMvcRestDocumentation.document( "{class-name}/test-success", requestFields( fieldWithPath("name").description("이름").attributes(field(EXAMPLE, request.name)) ), responseFields( fieldWithPath("hashValue").description("name의 hashCode 값"), fieldWithPath("originalName").description("trim을 적용한 name 값") ) ) ) }
🤦♂️ 해당 코드는 반복적이고, 코드가 잘 읽히지 않는다.
✔ 개선 코드
restDocMockMvc.post(targetUri) { jsonContent(request) }.andExpect { status { isOk() } }.andDo { createDocument( "test-success", requestBody("name" type STRING description "이름" example request.name), responseBody( "hashValue" type NUMBER description "name의 hashCode 값", "originalName" type STRING description "trim을 적용한 name 값" ) ) }
방법
Kotlin에서는 infix function과 확장 함수를 제공한다. 해당 기능을 사용해서 코드를 개선할 생각이다.
💬 Infix Function
두 개의 객체 중간에 들어가게 되는 함수를 의미한다.
👀 예시
val monday: Pair<String, String> = "Monday" to "월요일"
◼ 위와 같이 to라는 infix function을 사용해서 Pair 객체를 생성할 수 있다.
💬 확장 함수
기존에 정의된 클래스에 함수를 추가하는 기능이다.
👀 예시
fun MutableList<Int>.swap(a: Int, b: Int){ val tmp = this[a] this[a] = this[b] this[b] = tmp }
◼ 위와 같이 MutableList 클래스에 swap이라는 함수 기능을 추가할 수 있다.
개선 과정
✔ FieldDscriptor를 가지고 있는 class를 만든다.
class RestDocsField( val descriptor: FieldDescriptor ) { infix fun isOptional(value: Boolean): RestDocsField { if (value) descriptor.optional() return this } infix fun description(value: String): RestDocsField { descriptor.description(value) return this } infix fun example(value: String): RestDocsField { descriptor.attributes(field(EXAMPLE, value)) return this } }
◼ RestDocsField 클래스에서 FieldDescriptor를 받는다.
◼ infix 함수를 통해 FieldDescriptor에 해당 값을 추가한다.
✔ String 타입에 확장 함수 추가
infix fun String.type( type: JsonFieldType ): RestDocsField = createField(this, type)
private fun createField( path: String, type: JsonFieldType ): RestDocsField = RestDocsField(PayloadDocumentation.fieldWithPath(path).type(type))
◼ String 클래스에 type이라는 infix 함수를 추가한다.
◼ JsonFieldType은 Rest Docs에서 제공하는 class로 필드의 type 지정할 수 있다.
"code" type JsonFieldType.NUMBER
◼ 위와 같이 코드를 작성하면, type 이라는 infix 함수에 "code" 가 this로, JsonFieldType.NUMBER가 type 인자로 들어간다.
◼ createField 함수를 통해 path에 code를 type에는 JsonFieldType.NUMBER가 저장되어 FieldDescriptor 객체가 생성되고 해당 객체를 인자로 받는 RestDocsField 객체가 반환된다.
"code" type JsonFieldType.NUMBER description "응답 코드"
◼ 위와 같이 코드를 작성하면, "code" type JsonFieldType.NUMBER 가 RestDocsField로 변환된다.
◼ 이후, 생성된 ResDocsField의 description infix 함수를 통해 FieldDescriptor 의 description 값으로 "응답 코드" 가 저장된다.
✔ ResponseFieldsSnippet 만들기
fun responseBody(vararg fields: RestDocsField): ResponseFieldsSnippet = PayloadDocumentation.responseFields(fields.map { it.descriptor })
◼ 이제 생성된 RestDocsField를 인자로 받아 responseFields 함수에 RestDocsField의 descriptor를 인자로 넘겨주면 완성된다.
💬 vararg 키워드
가변 인자를 사용하면 함수를 호출할 때, 인자의 개수를 유동적으로 지정할 수 있다.
✔ MockMvcResultHandlersDsl에 확장 함수 추가
fun MockMvcResultHandlersDsl.createDocument(identifier: String, vararg snippets: Snippet) { return handle(MockMvcRestDocumentation.document("{class-name}/$identifier", *snippets)) }
◼ createDocument로 snippet을 저장할 폴더 이름과 만든 snippets을 인자로 받아 anDo 함수를 실행한다.
결론
코틀린에서 제공하는 다양한 기능을 활용하여 기존 코드를 개선할 수 있었다. 반복적인 코드를 최소한으로 줄이고 가독성을 높여 작성하기 쉽고 이해하기 쉬운 코드를 작성할 수 있었다.
'Backend > Spring' 카테고리의 다른 글
Spring Event 사용하기 (0) 2023.06.29 kotlin + HttpInterface 알아보기 (0) 2023.06.22 Kotest를 통한 DCI 패턴 적용 (0) 2023.05.27 spring에서 flyway 사용하기 (0) 2023.05.26 [Spring] checkstyle 적용하기 (0) 2023.05.03