면접 질문 목록으로 가기
면접 질문실전 질문꼬리 질문

안드로이드에서의 직렬화(Serialization) vs Parcelable 비교

skydovesJaewoong Eum (skydoves)||9분 소요

안드로이드에서의 직렬화(Serialization) vs Parcelable 비교

안드로이드는 객체를 전달 가능한 형태로 변환하기 위한 두 가지 메커니즘을 제공합니다. 하나는 Java 표준 라이브러리의 Serializable 인터페이스이고, 다른 하나는 안드로이드 고유의 Parcelable 인터페이스입니다. 두 방식 모두 IntentBundle을 통해 Activity, Fragment, Service 간에 객체를 전달할 수 있도록 해 줍니다. 하지만 변환을 수행하는 방식, 성능 특성, 그리고 각각이 적합한 상황이 근본적으로 다릅니다. 이 두 가지 방식의 차이를 제대로 이해하면 면접에서 깊이 있는 답변을 제시할 수 있으므로, 반드시 숙지하시길 권장합니다.

이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.

  • Serializable이 리플렉션(reflection)을 사용하여 객체를 변환하는 과정과, 이것이 성능에 미치는 영향을 설명할 수 있습니다.
  • Parcelable이 리플렉션 없이 Parcel에 직접 데이터를 기록하는 방식을 이해할 수 있습니다.
  • 두 방식의 가비지 컬렉션(GC) 오버헤드를 비교할 수 있습니다.
  • 성능 비용에도 불구하고 Serializable이 적합한 상황을 판별할 수 있습니다.
  • 코틀린의 @Parcelize 어노테이션을 활용하여 Parcelable 보일러플레이트 코드를 제거하는 방법을 익힐 수 있습니다.

Serializable과 리플렉션 기반 변환

Serializable은 Java 표준 라이브러리에서 제공하는 마커 인터페이스(marker interface)입니다. 구현할 때 별도의 메서드를 작성할 필요가 없으며, 런타임에서 리플렉션을 사용하여 객체의 필드를 검사하고, 값을 읽어 바이트 스트림(byte stream)에 기록합니다.

import java.io.Serializable

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

안드로이드가 Serializable 객체를 Bundle을 통해 전달할 때, 시스템은 ObjectOutputStream으로 직렬화하고 ObjectInputStream으로 역직렬화합니다. 이 과정에서 런타임은 클래스 계층 구조의 모든 필드를 리플렉션으로 순회하며, 중간 단계의 ObjectStreamClass 디스크립터를 생성하고, 임시 바이트 배열과 래퍼 객체를 할당하게 됩니다.

성능 비용은 크게 두 가지 원인에서 발생합니다. 첫째, 리플렉션은 직접적인 필드 접근보다 본질적으로 느립니다. JVM이 필드 메타데이터를 조회하고, 접근 권한을 확인하며, 타입이 지정된 접근자 대신 범용 접근자 메서드를 사용해야 하기 때문입니다. 둘째, 직렬화 과정에서 대량의 임시 객체가 생성되어 가비지 컬렉션 압박이 증가합니다. 안드로이드에서는 GC 일시 정지(pause)가 프레임 렌더링에 영향을 미칠 수 있으므로, 복잡한 객체 그래프에서 이러한 오버헤드는 체감할 수 있을 정도입니다. 실제로 면접에서도 이 GC 오버헤드에 대한 후속 질문이 빈번하게 출제되니, 반드시 숙지해 두시기 바랍니다.

아울러 Serializable은 클래스 계층 구조 전체를 처리합니다. data class가 다른 클래스를 상속하는 경우, ObjectOutputStream은 상속 체인을 따라 올라가면서 각 수준을 개별적으로 직렬화합니다. transient로 표시된 필드는 직렬화에서 제외되므로 영속화(persist)하고 싶지 않은 필드를 건너뛸 수 있지만, 이 제외 메커니즘은 개발자가 올바른 필드에 어노테이션을 붙이는 것을 기억해야 하므로 실수의 여지가 있습니다.

Parcelable과 직접 데이터 기록 방식

Parcelable은 안드로이드 고유의 인터페이스로, 클래스가 자체적으로 필드를 Parcel에 기록하고 동일한 순서로 읽어 들이도록 명시적으로 구현해야 합니다. 리플렉션을 전혀 사용하지 않습니다.

import android.os.Parcel
import android.os.Parcelable

data class User(val name: String, val age: Int) : Parcelable {
    override fun writeToParcel(dest: Parcel, flags: Int) {
        dest.writeString(name) // 문자열 직접 기록
        dest.writeInt(age) // 정수형 직접 기록
    }

    override fun describeContents(): Int = 0

    companion object CREATOR : Parcelable.Creator<User> {
        override fun createFromParcel(p: Parcel) =
            User(p.readString()!!, p.readInt())
        override fun newArray(size: Int) =
            arrayOfNulls<User>(size)
    }
}

이 면접 질문은 구독자 전용입니다

Dove Letter를 구독하시면 안드로이드, 코틀린 개발 관련 독점 면접 질문의 전체 내용을 볼 수 있습니다.

구독하기
면접 질문 목록으로 가기