Ktor 서버의 콘텐츠 협상 및 직렬화
필수 의존성: io.ktor:ktor-server-content-negotiation
코드 예제: json-kotlinx
ContentNegotiation 플러그인은 두 가지 주요 목적을 수행합니다:
- 클라이언트와 서버 간의 미디어 유형(media types) 협상. 이를 위해
Accept및Content-Type헤더를 사용합니다. - 콘텐츠를 특정 형식으로 직렬화/역직렬화합니다. Ktor는 JSON, XML, CBOR, ProtoBuf 형식을 기본적으로 지원합니다.
클라이언트 측에서 Ktor는 콘텐츠 직렬화/역직렬화를 위해 ContentNegotiation 플러그인을 제공합니다.
의존성 추가
ContentNegotiation
ContentNegotiation을 사용하려면 빌드 스크립트에 ktor-server-content-negotiation 아티팩트를 포함해야 합니다:
특정 형식의 직렬화 도구(serializer)에는 추가 아티팩트가 필요하다는 점에 유의하세요. 예를 들어, kotlinx.serialization은 JSON을 위해 ktor-serialization-kotlinx-json 의존성이 필요합니다.
직렬화 (Serialization)
kotlinx.serialization 컨버터를 사용하기 전에, 설정(Setup) 섹션에 설명된 대로 Kotlin 직렬화 플러그인을 추가해야 합니다.
JSON
JSON 데이터를 직렬화/역직렬화하려면 kotlinx.serialization, Gson, Jackson 라이브러리 중 하나를 선택할 수 있습니다.
빌드 스크립트에 ktor-serialization-kotlinx-json 아티팩트를 추가합니다:
빌드 스크립트에 ktor-serialization-gson 아티팩트를 추가합니다:
빌드 스크립트에 ktor-serialization-jackson 아티팩트를 추가합니다:
XML
XML을 직렬화/역직렬화하려면 빌드 스크립트에 ktor-serialization-kotlinx-xml을 추가합니다:
XML 직렬화는
jsNode타겟에서 지원되지 않음에 유의하세요.
CBOR
CBOR을 직렬화/역직렬화하려면 빌드 스크립트에 ktor-serialization-kotlinx-cbor을 추가합니다:
ProtoBuf
ProtoBuf를 직렬화/역직렬화하려면 빌드 스크립트에 ktor-serialization-kotlinx-protobuf를 추가합니다:
ContentNegotiation 설치
애플리케이션에 ContentNegotiation 플러그인을 설치하려면, 지정된
install 함수에 전달하세요. 아래 코드 스니펫은 ContentNegotiation을 설치하는 방법을 보여줍니다... - ...
embeddedServer함수 호출 내부에서 설치. - ...
Application클래스의 확장 함수인 명시적으로 정의된module내부에서 설치.
직렬화 도구(serializer) 구성
Ktor는 JSON, XML, CBOR 형식을 기본적으로 지원합니다. 또한 자신만의 커스텀 직렬화 도구를 구현할 수도 있습니다.
JSON 직렬화 도구
애플리케이션에 JSON 직렬화 도구를 등록하려면 json 메서드를 호출합니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
install(ContentNegotiation) {
json()
}json 메서드를 사용하면 JsonBuilder에서 제공하는 직렬화 설정을 조정할 수도 있습니다. 예시는 다음과 같습니다:
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
})애플리케이션에 Gson 직렬화 도구를 등록하려면 gson 메서드를 호출합니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.gson.*
install(ContentNegotiation) {
gson()
}gson 메서드를 사용하면 GsonBuilder에서 제공하는 직렬화 설정을 조정할 수도 있습니다. 예시는 다음과 같습니다:
install(ContentNegotiation) {
gson {
registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
setDateFormat(DateFormat.LONG, DateFormat.SHORT)
setPrettyPrinting()
}애플리케이션에 Jackson 직렬화 도구를 등록하려면 jackson 메서드를 호출합니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.jackson.*
install(ContentNegotiation) {
jackson()
}jackson 메서드를 사용하면 ObjectMapper에서 제공하는 직렬화 설정을 조정할 수도 있습니다. 예시는 다음과 같습니다:
install(ContentNegotiation) {
jackson {
configure(SerializationFeature.INDENT_OUTPUT, true)
setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "
"))
})
registerModule(JavaTimeModule()) // java.time.* 타입 지원
}
}XML 직렬화 도구
애플리케이션에 XML 직렬화 도구를 등록하려면 xml 메서드를 호출합니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
install(ContentNegotiation) {
xml()
}xml 메서드를 사용하면 XML 직렬화 설정에 접근할 수도 있습니다. 예시는 다음과 같습니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.xml.*
import nl.adaptivity.xmlutil.*
import nl.adaptivity.xmlutil.serialization.*
install(ContentNegotiation) {
xml(format = XML {
xmlDeclMode = XmlDeclMode.Charset
})
}CBOR 직렬화 도구
애플리케이션에 CBOR 직렬화 도구를 등록하려면 cbor 메서드를 호출합니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*
install(ContentNegotiation) {
cbor()
}cbor 메서드를 사용하면 CborBuilder에서 제공하는 CBOR 직렬화 설정에 접근할 수도 있습니다. 예시는 다음과 같습니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.cbor.*
import kotlinx.serialization.cbor.*
install(ContentNegotiation) {
cbor(Cbor {
ignoreUnknownKeys = true
})
}ProtoBuf 직렬화 도구
애플리케이션에 ProtoBuf 직렬화 도구를 등록하려면 protobuf 메서드를 호출합니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*
install(ContentNegotiation) {
protobuf()
}protobuf 메서드를 사용하면 ProtoBufBuilder에서 제공하는 ProtoBuf 직렬화 설정에 접근할 수도 있습니다. 예시는 다음과 같습니다:
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.protobuf.*
import kotlinx.serialization.protobuf.*
install(ContentNegotiation) {
protobuf(ProtoBuf {
encodeDefaults = true
})
}커스텀 직렬화 도구
지정된 Content-Type에 대한 커스텀 직렬화 도구를 등록하려면 register 메서드를 호출해야 합니다. 아래 예제에서는 application/json 및 application/xml 데이터를 역직렬화하기 위해 두 개의 커스텀 직렬화 도구를 등록합니다:
install(ContentNegotiation) {
register(ContentType.Application.Json, CustomJsonConverter())
register(ContentType.Application.Xml, CustomXmlConverter())
}데이터 수신 및 전송
데이터 클래스 생성
수신된 데이터를 객체로 역직렬화하려면 데이터 클래스를 생성해야 합니다. 예시는 다음과 같습니다:
data class Customer(val id: Int, val firstName: String, val lastName: String)kotlinx.serialization을 사용하는 경우, 이 클래스에 @Serializable 어노테이션이 있는지 확인하세요:
import kotlinx.serialization.*
@Serializable
data class Customer(val id: Int, val firstName: String, val lastName: String)다음 타입들의 직렬화/역직렬화는 kotlinx.serialization 라이브러리에서 지원됩니다:
데이터 수신
요청의 콘텐츠를 수신하고 변환하려면 데이터 클래스를 매개변수로 받는 receive 메서드를 호출합니다:
post("/customer") {
val customer = call.receive<Customer>()
customerStorage.add(customer)
call.respondText("Customer stored correctly", status = HttpStatusCode.Created)요청의 Content-Type은 요청 처리를 위한 직렬화 도구를 선택하는 데 사용됩니다. 아래 예제는 서버 측에서 Customer 객체로 변환되는 JSON 또는 XML 데이터를 포함하는 샘플 HTTP 클라이언트 요청을 보여줍니다:
POST http://0.0.0.0:8080/customer
Content-Type: application/json
{
"id": 3,
"firstName": "Jet",
"lastName": "Brains"
}POST http://0.0.0.0:8080/customer
Content-Type: application/xml
<Customer id="3" firstName="Jet" lastName="Brains"/>전체 예제는 여기에서 찾을 수 있습니다: json-kotlinx.
데이터 전송
응답으로 데이터 객체를 전달하려면 respond 메서드를 사용할 수 있습니다:
routing {
get("/customer/{id}") {
val id: Int by call.parameters
val customer: Customer = customerStorage.find { it.id == id }!!
call.respond(customer)이 경우 Ktor는 Accept 헤더를 사용하여 필요한 직렬화 도구를 선택합니다. 전체 예제는 여기에서 찾을 수 있습니다: json-kotlinx.
커스텀 직렬화 도구 구현
Ktor에서는 데이터 직렬화/역직렬화를 위한 자신만의 직렬화 도구를 작성할 수 있습니다. 이를 위해서는 ContentConverter 인터페이스를 구현해야 합니다:
interface ContentConverter {
suspend fun serialize(contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any): OutgoingContent?
suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any?
}구현 예제로 GsonConverter 클래스를 참고하세요.
