이 글의 원문은 여기서 확인할 수 있으며 블로그 리뷰에는 김태호(Google)님이 참여해주셨습니다.
지금까지 DataStore를 프로토콜 버퍼(Protocol Buffer) 또는 Preferences와 함께 사용하는 방법을 공유해 드렸습니다. 실제로는 두 DataStore 버전 모두 프로토콜 버퍼를 사용하여 데이터를 직렬화합니다. 이 뿐만 아니라, Kotlin Serialization과 커스텀 데이터 클래스(data class)를 활용하여 DataStore를 사용할 수도 있습니다. 그러면 데이터를 위한 스키마(schema)를 계속 제공함과 동시에, 프로토콜 버퍼 라이브러리를 사용 혹은 학습하지 않아도 번잡한 코드(boilerplate code)를 줄일 수 있습니다.
수행해야 할 작업은 다음과 같습니다.
- 데이터 클래스 정의하기
- 데이터 클래스의 불변(immutable) 확인하기
- Kotlin Serialization을 사용하여 DataStore Serializer를 구현합니다.
- DataStore Serializer 사용합니다.
데이터 클래스 정의
Kotlin 데이터 클래스는 Kotlin Serialization과 원활하게 작동하므로, DataStore와 함께 사용하기에 매우 적합합니다. DataStore를 사용하려면 equals와 hashCode 메서드가 필요한데요, 데이터 클래스는 이를 자동으로 생성해줍니다. 이 뿐만 아니라, 데이터를 디버깅하고 업데이트하는 데 유용한 toString 함수와 copy 함수도 생성해줍니다.
https://gist.github.com/rohitsat13/3d69af889f718cd5c265bc433b15aa88
데이터 클래스의 불변(immutable) 확인하기
DataStore는 가변(mutable) 유형과 호환되지 않으므로, 반드시 불변(immutable) 형태의 클래스를 사용해야 합니다. 만약 가변 유형 클래스를 사용하는 경우, 버그와 경쟁 상태(race conditions)를 포착하기 어려워집니다.
하지만, 데이터 클래스라고 해서 반드시 변경 불가능한 것은 아닙니다.
var는 변경 가능하므로, val을 대신 사용해야 합니다.
https://gist.github.com/rohitsat13/03fb3811251db0818d96436b059a1f86
배열(arrays)은 변경 가능하므로, 배열을 노출하면 안 됩니다.
https://gist.github.com/rohitsat13/db8142ea04bc2e67afd8375d30c787c0
읽기 전용 List를 데이터 클래스의 멤버로 사용하더라도 클래스는 변경될 수 있습니다. 그러므로 그 대신 변경 불가능(immutable)하거나 영속적인 컬렉션(persistent collections)의 사용을 고려해야 합니다.
https://gist.github.com/rohitsat13/6eac5886bf05931add6f4debb5a01fd9
데이터 클래스 멤버로 가변 유형 클래스를 사용하는 경우, 클래스가 변경될 수 있습니다. 그러므로 반드시 모든 멤버가 변경 불가능한 유형이 되도록 해야 합니다.
https://gist.github.com/rohitsat13/1896fa8a799403bf780c78ae645d8af9
DataStore Serializer 구현
Kotlin Serialization은 JSON 및 프로토콜 버퍼를 포함한 여러 형식을 지원합니다. 무척 흔하고 사용하기 쉬울 뿐 아니라 일반 텍스트로 저장되어 디버깅하기도 쉽기에, 저는 JSON을 사용하려 합니다. 더 작고 빠르며 protobuf-lite와 호환되는 프로토콜 버퍼를 선택하는 것도 좋습니다.
Kotlin Serialization을 사용하여 데이터 클래스를 읽고 JSON에 쓰려면 @Serializable로 데이터 클래스에 주석을 달고 Json.decodeFromString<YourType>(string) 및 Json.encodeToString(data).를 사용해야 합니다. 다음은 UserPreferences를 사용하는 예입니다.
https://gist.github.com/rohitsat13/137270aac38bf01d788d3ffccdd60f28
⚠️ Android 버전 간에 데이터 형식이 바뀔 수 있으므로 Parcelable을 DataStore와 함께 사용하기에는 안전하지 않습니다.
Serializer 사용
DataStore를 만들 때, 앞에서 만든 Serializer를 넣어줍니다.
https://gist.github.com/rohitsat13/1dc3f56de1e5b131aca628bb2a5224a6
프로토콜 버퍼에서 데이터를 읽는 방식과 동일해 보입니다.
https://gist.github.com/rohitsat13/1bf30e6da22072fd52ab8554e8a8f54f
생성된 .copy() 함수를 사용하여 데이터를 업데이트할 수 있습니다.
https://gist.github.com/rohitsat13/edc2bcaec09d55e37c45a2be4d3afaf6
결론
Kotlin Serialization과 데이터 클래스와 함께 DataStore를 사용하면 번잡한 코드(boiler plate)를 줄이고 코드를 간결하게 만들 수 있지만. 하지만, 변경 가능성(mutability)을 통해 버그가 발생하지 않도록 주의해야 합니다. 데이터 클래스를 정의하고 Serializer를 구현하기만 하면 되므로, 여러분도 직접 시도해 보세요!
DataStore에 관해 더 자세히 알아보려면 저희 팀에서 작성한 문서를 확인해 보시고, Proto DataStore 및 Preferences DataStore 코드랩을 통해 경험을 쌓아보세요.