현재 다니고 있는 회사의 앱은 상당히 오래전에 외주를 받아 만들어진 앱이다. 당연히 이해할 수 없는 구조들도 많고, 자바로 만들어진 앱을 Kotlin 으로 마이그레이션을 하는 와중에 JSONObject 객체를 없애고 각각 데이터클래스로 객체화 하자고 하여 이를 작업하고 있었다.
아마 최근 안드로이드 공부를 시작했거나, 이제 막 배우기 시작한 사람들이라면 JSONObject를 아마 못봤을 수 있고, 굳이 요즘은 활용해야 할 이유도 없지만... 이것이 무엇이고, 왜 리펙토링해야 하는지 아래와 같이 살펴보자.
AnalyticsWebInterface.IOT_PASS_BEACON ->
try {
MyBeaconManager.getInstance().
setBeaconFromJsonObject(JSONObject(message.obj.toString()))
//message.obj.toString는 Json 형태의 데이터
} catch (e: Exception) {
e.printStackTrace()
}
위의 코드를 보다시피 setBeaconFromJsonObject 함수는 파라미터로 JSONObject를 받고, JSONObject는 파라미터로 Json타입의 String 타입을 파라미터로 받는다.
즉, Json String타입의 데이터를 받는데, JSONObject로 위와같이 처리하면, 해당 JSONObject에 어떤 데이터가 들어있는지, 코드상으로 알기가 상당히 어렵다. 이를 가공하는 과정도 어렵고, 어떤 데이터를 put 또는 get을 해야하는지도 알 수가 없다. 구조도 알기 어렵다....
결과적으로 우리는 Json타입의 데이터를 data class로 객체화 한다면, 객체화된 데이터는 우리가 어떤 구조로 되어있는지, 해당 data class 객체의 어떤 파라미터를 어떻게 활용 또는 가공해야할지 훨씬 더 쉽게 알 수 있다.
data class IotPassBeaconData(
val type: String,
val data: List<BeaconInfo?>
)
data class BeaconInfo(
var recv_uuid: String? = "",
var send_uuid: String? = "",
var major: Int = 0,
var minor: Int = 0,
var power: Int = 0,
var main: Boolean = false,
var lastDetectTime: Long = 0
)
message.obj.toString()의 json 데이터를 역직렬화 하면 위와같은 데이터클래스 구조를 가진다. 보다시피 어떤 데이터를 담고있는지도 훨씬 보기 쉽고, 필요한 데이터가 있다는 이를 확인하여 비교하고 가공하기도 훨씬 쉽다. JSONObject를 리펙토링 해야 하는 이유가 이와같고, 아마도 불편하다고 느끼는 사람들도 꽤 많을 것이다.
그렇다면 추가적으로 이런 의문이 들 수도 있다.
만일 json데이터가 위와같은 구조로만 전송되지 않고, 다양한 구조로 전송되는 경우도 있을텐데, 이런건 어떻게 처리하나?
실제로 JSONObject를 리펙토링 하는 과정에서 단일한 구조로 전송되는 경우도 있지만, 하나의 데이터 처리 분기에서 다양한 구조로 전송받는 경우도 있었고, 이전에 해당 앱을 제작한 프리랜서 개발자는 별도의 로직을 만들어, 해당 JSONObject에서 key의 유무를 통해 데이터를 구분하여 분기 처리를 하였다.
(JSONObject의 key는 data class의 생성자 파라미터명이라고 생각하면
된다.)
나도 실제로 특정 생성자 파라미터 유무를 통해 2가지 data class로 분기처리를 하도록 구현은 하였다.
문제는 2가지 뿐만 아니라, json데이터 그 자체에 대한 다양한 유효성을 어떻게 처리하느냐도 중요하고, 다양한 형태의 json 데이터를 어떻게 가공할지, 혹은 다른 구조로 전달받은 데이터를 어떻게 처리할지 등등 추가적인 고민이 있었고, 이는 2부에서 추가로 다루도록 하겠다.