Skip to content
Server Plugin

타입 안전 라우팅

필수 의존성: io.ktor:ktor-server-resources

코드 예시: resource-routing

네이티브 서버
Ktor는 Kotlin/Native를 지원하며 추가 런타임이나 가상 머신 없이 서버를 실행할 수 있습니다.
지원: ✅

Ktor는 타입 안전 라우팅을 구현할 수 있도록 Resources 플러그인을 제공합니다. 이를 위해 타입이 지정된 라우트 역할을 할 클래스를 생성하고, @Resource 키워드를 사용하여 이 클래스에 어노테이션을 달아야 합니다. @Resource 어노테이션은 kotlinx.serialization 라이브러리에서 제공하는 @Serializable 동작을 포함하고 있습니다.

Ktor 클라이언트는 서버에 타입이 지정된 요청을 수행하는 기능을 제공합니다.

의존성 추가

kotlinx.serialization 추가

리소스 클래스@Serializable 동작을 가져야 하므로, 설정 (Setup) 섹션에 설명된 대로 Kotlin 직렬화 플러그인을 추가해야 합니다.

Resources 의존성 추가

Resources을(를) 사용하려면 빌드 스크립트에 ktor-server-resources 아티팩트를 포함해야 합니다.

Kotlin
Groovy
XML

Resources 설치

애플리케이션에 Resources 플러그인을 설치하려면, 지정된

모듈
모듈을 사용하면 라우트를 그룹화하여 애플리케이션을 구조화할 수 있습니다.
내의 install 함수에 전달하세요. 다음 코드 스니펫은 Resources을(를) 설치하는 방법을 보여줍니다...

  • ... embeddedServer 함수 호출 내에서.
  • ... Application 클래스의 확장 함수인 명시적으로 정의된 module 내에서.
kotlin
kotlin

리소스 클래스 생성

각 리소스 클래스는 @Resource 어노테이션을 가져야 합니다. 아래에서 단일 경로 세그먼트, 쿼리 및 경로 파라미터 등을 정의하는 몇 가지 리소스 클래스 예시를 살펴보겠습니다.

리소스 URL

아래 예시는 /articles 경로에 응답하는 리소스를 지정하는 Articles 클래스를 정의하는 방법을 보여줍니다.

kotlin
import io.ktor.resources.*

@Resource("/articles")
class Articles()

쿼리 파라미터가 있는 리소스

아래 Articles 클래스에는 쿼리 파라미터 역할을 하는 sort 문자열 프로퍼티가 있으며, 이를 통해 sort 쿼리 파라미터가 있는 다음 경로(/articles?sort=new)에 응답하는 리소스를 정의할 수 있습니다.

kotlin
@Resource("/articles")
class Articles(val sort: String? = "new")

중첩 클래스가 있는 리소스

여러 경로 세그먼트를 포함하는 리소스를 생성하기 위해 클래스를 중첩할 수 있습니다. 이 경우 중첩 클래스는 외부 클래스 타입을 가진 프로퍼티를 가져야 한다는 점에 유의하세요. 아래 예시는 /articles/new 경로에 응답하는 리소스를 보여줍니다.

kotlin
@Resource("/articles")
class Articles() {
    @Resource("new")
    class New(val parent: Articles = Articles())
}

경로 파라미터가 있는 리소스

아래 예시는 경로 세그먼트와 일치하며 id라는 이름으로 캡처되는 중첩된 {id} 정수 경로 파라미터를 추가하는 방법을 보여줍니다.

kotlin
@Resource("/articles")
class Articles() {
    @Resource("{id}")
    class Id(val parent: Articles = Articles(), val id: Long)
}

예를 들어, 이 리소스는 /articles/12에 응답하는 데 사용될 수 있습니다.

예시: CRUD 작업을 위한 리소스

위 예시들을 종합하여 CRUD 작업을 위한 Articles 리소스를 생성해 보겠습니다.

kotlin
@Resource("/articles")
class Articles(val sort: String? = "new") {
    @Resource("new")
    class New(val parent: Articles = Articles())

    @Resource("{id}")
    class Id(val parent: Articles = Articles(), val id: Long) {
        @Resource("edit")
        class Edit(val parent: Id)
    }
}

이 리소스는 모든 아티클을 나열하고, 새 아티클을 게시하고, 편집하는 등에 사용될 수 있습니다. 다음 장에서 이 리소스에 대한 라우트 핸들러를 정의하는 방법을 살펴보겠습니다.

전체 예시는 여기에서 확인할 수 있습니다: resource-routing.

라우트 핸들러 정의

타입이 지정된 리소스에 대한 라우트 핸들러를 정의하려면, 리소스 클래스를 동사 함수(get, post, put 등)에 전달해야 합니다. 예를 들어, 아래 라우트 핸들러는 /articles 경로에 응답합니다.

kotlin
@Resource("/articles")
class Articles()

fun Application.module() {
    install(Resources)
    routing {
        get<Articles> { articles ->
            // Get all articles ...
            call.respondText("List of articles: $articles")
        }
    }
}

아래 예시는 예시: CRUD 작업을 위한 리소스에서 생성된 Articles 리소스에 대한 라우트 핸들러를 정의하는 방법을 보여줍니다. 라우트 핸들러 내에서 Article을(를) 파라미터로 접근하고 해당 프로퍼티 값을 얻을 수 있다는 점에 유의하세요.

kotlin
fun Application.module() {
    install(Resources)
    routing {
        get<Articles> { article ->
            // Get all articles ...
            call.respondText("List of articles sorted starting from ${article.sort}")
        }
        get<Articles.New> {
            // Show a page with fields for creating a new article ...
            call.respondText("Create a new article")
        }
        post<Articles> {
            // Save an article ...
            call.respondText("An article is saved", status = HttpStatusCode.Created)
        }
        get<Articles.Id> { article ->
            // Show an article with id ${article.id} ...
            call.respondText("An article with id ${article.id}", status = HttpStatusCode.OK)
        }
        get<Articles.Id.Edit> { article ->
            // Show a page with fields for editing an article ...
            call.respondText("Edit an article with id ${article.parent.id}", status = HttpStatusCode.OK)
        }
        put<Articles.Id> { article ->
            // Update an article ...
            call.respondText("An article with id ${article.id} updated", status = HttpStatusCode.OK)
        }
        delete<Articles.Id> { article ->
            // Delete an article ...
            call.respondText("An article with id ${article.id} deleted", status = HttpStatusCode.OK)
        }
    }
}

각 엔드포인트에 대한 요청 처리 팁은 다음과 같습니다:

  • get<Articles>

    이 라우트 핸들러는 sort 쿼리 파라미터에 따라 정렬된 모든 아티클을 반환해야 합니다. 예를 들어, 이는 모든 아티클이 포함된 HTML 페이지 또는 JSON 객체일 수 있습니다.

  • get<Articles.New>

    이 엔드포인트는 새 아티클 생성을 위한 필드가 포함된 웹 폼으로 응답합니다.

  • post<Articles>

    post<Articles> 엔드포인트는 웹 폼을 사용하여 전송된 파라미터를 수신하도록 되어 있습니다. Ktor는 또한 ContentNegotiation 플러그인을 사용하여 JSON 데이터를 객체로 수신할 수 있도록 합니다.

  • get<Articles.Id>

    이 라우트 핸들러는 지정된 식별자를 가진 아티클을 반환해야 합니다. 이는 아티클을 보여주는 HTML 페이지이거나 아티클 데이터가 포함된 JSON 객체일 수 있습니다.

  • get<Articles.Id.Edit>

    이 엔드포인트는 기존 아티클 편집을 위한 필드가 포함된 웹 폼으로 응답합니다.

  • put<Articles.Id>

    post<Articles> 엔드포인트와 유사하게, put 핸들러는 웹 폼을 사용하여 전송된 폼 파라미터를 수신합니다.

  • delete<Articles.Id>

    이 라우트 핸들러는 지정된 식별자를 가진 아티클을 삭제합니다.

전체 예시는 여기에서 확인할 수 있습니다: resource-routing.

리소스 정의를 라우팅에 사용하는 것 외에도, 링크를 생성하는 데에도 사용될 수 있습니다. 이는 때때로 _역방향 라우팅_이라고 불립니다. 리소스로부터 링크를 생성하는 것은 HTML DSL로 생성된 HTML 문서에 이러한 링크를 추가해야 하거나, 리다이렉션 응답을 생성해야 할 때 유용할 수 있습니다.

Resources 플러그인은 Application에 오버로드된 href 메서드를 추가하며, 이를 통해 Resource로부터 링크를 생성할 수 있습니다. 예를 들어, 아래 코드 스니펫은 위에 정의된 Edit 리소스에 대한 링크를 생성합니다.

kotlin
val link: String = href(Articles.Id.Edit(Articles.Id(id = 123)))

상위 Articles 리소스가 기본값 new를 가진 sort 쿼리 파라미터를 정의하므로, link 변수는 다음을 포함합니다:

/articles/123/edit?sort=new

호스트와 프로토콜을 지정하는 URL을 생성하려면 URLBuilderhref 메서드에 제공할 수 있습니다. 추가 쿼리 파라미터도 URLBuilder를 사용하여 지정할 수 있으며, 이 예시에서 보여줍니다:

kotlin
val urlBuilder = URLBuilder(URLProtocol.HTTPS, "ktor.io", parameters = parametersOf("token", "123"))
href(Articles(sort = null), urlBuilder)
val link: String = urlBuilder.buildString()

link 변수는 다음을 포함합니다:

https://ktor.io/articles?token=123

아래 예시는 리소스로부터 생성된 링크를 HTML 응답에 추가하는 방법을 보여줍니다:

kotlin
get {
    call.respondHtml {
        body {
            this@module.apply {
                p {
                    val link: String = href(Articles())
                    a(link) { +"Get all articles" }
                }
                p {
                    val link: String = href(Articles.New())
                    a(link) { +"Create a new article" }
                }
                p {
                    val link: String = href(Articles.Id.Edit(Articles.Id(id = 123)))
                    a(link) { +"Edit an exising article" }
                }
                p {
                    val urlBuilder = URLBuilder(URLProtocol.HTTPS, "ktor.io", parameters = parametersOf("token", "123"))
                    href(Articles(sort = null), urlBuilder)
                    val link: String = urlBuilder.buildString()
                    i { a(link) { +link } }
                }
            }
        }
    }
}

전체 예시는 여기에서 확인할 수 있습니다: resource-routing.