(Kotlin/코틀린) kotlinx.serialization 기초 완전 정리


1. kotlinx.serialization이란?

JetBrains가 공식 제공하는 Kotlin 직렬화 라이브러리입니다.
Kotlin 타입 시스템과 완벽하게 통합되어 컴파일 타임에 직렬화 코드를 자동 생성합니다.

// Gson/Moshi와의 핵심 차이
// Gson: 리플렉션 기반 → 런타임 처리, ProGuard 주의
// kotlinx.serialization: 컴파일 타임 코드 생성 → 빠르고 안전

2. 의존성 추가

// build.gradle.kts (프로젝트 수준)
plugins {
    kotlin("plugin.serialization") version "1.9.0"
}

// build.gradle.kts (앱 수준)
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}

3. 기본 사용 — @Serializable

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString

@Serializable
data class User(
    val id: Int,
    val name: String,
    val email: String
)

// 직렬화 (객체 → JSON 문자열)
val user = User(1, "Alice", "alice@example.com")
val json = Json.encodeToString(user)
println(json)
// {"id":1,"name":"Alice","email":"alice@example.com"}

// 역직렬화 (JSON 문자열 → 객체)
val decoded = Json.decodeFromString<User>(json)
println(decoded)
// User(id=1, name=Alice, email=alice@example.com)

4. 컬렉션 직렬화

@Serializable
data class Product(val id: Int, val name: String, val price: Double)

// List
val products = listOf(
    Product(1, "노트북", 1200000.0),
    Product(2, "마우스", 35000.0)
)

val jsonList = Json.encodeToString(products)
// [{"id":1,"name":"노트북","price":1200000.0},{"id":2,"name":"마우스","price":35000.0}]

val decodedList = Json.decodeFromString<List<Product>>(jsonList)

// Map
val productMap = mapOf("A" to Product(1, "키보드", 85000.0))
val jsonMap = Json.encodeToString(productMap)
// {"A":{"id":1,"name":"키보드","price":85000.0}}

5. nullable과 기본값

@Serializable
data class Profile(
    val id: Int,
    val name: String,
    val bio: String? = null,          // nullable, JSON에 없으면 null
    val followerCount: Int = 0,       // 기본값, JSON에 없으면 0
    val isVerified: Boolean = false
)

// JSON에 일부 필드가 없어도 기본값으로 처리
val json = """{"id":1,"name":"Bob"}"""
val profile = Json.decodeFromString<Profile>(json)
println(profile)
// Profile(id=1, name=Bob, bio=null, followerCount=0, isVerified=false)

// null 필드를 JSON에서 제외 (기본값은 포함)
val configured = Json { explicitNulls = false }
val encoded = configured.encodeToString(profile)
// {"id":1,"name":"Bob","followerCount":0,"isVerified":false}

6. Json 설정 커스터마이징

val json = Json {
    prettyPrint = true         // 들여쓰기 포함 출력
    ignoreUnknownKeys = true   // 모르는 키 무시 (역직렬화 시)
    isLenient = true           // 느슨한 JSON 파싱 (따옴표 없는 문자열 등)
    encodeDefaults = true      // 기본값도 JSON에 포함
    explicitNulls = false      // null 필드 JSON 출력 제외
    coerceInputValues = true   // 잘못된 값을 기본값으로 대체
}

val user = User(1, "Alice", "alice@example.com")
println(json.encodeToString(user))
// {
//     "id": 1,
//     "name": "Alice",
//     "email": "alice@example.com"
// }

7. 중첩 클래스

@Serializable
data class Address(
    val street: String,
    val city: String,
    val zipCode: String
)

@Serializable
data class Order(
    val orderId: String,
    val customer: User,
    val shippingAddress: Address,
    val items: List<Product>
)

val order = Order(
    orderId = "ORD-001",
    customer = User(1, "Alice", "alice@example.com"),
    shippingAddress = Address("강남대로 1", "서울", "06100"),
    items = listOf(Product(1, "노트북", 1200000.0))
)

val json = Json { prettyPrint = true }
println(json.encodeToString(order))

중첩된 모든 클래스에 @Serializable이 필요합니다.


8. enum 직렬화

@Serializable
enum class Status {
    PENDING, ACTIVE, INACTIVE
}

@Serializable
data class Account(val id: Int, val status: Status)

val account = Account(1, Status.ACTIVE)
val json = Json.encodeToString(account)
println(json)  // {"id":1,"status":"ACTIVE"}

val decoded = Json.decodeFromString<Account>(json)
println(decoded.status)  // ACTIVE

9. 실전 — Retrofit과 함께 사용

// build.gradle.kts
implementation("com.jakewharton.retrofit2:retrofit2-kotlinx-serialization-converter:1.0.0")

// Retrofit 설정
val contentType = "application/json".toMediaType()
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(Json.asConverterFactory(contentType))
    .build()

// API 인터페이스
interface UserApi {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Int): User

    @POST("users")
    suspend fun createUser(@Body user: User): User
}

10. 정리



Related Posts