Skip to content

kimsunhak/rest-docs-dsl-kotlin

Repository files navigation

📘 RestDocs-DSL for Kotlin

Kotlin 기반의 DSL(도메인 특화 언어)을 활용하여 Spring REST Docs 문서 작성을 더 간결하고 읽기 쉽게 만들어주는 라이브러리입니다.

토스 기술 블로그 해당 Kotlin으로 DSL 만들기: 반복적이고 지루한 REST Docs 벗어나기 글을 보고 만들었습니다.

✨ 복잡한 설정 없이, 깔끔하고 선언적인 방식으로 API 문서를 작성해보세요!


✨ 주요 기능

  • ✅ Spring REST Docs 기반 DSL 제공
  • ✅ 요청/응답 필드 문서를 선언형으로 정의 가능
  • ✅ 테스트 코드와 문서 생성을 자연스럽게 통합
  • ✅ Enum, Object 타입에 대한 자동 문서화 지원

🛠 설치 방법

현재는 별도 배포 없이 로컬 모듈로 포함하여 사용 가능합니다.

1. 프로젝트에 모듈 추가

settings.gradle.kts 또는 settings.gradle에 다음을 추가합니다.

include(":restdocs-dsl")

2. 의존성 추가

// build.gradle.kts
dependencies {
    implementation(project(":restdocs-dsl"))
}

3. gradle.properties 정의

## openapi3 ##
openapi3ServerUrls=http://localhost:8080,https://dev.shower.me
openapi3Title=Sample-API
openapi3Description=Sample-API-Description
openapi3DocsVersion=0.1
openapi3OutDirectory=build/api-spec
openapi3IntoDirectory=src/main/resources/static/
openapi3JsonName=sample-docs

4. swagger.yml 추가

springdoc:
  default-consumes-media-type: application/json;charset=UTF-8
  default-produces-media-type: application/json;charset=UTF-8
  swagger-ui:
    url: /static/sample-docs.yaml #gradle.properties openapi3JsonName

5. 📂 디렉토리 구조 예시

├── restdocs-dsl/
   ├── src/main/kotlin/
      └── dsl/ // DSL 구성 요소
   └── build.gradle.kts
├── sample-api/
   └── src/test/kotlin/
       └── api/LoginApiTest.kt // DSL 사용 예시

6. 📄 RestDocs 코드 및 RestDocs-DSL 코드

📄 RestDocs Code

@DisplayName("로그인")
@Test
fun login() {
    every { service.login(any<Credentials>()) } returns Token("accessToken", "refreshToken")

    val response = given()
        .contentType(ContentType.JSON)
        .body(
            LoginRequest(
                loginId = "test@test.com",
                password = "test1234",
                socialType = "ME"
            )
        )
        .post("/api/v1/sample-login")
        .then()
        .status(HttpStatus.OK)
        .apply(
            document(
                "샘플 로그인",
                requestPreprocessor(),
                responsePreprocessor(),
                ResourceDocumentation.resource(
                    ResourceSnippetParameters.builder()
                        .tags(DocsTag.AUTH.tagName)
                        .summary("샘플 로그인")
                        .requestSchema(Schema.schema("LoginRequest"))
                        .requestFields(
                            fieldWithPath("loginId").description("로그인 아이디").type("String"),
                            fieldWithPath("password").description("패스워드").type("String"),
                            fieldWithPath("socialType").description("소셜 유형").type(SocialType::class),
                        )
                        .responseSchema(Schema.schema("TokenResponse"))
                        .responseFiedls(
                            fieldWithPath("result").description("응답").type(ResultType::class),
                            fieldWithPath("error").description("에러").optional(),
                            fieldWithPath("data.accessToken").description("accessToken").type("String"),
                            fieldWithPath("data.refreshToken").description("refreshToken").type("String"),
                        )
                        .build(),
                ),
            ),
        )
}

📄 RestDocs DSL Code

@DisplayName("로그인")
@Test
fun login() {
    every { service.login(any<Credentials>()) } returns Token("accessToken", "refreshToken")

    val response = given()
        .contentType(ContentType.JSON)
        .body(
            LoginRequest(
                loginId = "test@test.com",
                password = "test1234",
                socialType = "ME"
            )
        )
        .post("/api/v1/sample-login")
        .then()
        .status(HttpStatus.OK)

    response.makeDocument(
        identifier = "샘플 로그인",
        summary = "샘플 로그인",
        requestSchema = "LoginRequest",
        responseSchema = "TokenResponse",
        requestBody = requestBody(
            "loginId" type STRING means "로그인 아이디" example "test@test.com" isOptional false,
            "password" type STRING means "비밀번호" example "test1234",
            "socialType" type ENUM(SocialType::class) means "소셜 유형" example "ME",
        ),
        responseBody = responseBody(
            "result" type ENUM(ResultType::class) means "응답",
            "error" type OBJECT means "에러" isOptional true,
            "data.accessToken" type STRING means "accessToken",
            "data.refreshToken" type STRING means "refreshToken",
        ),
    )
}

Run Shell Script

$ ./gradlew clean build tasks restDocsTest
$ ./gradlew clean build tasks copyOasSwagger

Useful Links

About

Kotlin DSL for Spring REST Docs – Write clean and expressive API documentation with less boilerplate.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages