코틀린 class 의 properties 개수가 많고, 이의 fields 가 반복되는 규칙의 이름을 가진 경우, objectMapper 를 통해 쉽게 값을 사용할 수 있다.

어떤 경우에 이런 기능이 필요할까? 필자가 개발하다 보니, 인수인계 받은 코드의 item class 의 프로퍼티가 매우 많았다. item01 ~ item35 까지 있었다. 이렇게 되면 한 눈에 클래스를 파악하기도 힘들었다. 설상가상으로, item class 는 entity 였기 때문에 덩달아 service layer 의 코드도 길어졌다.

이런 redundancy 를 줄이기 위해 ITEM01, ITEM02, … 대신에 listOfItems 라는 list 로 대체하는 작업을 했다. 그때 유용하게 사용한 것이 ObjectMapper 의 convertValue 함수다.

먼저 간단한 사용법을 알아보자. 아래와 같이 properties 가 redundant 한 class 는 objectMapper 를 사용해 Map 으로 변환할 수 있다.

class Item(
    val someProperty: String,
    val item01Name: String,
    val item01Value: Int,
    val ITEM02Name: String,
    val ITEM02Value: Int,
    val ITEM03Name: String,
    val ITEM03Value: Int
)

fun main() {
    val objectMapper = ObjectMapper()
    val item = Item("some", "name01", 1, "name02", 2, "name03", 3)
    val mappedPropertyInItem = objectMapper.convertValue(item, MutableMap::class.java)
    println(mappedPropertyInItem)
}

출력 결과는 아래와 같다.

{someProperty=some, item01Name=name01, item01Value=1, item02Value=2, item03Name=name03, item02Name=name02, item03Value=3}

클래스의 객체의 프로퍼티를 호출하고 싶을 때 objectMapper 를 사용해 간편하게 호출할 수 있다. 주의할 점은, 모든 key 가 lowercase 라는 것이다. 이는 출력 결과에서도 확인할 수 있다.

fun main() {
    val objectMapper = ObjectMapper()
    val item = Item("some", "name01", 1, "name02", 2, "name03", 3)
    val mappedPropertyInItem = objectMapper.convertValue(item, MutableMap::class.java)

    println(mappedPropertyInItem["ITEM02Name"])
}

즉, 아래와 같이 null 을 출력한다.

null

이제 구체적인 응용법을 알아보자. 먼저 Item class 를 대체할 NewItem class 를 정의한다.

class NewItem(
    val someProperty: String,
    val listOfItems: List<ItemMap>
)

class ItemMap(
    val itemName: String,
    val itemValue: Int
)

이제 objectMapper 를 이용해 Item class object 를 NewItem class object 로 변환하자.

fun main() {
	val objectMapper = ObjectMapper()
    val item = Item("some", "name01", 1, "name02", 2, "name03", 3)
    val mappedPropertyInItem = objectMapper.convertValue(item, MutableMap::class.java)

    val listOfItemMaps =

    	// item01 부터 item03 까지를 표현
        (1..3)
            .map { number ->
                number.toString().padStart(2, '0')
            }
            .map { numberWithPadding ->
                val itemName = mappedPropertyInItem["item${numberWithPadding}Name"].toString()
                val itemValue = mappedPropertyInItem["item${numberWithPadding}Value"].toString().toInt()
                ItemMap(itemName, itemValue)
            }

    val newItem = NewItem(item.someProperty, listOfItemMaps)

    println(newItem)
}
NewItem(someProperty='some', listOfItems=[ItemMap(itemName='name01', itemValue=1), ItemMap(itemName='name02', itemValue=2), ItemMap(itemName='name03', itemValue=3)])