GsonConverterFactory 와 Kotlin Serialization Converter 같이 쓰기

이창민·2023년 2월 26일
0

같이쓰게 된 배경..

우선 회사에서 api 통신시 null 값이 오는 부분들이 좀 있었고 null로 인해 굉장히 불필요한 코드들이 적히는 것이 싫었다.. 우선 null 체크같은 것들 이런 것들이 기본적으로 보일러 플레이트가 되었다.

또한 이제 Gson도 잘 안쓰고 Moshi를 더 쓴다는 것을 알았고,, 이 Gson 을 이제 어떻게 할까 고민을 하는 계기가 되었다.

고민을 하는 도중 Kotlin Serialization 이 있는 것을 알게되었고, 성능도 Gson 보다 좋고 나쁘지 않은 것 같아 이를 적용해보았다.

Gson의 단점?..

우선 retrofit의 컨버터를 GsonConverterFactory를 사용하는 경우를 예로 들면

우리가 만약 데이터 클래스를 아래와 같이 구현하고,

data class ServerResponseData(
	@SerializedName("NAME")
    val name: String = "CHANGMIN",
    @SerializedName("DESCRIPTION")
    val description: String = "Junior Android"
)

서버에서 응답 데이터가 아래와 같이 왔다면

	{
    	"NAME": "NAME",
        "DESCRIPTION": null
    }

ServerResponseData클래스에서 default value를 설정했더라도 description은 null 값이 된다.

이런 상황에서 해당 모델의 값들을 사용한다면 거의 모든 대다수의 상황에서 아래와 같은 일이 일어날 것이다.

	otherName = name ?: ""
    otherDescription = description ?: ""

?: "" 를 매번 사용 하다보니 보일러 플레이트가 발생한다..

Kotlin Serialization

만약 ServerResponseData를 아래와 같이 작성했다면 어떻게 될까

@Serializable
data class ServerResponseData(
	@SerialName("NAME")
    val name: String = "CHANGMIN",
    @SerialName("DESCRIPTION")
    val description: String = "Junior Android"
)

Json의 Description 값이 null 이지만 Junior Android 로 값이 설정된 것을 확인할 수 있을 것이다.

그러면 같이 사용해보자.

기존 구조에서 Retrofit의 Converter를 GsonConverterFactory를 사용하고 있었으니..
갑자기 Kotlin Serialization Converter 만 사용하는 것으로 모든 코드를 변경하면
무척 힘들고.. 어디서 터질지 모르는 오류가 팡팡 터질 수 도 있으니 점진적으로 적용하고자
Kotlin Serialization Converter 를 같이 사용하기 위해 CustomConverterFactory를 만들어 준다.

class CustomConverterFactory @Inject constructor(
    private val gsonConverterFactory: GsonConverterFactory,
    private val kotlinSerializationConverterFactory: Converter.Factory
) : Converter.Factory() {
    override fun requestBodyConverter(type: Type, parameterAnnotations: Array<out Annotation>, methodAnnotations: Array<out Annotation>, retrofit: Retrofit): Converter<*, RequestBody>? {
        return kotlinSerializationConverterFactory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit)
    }

    override fun responseBodyConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? {
        return when {
            annotations.any { it.annotationClass == KotlinSerialization::class }
            -> {
                kotlinSerializationConverterFactory.responseBodyConverter(type, annotations, retrofit)
            }
            else -> {
                gsonConverterFactory.responseBodyConverter(type, annotations, retrofit)
            }
        }
    }
}

responseBodyConverter를 한번 살펴보자.
gson 혹은 KotlinSerialization 을 사용한다.

@KotlinSerialization 어노테이션을 만들고
API 함수에 KotlinSerialization의 어노테이션을 붙여
annotations.any { it.annotationClass == KotlinSerialization::class } 부분을 통해 KotlinSerialization 부분 어노테이션이 존재하는 함수 호출시에만 KotlinSerializationConverter를 사용할 수 있도록 했다.

이러면 API 함수는 아래처럼 작성되면 된다.

interface ApiInterface {
	@GET("calls/test")
    @KotlinSerialization
    fun getTest(...): Call<ServerResponseData>
}

@KotlinSerialization 어노테이션이 붙은 함수들은 전부 KotlinSerializationConverter를 통해 역직렬화가 이뤄지는 것을 확인할 수 있을 것이다.

이제 점진적으로.. 기존 코드를 조금씩 바꿔가고, 추가되는 api에는 위 어노테이션을 붙여 KotlinSerializationConverter 를 사용할 수 있다

주의사항

KotlinSerialization은 Ktype 을 처리할 때는 문제가 없지만. 만약 서버에서 다른 type 들을 내려준다면 변환하는 Serializer 를 만들거나 다른 방법을 찾아봐야한다.

또한 별 것 아니지만 릴리즈모드에서 프로가드를 사용한다면 프로가드에 KotlinSerialization 깃허브에 들어가서 넣어야하는 것을 꼭 넣어주자.

참고문서

  1. https://blog.mathpresso.com/%EC%8B%A0%EC%9E%85-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-kotlinx-serialization-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%84%9C%EC%82%AC%EC%8B%9C-740597911e2e
  2. https://github.com/Kotlin/kotlinx.serialization
  3. https://github.com/JakeWharton/retrofit2-kotlinx-serialization-converter
profile
android 를 공부해보아요

0개의 댓글