Skip to content

요청하기

클라이언트를 구성한 후 HTTP 요청을 시작할 수 있습니다. 이를 위한 주요 방법은 URL을 파라미터로 받는 .request() 함수를 사용하는 것입니다. 이 함수 내에서 다양한 요청 파라미터를 구성할 수 있습니다:

  • GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH와 같은 HTTP 메서드를 지정합니다.
  • URL을 문자열로 구성하거나, 도메인, 경로, 쿼리 파라미터와 같은 구성 요소를 개별적으로 구성합니다.
  • Unix 도메인 소켓을 사용합니다.
  • 헤더와 쿠키를 추가합니다.
  • 요청 본문을 포함합니다. 예를 들어, 일반 텍스트, 데이터 객체 또는 폼 파라미터가 있습니다.

이러한 파라미터는 HttpRequestBuilder 클래스에 의해 노출됩니다.

kotlin
import io.ktor.client.request.*
import io.ktor.client.statement.*

val response: HttpResponse = client.request("https://ktor.io/") {
  // Configure request parameters exposed by HttpRequestBuilder
}

.request() 함수는 HttpResponse 객체로 응답을 반환합니다. HttpResponse는 문자열, JSON 객체 등 다양한 형식으로 응답 본문을 가져오는 데 필요한 API와 상태 코드, 콘텐츠 타입, 헤더와 같은 응답 파라미터를 검색하는 API를 노출합니다. 자세한 내용은 응답 수신을 참조하세요.

.request()는 정지 함수(suspending function)입니다. 즉, 코루틴(coroutine) 또는 다른 정지 함수 내에서 호출되어야 합니다. 정지 함수에 대해 자세히 알아보려면 코루틴 기본을 참조하세요.

HTTP 메서드 지정

.request() 함수를 호출할 때 method 속성을 사용하여 원하는 HTTP 메서드를 지정할 수 있습니다.

kotlin
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*

val response: HttpResponse = client.request("https://ktor.io/") {
    method = HttpMethod.Get
}

.request() 외에도 HttpClient.get(), .post(), .put()와 같은 기본 HTTP 메서드를 위한 특정 함수를 제공합니다. 위 예시는 .get() 함수를 사용하여 다음과 같이 간소화할 수 있습니다:

kotlin
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")

두 예시 모두에서 요청 URL은 문자열로 지정됩니다. 또한 HttpRequestBuilder를 사용하여 URL 구성 요소를 개별적으로 구성할 수 있습니다.

요청 URL 지정

Ktor 클라이언트는 여러 가지 방법으로 요청 URL을 구성할 수 있도록 합니다.

전체 URL 문자열 전달

kotlin
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")

URL 구성 요소 개별적으로 구성

kotlin
client.get {
    url {
        protocol = URLProtocol.HTTPS
        host = "ktor.io"
        path("docs/welcome.html")
    }
}

이 경우 HttpRequestBuilder가 제공하는 url 파라미터가 사용됩니다. 이는 URLBuilder 인스턴스를 받아 복잡한 URL을 구성하는 데 더 많은 유연성을 제공합니다.

모든 요청에 대한 기본 URL을 구성하려면 DefaultRequest 플러그인을 사용하세요.

경로 세그먼트

이전 예시에서는 URLBuilder.path 속성을 사용하여 전체 URL 경로를 지정했습니다. 대신 appendPathSegments() 함수를 사용하여 개별 경로 세그먼트를 전달할 수 있습니다.

kotlin
client.get("https://ktor.io") {
    url {
        appendPathSegments("docs", "welcome.html")
    }
}

기본적으로 appendPathSegments는 경로 세그먼트를 인코딩합니다. 인코딩을 비활성화하려면 대신 appendEncodedPathSegments()를 사용하세요.

쿼리 파라미터

쿼리 문자열 파라미터를 추가하려면 URLBuilder.parameters 속성을 사용하세요.
kotlin
client.get("https://ktor.io") {
    url {
        parameters.append("token", "abc123")
    }
}

기본적으로 parameters는 쿼리 파라미터를 인코딩합니다. 인코딩을 비활성화하려면 대신 encodedParameters()를 사용하세요.

trailingQuery 속성은 쿼리 파라미터가 없더라도 ? 문자를 유지하는 데 사용할 수 있습니다.

URL 프래그먼트

해시 마크 #는 URL 끝 근처에 선택적 프래그먼트(fragment)를 도입합니다. fragment 속성을 사용하여 URL 프래그먼트를 구성할 수 있습니다.

kotlin
client.get("https://ktor.io") {
    url {
        fragment = "some_anchor"
    }
}

기본적으로 fragment는 URL 프래그먼트를 인코딩합니다. 인코딩을 비활성화하려면 대신 encodedFragment()를 사용하세요.

Unix 도메인 소켓 지정

Unix 도메인 소켓은 CIO 엔진에서만 지원됩니다. Ktor 서버와 함께 Unix 소켓을 사용하려면 서버를 그에 따라 구성하세요.

Unix 도메인 소켓을 수신 대기하는 서버에 요청을 보내려면, CIO 클라이언트를 사용할 때 unixSocket() 함수를 호출하세요.

kotlin
val client = HttpClient(CIO)

val response: HttpResponse = client.get("/") {
    unixSocket("/tmp/test-unix-socket-ktor.sock")
}

기본 요청의 일부로 Unix 도메인 소켓을 구성할 수도 있습니다.

요청 파라미터 설정

HTTP 메서드, 헤더, 쿠키를 포함한 다양한 요청 파라미터를 지정할 수 있습니다. 특정 클라이언트의 모든 요청에 대한 기본 파라미터를 구성해야 하는 경우, DefaultRequest 플러그인을 사용하세요.

헤더

여러 가지 방법으로 요청에 헤더를 추가할 수 있습니다.

여러 헤더 추가

headers 함수를 사용하면 여러 헤더를 한 번에 추가할 수 있습니다.

kotlin
client.get("https://ktor.io") {
    headers {
        append(HttpHeaders.Accept, "text/html")
        append(HttpHeaders.Authorization, "abc123")
        append(HttpHeaders.UserAgent, "ktor client")
    }
}

단일 헤더 추가

header 함수를 사용하면 단일 헤더를 추가할 수 있습니다.

인증에 basicAuth 또는 bearerAuth 사용

basicAuthbearerAuth 함수는 해당 HTTP 스키마와 함께 Authorization 헤더를 추가합니다.

고급 인증 구성에 대해서는 Ktor 클라이언트의 인증 및 권한 부여를 참조하세요.

쿠키

쿠키를 보내려면 cookie() 함수를 사용하세요.

kotlin
client.get("https://ktor.io") {
    cookie(name = "user_name", value = "jetbrains", expires = GMTDate(
        seconds = 0,
        minutes = 0,
        hours = 10,
        dayOfMonth = 1,
        month = Month.APRIL,
        year = 2023
    ))
}

Ktor는 또한 호출 간에 쿠키를 유지할 수 있는 HttpCookies 플러그인을 제공합니다. 이 플러그인이 설치된 경우, cookie() 함수를 사용하여 추가된 쿠키는 무시됩니다.

요청 본문 설정

요청 본문을 설정하려면 HttpRequestBuilder가 제공하는 setBody() 함수를 호출하세요. 이 함수는 일반 텍스트, 임의 클래스 인스턴스, 폼 데이터, 바이트 배열을 포함한 다양한 유형의 페이로드를 허용합니다.

텍스트

본문으로 일반 텍스트를 보내는 것은 다음과 같이 구현할 수 있습니다.

kotlin
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*

val response: HttpResponse = client.post("http://localhost:8080/post") {
    setBody("Body content")
}

객체

ContentNegotiation 플러그인을 활성화하면 요청 본문 내에서 클래스 인스턴스를 JSON으로 보낼 수 있습니다. 이를 위해 setBody() 함수에 클래스 인스턴스를 전달하고 contentType() 함수를 사용하여 콘텐츠 타입을 application/json으로 설정합니다.

kotlin
val response: HttpResponse = client.post("http://localhost:8080/customer") {
    contentType(ContentType.Application.Json)
    setBody(Customer(3, "Jet", "Brains"))
}

자세한 내용은 Ktor 클라이언트의 콘텐츠 협상 및 직렬화를 참조하세요.

폼 파라미터

Ktor 클라이언트는 application/x-www-form-urlencoded 타입으로 폼 파라미터를 보내기 위한 submitForm() 함수를 제공합니다. 다음 예시는 그 사용법을 보여줍니다.

kotlin
val client = HttpClient(CIO)
val response: HttpResponse = client.submitForm(
    url = "http://localhost:8080/signup",
    formParameters = parameters {
        append("username", "JetBrains")
        append("email", "[email protected]")
        append("password", "foobar")
        append("confirmation", "foobar")
    }
)
  • url은 요청을 만드는 URL을 지정합니다.
  • formParametersparameters를 사용하여 구성된 폼 파라미터 집합입니다.

전체 예시는 client-submit-form을 참조하세요.

URL에 인코딩된 폼 파라미터를 보내려면 encodeInQuerytrue로 설정하세요.

파일 업로드

폼과 함께 파일을 보내야 하는 경우 다음 방법을 사용할 수 있습니다.

  • .submitFormWithBinaryData() 함수를 사용합니다. 이 경우 경계(boundary)가 자동으로 생성됩니다.
  • post 함수를 호출하고 MultiPartFormDataContent 인스턴스를 setBody 함수에 전달합니다. MultiPartFormDataContent 생성자는 경계 값을 전달할 수도 있습니다.

두 가지 접근 방식 모두 formData {} 함수를 사용하여 폼 데이터를 구성해야 합니다.

.submitFormWithBinaryData() 사용

.submitFormWithBinaryData() 함수는 자동으로 경계를 생성하며, .readBytes()를 사용하여 파일 콘텐츠가 메모리로 안전하게 읽어올 수 있을 만큼 작은 간단한 사용 사례에 적합합니다.

kotlin
        val client = HttpClient(CIO)

        val response: HttpResponse = client.submitFormWithBinaryData(
            url = "http://localhost:8080/upload",
            formData = formData {
                append("description", "Ktor logo")
                append("image", File("ktor_logo.png").readBytes(), Headers.build {
                    append(HttpHeaders.ContentType, "image/png")
                    append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
                })
            }
        )

전체 예시는 client-upload를 참조하세요.

MultiPartFormDataContent 사용

크거나 동적인 콘텐츠를 효율적으로 스트리밍하려면 InputProvider와 함께 MultiPartFormDataContent를 사용할 수 있습니다. InputProvider를 사용하면 파일 데이터를 전체를 메모리로 로드하는 대신 버퍼링된 스트림으로 공급할 수 있으므로 대용량 파일에 적합합니다. MultiPartFormDataContent를 사용하면 onUpload 콜백을 사용하여 업로드 진행 상황을 모니터링할 수도 있습니다.

kotlin
        val client = HttpClient(CIO)

        val file = File("ktor_logo.png")

        val response: HttpResponse = client.post("http://localhost:8080/upload") {
            setBody(
                MultiPartFormDataContent(
                    formData {
                        append("description", "Ktor logo")
                        append(
                            "image",
                            InputProvider { file.inputStream().asInput().buffered() },
                            Headers.build {
                                append(HttpHeaders.ContentType, "image/png")
                                append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
                            }
                        )
                    },
                    boundary = "WebAppBoundary"
                )
            )
            onUpload { bytesSentTotal, contentLength ->
                println("Sent $bytesSentTotal bytes from $contentLength")
            }
        }

멀티플랫폼 프로젝트에서는 InputProvider와 함께 SystemFileSystem.source()를 사용할 수 있습니다.

kotlin
InputProvider { SystemFileSystem.source(Path("ktor_logo.png")).buffered() }

또한 사용자 지정 경계 및 콘텐츠 타입으로 MultiPartFormDataContent를 수동으로 구성할 수도 있습니다.

kotlin
fun customMultiPartMixedDataContent(parts: List<PartData>): MultiPartFormDataContent {
    val boundary = "WebAppBoundary"
    val contentType = ContentType.MultiPart.Mixed.withParameter("boundary", boundary)
    return MultiPartFormDataContent(parts, boundary, contentType)
}

전체 예시는 client-upload-progress를 참조하세요.

바이너리 데이터

application/octet-stream 콘텐츠 타입으로 바이너리 데이터를 보내려면 ByteReadChannel 인스턴스를 setBody() 함수에 전달합니다. 예를 들어, File.readChannel() 함수를 사용하여 파일에 대한 읽기 채널을 열 수 있습니다.

kotlin
val response = client.post("http://0.0.0.0:8080/upload") {
    setBody(File("ktor_logo.png").readChannel())
}

전체 예시는 client-upload-binary-data를 참조하세요.

병렬 요청

기본적으로 여러 요청을 순차적으로 보내면 클라이언트는 이전 요청이 완료될 때까지 각 호출을 정지합니다. 여러 요청을 동시에 수행하려면 launch() 또는 async() 함수를 사용하세요. 다음 예시는 async()를 사용하여 두 개의 요청을 병렬로 실행하는 방법을 보여줍니다.

kotlin
coroutineScope {
    // Parallel requests
    val firstRequest: Deferred<String> = async { client.get("http://localhost:8080/path1").bodyAsText() }
    val secondRequest: Deferred<String> = async { client.get("http://localhost:8080/path2").bodyAsText() }
    val firstRequestContent = firstRequest.await()
    val secondRequestContent = secondRequest.await()
}

전체 예시는 client-parallel-requests를 참조하세요.

요청 취소

요청을 취소하려면 해당 요청을 실행하는 코루틴을 취소합니다. launch() 함수는 실행 중인 코루틴을 취소하는 데 사용할 수 있는 Job을 반환합니다.

kotlin
import kotlinx.coroutines.*

val client = HttpClient(CIO)
val job = launch {
    val requestContent: String = client.get("http://localhost:8080")
}
job.cancel()

자세한 내용은 취소 및 타임아웃을 참조하세요.