Serializable과 Parcelable의 직렬화/역직렬화 방식에 대해..

devel_liz·2025년 1월 5일
1

일단 내가 이 포스팅을 쓴 이유는 이번에 플젝을 하면서 @SerializedName과 @Parcelize를 같이 쓴 코드가 있고 이렇게 써도 아무 문제가 없는지 궁금해져서 작성하게 된 포스팅입니도 🙏

내가 작성한 코드

@Parcelize
data class RouteResponse(
    @SerializedName("features")
    val features: List<FeatureEntity>
) : Parcelable

왜 위처럼 썼냐면
1. 해당 데이터를 bundle로 주고받을 일이 있어서 Parcelable@Parcelize를 사용함
2. RouteResponse를 API 통신을 통해 받아오고 있기 때문에 프로퍼티 매핑을 위하여 @SerializedName("features")을 사용함

결론: 찾아본 결과 함께 사용하는 것은 문제가 없다. 두 어노테이션은 서로 다른 용도로 사용되며 위 코드처럼 사용할 경우 각자 역할을 잘 수행할 수 있다.

그래도 이번 기회에 한번 더 직렬화/역직렬화에 대해 공부해보기로 함


Serializable에 대해 알아보자!

Serializable은 "직렬화 가능"이란 뜻이고 "시리얼라이져블"이라고 발음한다.

직렬화? 그게 뭔데.

데이터를 주고 받거나 저장할 때 일정한 형식으로 바꾸는 것을 뜻한다.

그러니까 어떤 것을 어떤 것으로 바꾼다는 건데...

자바에서 뜻하는 직렬화로 말하자면 객체를 바이트 스트림으로 변환하여 파일로 저장하거나 네트워크를 통해 전송할 수 있게 만드는 과정이다.
반대로, 바이트 스트림을 객체로 복원하는 "역직렬화" 라고 해!

(사진 출처 https://www.geeksforgeeks.org/serialization-in-java/)

왜 직렬화가 필요할까?

"바이트스트림이고 뭐고 그냥 저장하면 되는 거 아니야?"
"왜 꼭 변환해서 저장해야 돼?"
"왜 꼭 변환해서 데이터를 주고받아야 해?"

라고 생각할 수 있다.

그 이유는 우선 바이트 스트림은 어떠한 플랫폼에도 종속되지 않는다.
윈도우, 리눅스, 맥 등 서로 다른 환경에서도 같은 데이터를 해석할 수 있고
바이트 스트림은 저장 공간을 최소화하여 읽기/쓰기에도 빠르다!
또한 객체 구조 유지도 해준다. 속성 값, 참조 관계 등을 그대로 저장해준다.
네트워크로 데이터를 전송할 때는 표준적으로 바이트 단위를 사용하기 때문에 네트워크 통신도 수월해진다.

어떻게 사용하는데

Serializable은 Java 내장 지원을 하고 있고 Kotlin에서 사용할 경우에는 Kotlinx Serialization 라이브러리에서 제공하는 기능을 사용하면 쉽게 사용 가능하다.

나는 현재 kotlin을 주로 사용하고 있기 때문에 kotlin에서 사용하는 방법에 대해 알아보려고한다.



1. 일단, kotlinx.serialization 라이브러리 추가해줘야 한다.
프로젝트의 build.gradle에 아래 코드를 추가한다.

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




2. 그 다음 plugins 블록에도 kotlinx-serialization을 추가한다

plugins {
    kotlin("plugin.serialization") version "1.9.0" // 사용 중인 Kotlin 버전에 맞춰 설정
}




3. 다음은 직렬화가 필요한 class에 @Serializable 어노테이션 추가

import kotlinx.serialization.Serializable

@Serializable
data class Person(
    val name: String,
    val age: Int
)




4. JSON 직렬화 및 역직렬화도 가능하다
kotlinx.serialization.json.Json을 사용해 객체를 JSON으로 변환하거나 JSON을 객체로 변환할 수 있음

import kotlinx.serialization.*
import kotlinx.serialization.json.*

fun main() {
    val person = Person("Alice", 25)

    // 직렬화: 객체 -> JSON 문자열
    val json = Json.encodeToString(person)
    println("직렬화된 JSON: $json")

    // 역직렬화: JSON 문자열 -> 객체
    val deserializedPerson = Json.decodeFromString<Person>(json)
    println("역직렬화된 객체: $deserializedPerson")
}

직렬화된 JSON: {"name":"Alice","age":25}
역직렬화된 객체: Person(name=Alice, age=25)


지금까지 자바에서 말하는 Serializable에 대해 설명하고 사용법은 왜 코틀린에서 사용하는 Serializable로 알려줘요? 둘이 같은 거 아니에요?!

Java Serializable와 Kotlinx Serialization 비교

참고로 도입부에서 말한 바이트스트림으로 변환해주는 직렬화는 자바에서의 Serializable을 말한 거고,
kotlin에서 사용하는 Kotlinx Serialization와는 다르다. 밑에 표에서 살펴보자!


구분Java SerializableKotlinx Serialization
직렬화 포맷바이트 스트림JSON, ProtoBuf, CBOR 등 다양한 포맷 지원
구현 방식JVM에 종속적인 방식JVM 독립적으로 설계, 가볍고 빠름
사용성단순히 바이트 스트림 처리용다양한 포맷 지원 및 더 나은 유연성 제공
속도 및 성능비교적 느림최적화되어 있어 빠르고 가벼움
사용 대상파일 저장 및 네트워크 전송용주로 JSON, API 데이터 교환 등 현대적 사용 사례에 적합
추가 기능제한적 (transient 키워드 등)기본값 처리, @Transient, 다양한 포맷 지원 등 고급 기능 제공
의존성자바 기본 라이브러리 포함kotlinx.serialization 라이브러리 추가 필요




흠.. 그렇군
그럼 또

@Parcelize는 뭔데?

뭐 data class위에 어노테이션 갖다 붙이면 다 직렬화/역직렬화 알아서 시켜주는 것 같죠?
저도 뭣 모르고 사용하던 코흘리개 시절에는 둘의 차이점도 자세히 모르고 갖다 붙이면 알아서 변환해주것지.. 하고 썼슴미다..

둘 다 직렬화/역직렬화 해주는 건 맞는데 어떻게 해주는지, 그리고 어떤 상황에 사용하는지를 알고 써야 찐 개발자라고 할 수 있죠.. maybe


(저 아님)

@Parcelize는 번역하면 "소포화", 발음은 "파썰라이즈"이다.
@Parcelize는 Kotlin에서 Parcelable 인터페이스를 자동으로 구현해주는 어노테이션이다.
이 어노테이션을 사용하면, 직렬화/역직렬화 과정을 매우 간편하게 처리할 수 있다.

Parcelable은 또 뭔데요?

Parcelable은 Android 시스템에 최적화된 직렬화 방식이다.
즉, Android에서 객체를 Intent, Bundle 등을 통해 전달할 수 있도록 하는 인터페이스

@Parcelize를 함께 사용하면 Kotlin에서 Parcelable을 쉽게 구현할 수 있도록 도와준다.
@Parcelize를 사용하지 않을 경우 writeToParcel()describeContents() 메소드를 직접 구현해야 한다.


왜 Intent, Bundle 등을 통해 전달할 때는 Parcelable를 쓰라고 하는 거지?

Parcelable을 Intent, Bundle 등을 통해 객체를 전달할 때 사용하는 것이 권장되는 이유는 성능과 효율성 때문이다.
Serializable도 직렬화 방식으로 사용할 수 있지만, Parcelable은 Android에서 더 최적화된 방식으로 데이터를 전달하는 데 유리하다.


  • 성능(속도)이 뛰어난 이유
    Parcelable은 Android 시스템에 맞게 최적화되어 있어서, 객체를 Parcel에 직렬화하고 이를 IntentBundle로 전달할 때 훨씬 더 빠르고 메모리 효율적입니다.



    잠깐만.. Parcel은 또 뭐야?
    ( Parcel에 대해 궁금하다면 ? => https://developer.android.com/reference/android/os/Parcel )


Pacel은 Android 플랫폼에 특화된 클래스이고, 직렬화된 데이터를 담는 컨테이너인데
Parcel을 사용할 수 있는 것이 바로 Parcelable이다.
SerializableParcel을 사용할 수 없다..!!

즉, Parcelable은 Android의 Parcel 구조에 맞게 설계되어 있어, 객체를 전달하는 데 필요한 메모리 양이 적다.
이로 인해, 대규모 데이터를 처리할 때 메모리 소비가 적고 효율적이다.

Parcelable 작동방식

  1. 데이터를 직렬화/역직렬화 해서 Parcel에 저장한다.
  2. 그리고 Intent안에 Parcel 객체를 담아 ActivityService에 전달하고, 이 객체는 Intent가 도착한 곳에서 Parcel을 역직렬화하여 다시 원래의 객체로 복원할 수 있다.


📌 결론: Intent를 통해 데이터 전달 시 Parcelable은 Parcel 에 담아 전달! Serializable은 바이트스트림으로 변환하여 전달!


정리해 봅시다

  1. Serializable과 Pacelable은 어떤 용도로 사용하나요?
    -> 객체 또는 데이터를 직렬화/역직렬화할 때 사용하는 인터페이스입니다.

  2. 자바에서의 직렬화가 뭔가요?
    -> 객체 또는 데이터를 외부 시스템에서도 사용할 수 있도록 byte-stream 형태로 변환하는 기술을 의미합니다.

  3. Serializable은 어떻게 직렬화시키나요?
    JVM이 자동으로 ObjectOutputStream을 통해 객체의 필드를 바이트 스트림으로 변환합니다.

  4. Pacelable은 어떻게 직렬화시키나요?
    -> Android에서 객체 데이터를 Parcel에 쓰고 읽는 방식으로 직렬화를 수행합니다.

  5. Serializable이 Pacelable에 비해 성능이 떨어진다는 말이 나오는 이유가 뭘까요?
    -> Serializable은 리플렉션을 통해 객체의 메타데이터를 동적으로 검색하고 직렬화/역직렬화 하는 반면, Pacelable은 리플렉션을 사용하지 않고 필요한 필드만 개발자가 명시적으로 Parcel에 어떤 데이터를 저장할지, 그리고 어떻게 복원할지를 정의합니다.

  6. 리플렉션이 뭔가요?
    -> 런타임에 클래스, 메서드, 필드 등의 정보를 동적으로 탐색하고 수정할 수 있는 기능입니다.




추가로 SerializableParcelable에 대해 이해하기 쉽게 정리된 영상 자료 및 글을 첨부합니다

끝으로..!

본 글에서 다른 의견이 있거나 추가적으로 공유하고 싶은 내용이 있다면 댓글로 남겨주시면 저에게 엄청난 도움이 됩니다

profile
Android zizon

1개의 댓글

comment-user-thumbnail
2025년 1월 5일

딥하게 공부하시는 모습이 멋있네요 ㅠㅅ ㅜ

답글 달기

관련 채용 정보