Moshi

KSang·2024년 4월 23일
0

TIL

목록 보기
89/101

OkHttp, retrofit을 사용하면 안드로이드에서 Json파일을 직렬화 해서 사용해야한다.

그럴때 컨버트 라이브러리로 주로 Gson을 사용했는데, 요즘 잘 안쓴다고 한다.

20년도에 Jake Wharton이 말하길 개발자 3명중 2명은 Gson은 deprecate되었다고 한다.

현재는 Moshi를 사용한다고 하는데, 그 이유는 뭐고 Gson은 왜 안쓰일까?

서로의 장단점을 정리해보자

Gson의 단점

❌ 코틀린 친화적이지 않다, null-safety같은 코틀린의 특징을 준수하지 않고 업데이트 또한 더딘 편이다.
가장 최신 버전이 2.10.1버전 인데, 23.01.07에 나오고 이후 업데이트가 되지 않았다.
https://github.com/google/gson

❌ 메소드 수 또한 Moshi에 비해 거의 2배정도 차이 난다. (20년도 기준)
그렇기 때문에 인터페이스가 복잡해지고, 성능 등 문제가 발생한다.

❌ 용량 또한 차이가 나는대, Gson은 APK에 277KB (2.10.1 버전 기준), Moshi는 158KB ,코틀린 확장시 +19.8KB 177.8kb가 된다(1.15.1 버전 기준).
https://central.sonatype.com/

❌ Gson은 리플렉션을 사용하여 JSON 문자열을 직렬화/역직렬화할 수만 있다.
이게 왜 문제 냐면, 리플렉션은 런타임에 클래스의 메타데이터를 분석하기 때문에, 컴파일 시간에 코드를 최적화하는 것보다 성능이 떨어진다.
또한 프로가드 같은 코드 축소 및 최적화가 힘들며, 보안또한 문제가된다.
Moshi같은경우 Kotlin과 함께 사용할 때 코드 생성방식을 제공한다.

❌ Gson은 필드에 대한 기본값을 지원하지 않는다. 응답 Json 문자열에 필드가 누락되고 해당 기본값을 null이 아닌 다른 값으로 설정해도 null이 된다.

Moshi

✔️JSON에서 데이터를 읽을 때, 예를 들어 색상 코드를 숫자로 바꿔야 하는 등의 특별한 처리가 필요한 경우, 그 처리 방식을 정의하는 '맞춤형 태그'를 만들어 코드에 붙일 수 있다.

// 커스텀 어노테이션 정의 예
annotation class FromHexColor

// 사용 예
data class ThemeSettings(
    @FromHexColor val backgroundColor: Int // JSON에서 "#FFFFFF" 같은 문자열을 Int로 자동 변환
)

✔️덜 반복되는 코드를 작성할 수 있도록 하는 깔끔한 기능들을 제공한다.
지연 로딩 어댑터를 사용할 수 있는데,
지연 로딩 어댑터는 애플리케이션의 성능을 최적화하고, 불필요한 메모리 사용을 줄이는 데 도움을 준다.

예를 들어, 애플리케이션에서 사용자의 프로필 데이터를 JSON형식으로 서버에서 받아오지만, 사용자가 프로필 페이지를 열 때까지 프로필 데이터를 파싱할 필요가 없는 경우, 사용자가 실제로 프로필페이지를 열 때까지 데이터 파싱을 연기할 수 있다.

// Moshi 인스턴스 생성
val moshi = Moshi.Builder().build()

// 지연 로딩 어댑터 생성
val adapter = moshi.adapter(Profile::class.java).lazy()

val profile = adapter.fromJson(data)

✔️오류메시지또한 장점이다.
Moshi는 오류 발생 시 더 이해하기 쉬운 직렬화 실패 메시지를 제공한다.
앱이 배포된 후 이상한 스택 트레이스를 받았을 때 원인을 파악하기 더 쉽게 해준다.

✔️newBuilder() API를 통해 기존 어댑터를 기반으로 새 어댑터를 생성할 수 있다.
OkHttp또는 Okio빌더와 유사하며, 별도의 어댑터를 생성함으로 하나의 큰 어댑터가 수천개의 모델을 파싱하는걸 방지할 수 있다.

✔️Moshi는 다형성 데이터 타입을 내장 지원하며, 알려지지 않은 타입에 대해서도 폴백 지원을 한다.
다형성 데이터 타입 지원은 서로 다른 타입의 객체를 동일한 상위클래스나 인터페이스로 처리할 수 있게 해주는 기능이다.

예를 들어, Animal 이라는 인터페이스가 있고 DogCat이라는 두개의 구현체가 있을때, Moshi는 Json 데이터를 보고 어떤 타입(Dog 혹은 Cat)으로 객체를 생성할지 결정할 수 있다.

✔️폴백지원은 Moshi가 처리할 수 없는 타입이 나타났을 때 대체할 수 있는 기본 동작이나 객체를 제공하는 기능이다.

예를 들어, Animal 타입의 JSON 배열을 파싱하는 중에 typeDogCat이 아닌 Bird로 설정된 객체를 발견했을때, bird에 대한 처리 로직이 없다면 폴백 객체로 정의된 UnknwnAnimal클래스의 인스턴스를 생성할 수 있다.

이렇게 하면 프로그램이 예외 없이 계속 실행되게 된다

✔️Moshi는 코틀린용 Code-gen 어댑터가 있다.
주석을 사용해 직렬화/역직렬화 과정을 더욱 빠르게 만들 수 있다. Gson이 사용하는 리플렉션 방식을 우회하는 방법인데, 런타임에서 분석하는 리플렉션 방식과 달리
컴파일 시점에서 확인하기 때문에 처리속도가 빠르고 타입안전성이 높아지며, 프로가드 같은 도구로 축소 및 최적화하기 용이 하다.


Moshi는 Gson보다 훨씬 빠르고 메모리를 덜 사용한다.
Okio를 사용하기 때문인데, retrofit또한 내부적으로 Okio를 사용하고 있다.
그렇게 되면 Moshi와 Retrofit는 사용하는 버퍼를 공유하게되고, 네트워크 호출과 응답을 직렬화 하는 동안 메모리 소비가 훨씬 줄어든다.

💻사용 방법

/*Moshi*/
def moshiVersion = "1.15.1"
implementation("com.squareup.moshi:moshi:$moshiVersion")
kapt("com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion")
implementation("com.squareup.retrofit2:converter-moshi:2.11.0")
@JsonClass(generateAdapter = true)
data class Animal(
	@Json(name = "dog")
    val dog: String,
    
    @Json(name = "cat")
    val cat: String,
    
    @Json(name = "UnknwnAnimal")
    val UnknwnAnimal: String,
)
@Singleton
@Provides
fun providesMoshi() = Moshi.Builder().build()
@Singleton
@Provides
fun provideRetrofit(okHttpClient: OkHttpClient, mosh: Moshi) = Retrofit.Builder()
	.client(okHttpClient)
    .addConverterFactory(MoshiConverterFactory.create(mosh))
    .baseUrl(BASE_ENDPOINT)
    .build()

1개의 댓글

comment-user-thumbnail
2024년 7월 25일

This is a great breakdown of Moshi vs Gson! I've been using Gson for a while, but the limitations you mentioned, like slow updates and lack of Kotlin support, make Moshi sound like a much better Slope Game option. The custom tag functionality and lazy loading adapters seem particularly helpful.

답글 달기