Skip to content

응답 받기

HTTP 요청을 수행하는 데 사용되는 모든 함수(request, get, post 등)는 HttpResponse 객체로 응답을 받을 수 있도록 합니다.

HttpResponse응답 본문을 다양한 방식(원시 바이트, JSON 객체 등)으로 얻고, 상태 코드, 콘텐츠 타입, 헤더와 같은 응답 파라미터를 획득하는 데 필요한 API를 노출합니다. 예를 들어, 파라미터 없는 GET 요청에 대한 HttpResponse는 다음과 같이 받을 수 있습니다:

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

응답 파라미터 받기

HttpResponse 클래스를 사용하면 상태 코드, 헤더, HTTP 버전 등과 같은 다양한 응답 파라미터를 얻을 수 있습니다.

상태 코드

응답의 상태 코드를 얻으려면 HttpResponse.status 속성을 사용하세요:

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

val httpResponse: HttpResponse = client.get("https://ktor.io/")
if (httpResponse.status.value in 200..299) {
    println("Successful response!")
}

헤더

HttpResponse.headers 속성을 사용하면 모든 응답 헤더를 포함하는 Headers 맵을 얻을 수 있습니다. 또한, HttpResponse는 특정 헤더 값을 받기 위한 다음 함수들을 노출합니다:

  • contentType for the Content-Type header value
  • charset for a charset from the Content-Type header value.
  • etag for the E-Tag header value.
  • setCookie for the Set-Cookie header value.

Ktor는 또한 호출 간에 쿠키를 유지할 수 있는 HttpCookies 플러그인을 제공합니다.

응답 본문 받기

원시 본문

응답의 원시 본문을 받으려면 body 함수를 호출하고 필요한 타입을 파라미터로 전달하세요. 아래 코드 스니펫은 String으로 원시 본문을 받는 방법을 보여줍니다:

kotlin
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val stringBody: String = httpResponse.body()

마찬가지로, ByteArray로 본문을 얻을 수 있습니다:

kotlin
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val byteArrayBody: ByteArray = httpResponse.body()

아래 실행 가능한 예제ByteArray로 응답을 받아 파일에 저장하는 방법을 보여줍니다:

kotlin
    val client = HttpClient()
    val file = File.createTempFile("files", "index")

    runBlocking {
        val httpResponse: HttpResponse = client.get("https://ktor.io/") {
            onDownload { bytesSentTotal, contentLength ->
                println("Received $bytesSentTotal bytes from $contentLength")
            }
        }
        val responseBody: ByteArray = httpResponse.body()
        file.writeBytes(responseBody)
        println("A file saved to ${file.path}")
    }

위 예제의 onDownload() 확장 함수는 다운로드 진행 상황을 표시하는 데 사용됩니다.

비 스트리밍 요청의 경우, 응답 본문은 자동으로 메모리에 로드되고 캐시되어 반복적인 접근을 허용합니다. 이는 작은 페이로드에는 효율적이지만, 큰 응답에서는 높은 메모리 사용량을 초래할 수 있습니다.

큰 응답을 효율적으로 처리하려면, 응답을 메모리에 저장하지 않고 증분적으로 처리하는 스트리밍 방식을 사용하세요.

JSON 객체

ContentNegotiation 플러그인이 설치된 경우, 응답을 받을 때 JSON 데이터를 데이터 클래스로 역직렬화할 수 있습니다:

kotlin
val customer: Customer = client.get("http://localhost:8080/customer/3").body()

더 자세히 알아보려면 데이터 주고받기를 참조하세요.

ContentNegotiation 플러그인은 클라이언트서버 모두에서 사용할 수 있습니다. 사용 사례에 맞는 것을 사용해야 합니다.

데이터 스트리밍

HttpResponse.body 함수를 호출하여 본문을 얻을 때, Ktor는 응답을 메모리에서 처리하고 전체 응답 본문을 반환합니다. 전체 응답을 기다리는 대신 응답의 청크를 순차적으로 받아야 하는 경우, 스코프가 지정된 execute 블록과 함께 HttpStatement를 사용하세요. 아래 실행 가능한 예제는 응답 콘텐츠를 청크(바이트 패킷) 단위로 받아 파일에 저장하는 방법을 보여줍니다:

kotlin
    val client = HttpClient(CIO)
    val file = File.createTempFile("files", "index")
    val stream = file.outputStream().asSink()
    val fileSize = 100 * 1024 * 1024

    runBlocking {
        client.prepareGet("https://httpbin.org/bytes/$fileSize").execute { httpResponse ->
            val channel: ByteReadChannel = httpResponse.body()
            var count = 0L
            stream.use {
                while (!channel.exhausted()) {
                    val chunk = channel.readRemaining()
                    count += chunk.remaining

                    chunk.transferTo(stream)
                    println("Received $count bytes from ${httpResponse.contentLength()}")
                }
            }
        }

        println("A file saved to ${file.path}")
    }

이 예제에서는 ByteReadChannel이 데이터를 비동기적으로 읽는 데 사용됩니다. ByteReadChannel.readRemaining()을 사용하면 채널의 모든 사용 가능한 바이트를 가져오고, Source.transferTo()는 데이터를 파일에 직접 써서 불필요한 할당을 줄입니다.

추가 처리 없이 응답 본문을 파일에 저장하려면, 대신 ByteReadChannel.copyAndClose() 함수를 사용할 수 있습니다:

Kotlin
client.prepareGet("https://httpbin.org/bytes/$fileSize").execute { httpResponse ->
    val channel: ByteReadChannel = httpResponse.body()
    channel.copyAndClose(file.writeChannel())
    println("A file saved to ${file.path}")
}