Kotlin 직렬화 시작하기

Kotlin 직렬화 시작하기

직렬화(Serialization)는 객체를 저장하거나 전송할 수 있는 형식으로 변환하고, 나중에 다시 재구성할 수 있게 해 주는 기법이에요.

Kotlin 직렬화는 여러 형식을 지원해요. 이 튜토리얼에서는 Kotlin 직렬화에 필요한 플러그인과 의존성을 추가하는 방법, 그리고 객체를 JSON 형식으로 직렬화·역직렬화하는 방법을 차근차근 보여 드릴게요.

출처: Get started with Kotlin serialization

본문

플러그인과 의존성 추가하기

프로젝트에 kotlinx.serialization 라이브러리를 포함하려면, 사용하는 빌드 도구에 맞춰 플러그인과 의존성 설정을 추가하면 돼요.

// build.gradle.kts
plugins {
    kotlin("plugin.serialization") version "2.4.20"
}

dependencies { 
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0")
}
// build.gradle
plugins {
    id 'org.jetbrains.kotlin.plugin.serialization' version '2.4.20'  
}

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0'
}
<!-- pom.xml -->
<properties>
    <kotlin.version>2.4.20</kotlin.version>
    <serialization.version>1.11.0</serialization.version>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <version>${kotlin.version}</version>
            <executions>
                <execution>
                    <id>compile</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <compilerPlugins>
                    <plugin>kotlinx-serialization</plugin>
                </compilerPlugins>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.jetbrains.kotlin</groupId>
                    <artifactId>kotlin-maven-serialization</artifactId>
                    <version>${kotlin.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>org.jetbrains.kotlinx</groupId>
        <artifactId>kotlinx-serialization-json</artifactId>
        <version>${serialization.version}</version>
    </dependency>
</dependencies>

Bazel의 Kotlin 컴파일러 플러그인을 설정하려면 rules_kotlin 저장소의 예시를 따라가면 돼요. Bazel은 Kotlin 팀이 공식 지원하지 않으며, 이 저장소는 독립적으로 관리되고 있어요.

멀티플랫폼 프로젝트에 라이브러리 추가하기

멀티플랫폼 프로젝트에서 JSON용 Kotlin 직렬화를 사용하려면, 공통 소스 셋(common source set)에 JSON 직렬화 라이브러리 의존성을 추가하면 됩니다.

commonMain {
   dependencies {
      implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0")
   }
}

이 의존성은 핵심 직렬화 라이브러리도 자동으로 함께 포함시켜요.

Android 프로젝트에서 R8 구성하기

Kotlin 직렬화 라이브러리는 기본 ProGuard 규칙을 포함하고 있어서, 앱 축소(shrinking) 후에도 모든 직렬화 가능 클래스에 대한 직렬화기를 유지하기 위한 추가 설정이 필요 없어요. 다만 이 규칙은 이름이 있는 동반 객체(named companion object)를 가진 클래스에는 적용되지 않아요.

이름이 있는 동반 객체를 가진 클래스의 직렬화기를 유지하려면, 사용하는 호환 모드(compatibility mode)에 따른 규칙을 proguard-rules.pro 파일에 추가하면 됩니다.

# Serializer for classes with named companion objects are retrieved using getDeclaredClasses
# If you have any such classes, replace the examples below with your own
-keepattributes InnerClasses # Required for getDeclaredClasses

-if @kotlinx.serialization.Serializable class
com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions
com.example.myapplication.HasNamedCompanion2
{
    static **$* *;
}
-keepnames class <1>$$serializer { # Using -keepnames is enough for the serializer() call to reference the class correctly
    static <1>$$serializer INSTANCE;
}
# Serializer for classes with named companion objects are retrieved using getDeclaredClasses
# If you have any such classes, replace the examples below with your own
-keepattributes InnerClasses # Required for getDeclaredClasses

-if @kotlinx.serialization.Serializable class
com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions
com.example.myapplication.HasNamedCompanion2
{
    static **$* *;
}
-keepnames class <1>$$serializer { # Using -keepnames is enough for the serializer() call to reference the class correctly
    static <1>$$serializer INSTANCE;
}

# Keep both serializer and serializable classes to save the attribute InnerClasses
-keepclasseswithmembers, allowshrinking, allowobfuscation, allowaccessmodification class
com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions
com.example.myapplication.HasNamedCompanion2
{
    *;
}

런타임에 절대 직렬화되지 않는 직렬화 가능 클래스들은, 더 좁은 클래스 스펙(class specifications)을 가진 커스텀 ProGuard 규칙으로 제외할 수 있어요.

객체를 JSON으로 직렬화하기

Kotlin에서는 kotlinx.serialization 라이브러리를 이용해 객체를 JSON으로 직렬화할 수 있어요.

클래스를 직렬화 가능하게 만들려면 @Serializable 어노테이션으로 표시해야 해요. 이 어노테이션은 컴파일러가 클래스 인스턴스를 직렬화·역직렬화하는 데 필요한 코드를 생성하도록 지시합니다. 자세한 내용은 @Serializable 어노테이션을 참고해 주세요.

예시를 하나 볼게요.

  1. 필요한 직렬화 라이브러리에서 선언을 임포트합니다.
import kotlinx.serialization.*
import kotlinx.serialization.json.*
  1. 클래스를 @Serializable로 표시해 직렬화 가능하게 만듭니다.
@Serializable
data class Book(val yearPublished: Int, val title: String)

@Serializable 어노테이션은 backing field를 가진 모든 프로퍼티의 기본 직렬화를 켜 줘요. 프로퍼티 레벨 어노테이션, 선택적 프로퍼티 등으로 직렬화 동작을 커스터마이즈할 수 있어요. 자세한 내용은 클래스 직렬화하기를 참고해 주세요.

  1. Json.encodeToString() 함수로 이 클래스의 인스턴스를 직렬화합니다.
// Imports declarations from the serialization and JSON handling libraries
import kotlinx.serialization.*
import kotlinx.serialization.json.*

// Marks the Book class as serializable
@Serializable
data class Book(val yearPublished: Int, val title: String)

fun main() {
    // Serializes an instance of the Book class into a JSON string
    val json = Json.encodeToString(Book(1937, "The Hobbit"))
    println(json)
    // {"yearPublished":1937,"title":"The Hobbit"}
}

그 결과로 이 객체의 상태를 JSON 형식으로 담은 문자열을 얻게 돼요: {"yearPublished":1937,"title":"The Hobbit"} 객체 컬렉션도 한 번의 호출로 직렬화할 수 있어요: val bookList = listOf(Book(1937, "The Hobbit"), Book(1867, "War and Peace")) 그다음 val jsonList = Json.encodeToString(bookList)처럼 쓰면 되죠.

객체를 JSON에서 역직렬화하기

역직렬화는 JSON 문자열을 다시 객체로 변환하는 과정이에요.

Kotlin에서 객체를 JSON에서 역직렬화하려면:

  1. 필요한 직렬화 라이브러리에서 선언을 임포트합니다.
import kotlinx.serialization.*
import kotlinx.serialization.json.*
  1. 클래스를 @Serializable로 표시해 직렬화 가능하게 만듭니다.
@Serializable
data class Book(val yearPublished: Int, val title: String)
  1. Json.decodeFromString() 함수로 객체를 JSON에서 역직렬화합니다.
// Imports declarations from the serialization and JSON handling libraries
import kotlinx.serialization.*
import kotlinx.serialization.json.*

// Marks the Book class as serializable
@Serializable
data class Book(val yearPublished: Int, val title: String)

fun main() {
    // Deserializes a JSON string into an instance of the Book class
    val obj = Json.decodeFromString<Book>("""{"yearPublished":1937, "title": "The Hobbit"}""")
    println(obj)
    // Book(yearPublished=1937, title=The Hobbit)
}

축하해요! 이제 객체를 JSON으로 직렬화하고, 다시 객체로 역직렬화하는 데 성공했어요.

더 알아보기

  • 프리미티브, 문자열 같은 기본 타입과 일부 표준 라이브러리 클래스를 직렬화하는 방법은 내장 타입 직렬화하기에서 배울 수 있어요.
  • 클래스 직렬화를 커스터마이즈하고 @Serializable 어노테이션의 기본 동작을 조정하는 방법은 클래스 직렬화하기에서 알아볼 수 있어요.
  • JSON 데이터 처리를 더 깊이 파고들고 JSON 직렬화를 구성하는 방법은 JSON 직렬화 개요에서 다뤄요.