반응형

아마 REST API를 구현해보신 분이라면 JSON 데이터를 다뤄보신 경험이 있으실 겁니다.

개발을 하다보면 JSON 이나 XML 등을 다뤄야하는 경우가 자주 발생하는데요.

기본적으로 JSON 이나 XML 은 JVM 언어에 정의되어 있는 타입이 아니기 때문에 별도의 변환 작업과 파싱 작업을 해주어야 합니다. 이 과정에서 행해지는 변환 작업을 직렬화(Serialization)/역직렬화(Deserialization) 라고 합니다.

 

대표적인 Converter Library 로는 Gson, Jackson, Moshi 등이 있습니다. 그리고 이는 모두 Java 언어로 개발되어 있습니다. Java로 구현되어 있다해서 코틀린에서 사용하지 못하는건 아니기 때문에 코틀린에서도 역시 이 라이브러리를 사용하고 계신 분들이 굉장히 많은데요. 그러나 완벽하게 호환된다고 하기에는 약간 아쉬운 부분들이 있습니다.

 

어떤 부분이 아쉬운지 코틀린의 data class 를 예로 들어보겠습니다.

먼저, User 라는 이름의 data class 를 정의해줍니다.

data class User (
    val name: String,
    val email: String,
    val age: Int = 20
)

 

아시다시피 코틀린의 data class 는 default value가 설정 가능하여, User 객체를 생성할 때 생성자에 age에 대한 값을 생략할 수 있고, 생략할 경우에 age 변수에는 기본 값으로 설정한 20이 설정됩니다. (자바에는 이런 문법이 없습니다.)

 

이 User 클래스를 두고 Gson과 Kotlinx.Serialization 의 결과를 비교해보도록 하겠습니다.

 

Gson

먼저 Gson을 통해 변환해보도록 하겠습니다.

fun main() {
    val jsonString = """
            {
                "name" : "Ready Kim",
                "email" : "ready.kim@gmail.com"
            }
        """.trimIndent()

    val user = Gson().fromJson(jsonString, User::class.java)

    println(user)
}

 

간단하게 JSON을 구성해서 "name" 과 "email" 필드만 지정해주고서 User 클래스로 변환하도록 코드를 작성해봤습니다.

위 코드를 실행하면 결과는 다음과 같이 나옵니다.

 

User(name=Ready Kim, email=ready.kim@gmail.com, age=0)

 

우리는 분명히 위에서 User 클래스를 작성할 때 age 프로퍼티의 default value로 20을 설정해줬습니다만, 변환된 결과를 보시면 제대로 설정이 안 된것을 확인할 수 있습니다.

 

즉, Gson은 코틀린의 default value 문법을 무시해버립니다. primitive 타입의 필드에 대해서는 0, refernce 타입의 필드에 대해서는 null 값을 기본으로 합니다. 주의할 점은 gson 컨버터는 코틀린의 null-safety 를 준수하지 않기 때문에 이에 대한 고려를 하지 않으면 Crash를 발생하게 됩니다.

그렇기 때문에 코틀린에서 Gson, Jackson, Moshi 등을 사용할 때는 각 필드를 nullable 하게 설정해줘야 합니다.

 

Kotlinx.Serialization

이번에는 코틀린에서 공식적으로 제공하고 있는 Kotlinx.Serialization 라이브러리를 사용해보겠습니다. 

일단 Kotlin Serialization 은 코틀린 언어를 만든 JetBrains 사에서 만들었으며, 다른 컨버터 언어들과는 다르게 Reflection을 사용하지 않고 개발한 라이브러리입니다. 리플렉션을 사용하지 않았기 때문에 성능상에서도 다른 컨버터 라이브러리에 비해 경쟁력을 갖습니다.

 

Kotlinx.Serialization의 사용법은 굉장히 간단합니다.

그냥 변환하고자 하는 클래스에 @Serializable 어노테이션만 추가해주면 됩니다. 이 어노테이션을 부착하고 나면 해당 클래스의 Companion Object의 serializer() 함수를 사용할 수 있게 됩니다. 그리고 이 함수가 반환하는 KSerializer 타입의 객체를 직렬화 작업에 사용하게 됩니다.

 

백문이 불여일코! 한 번 코드를 작성해보도록 하겠습니다.

 

Kotlin Serialization을 사용하기 위해서는 디펜던시를 추가해줘야 하는데, 이 부분은 Document를 참고하세요.

 

준비가 되었다면 코드를 작성해보겠습니다.

@Serializable
data class User (
    val name: String,
    val email: String,
    val age: Int = 20
)

fun main() {
    val jsonString = """
            {
                "name" : "Ready Kim",
                "email" : "ready.kim@gmail.com"
            }
        """.trimIndent()

    val user = Json.parse(User.serializer(), jsonString)

    println(user)
}

 

말씀드린것처럼 Kotlinx Serialization은 리플렉션을 사용하지 않기 때문에 User::class.java 타입의 파라미터를 사용하지 않았습니다.

실행 결과는 다음과 같습니다.

 

User(name=Ready Kim, email=ready.kim@gmail.com, age=20)

 

자! 이번에는 age에 20이 성공적으로 설정된 것을 확인할 수 있습니다.

 

글의 서론에서 언급한 것처럼 이런 컨버터 라이브러리는 REST API를 사용할 때 자주 필요로 하게 됩니다. 따라서, HTTP 통신에 사용되는 대표적인 라이브러리인 Retrofit 과 함께 사용하는 경우가 많은데요. 세계적인 네임드 개발자 Jake Wharton 형님이 Retrofit 과 호환되도록 Converter를 추가해주셨습니다. 문서를 참고하셔서 Retrofit 객체를 생성하실 때 Converter Factory 를 설정해주시면 레트로핏과도 함께 사용할 수 있습니다.

 

추가로, 직렬화의 대상이 되는 클래스에 @Optional, @Transient 등의 어노테이션을 사용하면 보다 풍부하게 사용할 수 있습니다.

 

위에서 작성된 코드는 Github 저장소에서 확인할 수 있습니다.


참고

https://medium.com/@jurajkunier/kotlinx-json-vs-gson-4ba24a21bd73

반응형
반응형