1 분 소요

문제 상황

data class의 copy()를 통해서 객체가 가진 값을 아주 편리하게 그대로 가져오고 원하는 값을 설정해줄 수 있다.
작업했던 로직에서는 일련의 단계를 거치면서 객체의 값을 업데이트 해주고 있었는데, 이 과정에서 copy()를 무진장 쓰고있었다.
클래스에서는 backing field를 갖고 있는 상황이었고, 객체를 생성자로 바로 생성하지 않고 of()로 생성해주었었다.

data class Member(
    val id: Long,
    val name: String,
) {
    var nickName: String = ""
        private set
    
    companion object {
        fun of(id: Long, name: String, nickName: String): Member {
            return Member(id, name).apply {
                this.nickName = nickName
            }
        }
    }
}

근데 최종 응답으로 나가는 객체에서 backing field 값이 누락됐다는 제보를 받고.. 열심히 원인을 추적해봤다😇
당시 로직은 대략 이런 상황이었다. 로직이 길어지다보니까 코드만으로 원인을 찾기 힘들었고 로그를 남겨보며 원인을 좁혀나가봤다.

val member = Member.of(1L, "AAA", "nickName")

// -- 복잡한 로직.. --

member.copy(name = "BBB").also {
    // -- 생략 --
}

// -- 복잡한 로직2.. --
// 근데 backing field(nickName) 값이 없음..🥲


눈치 빠른 분들은 이미 원인을 알아챘겠지만, copy()의 also 쪽을 보면 backing field를 초기화해주는 내용이 없다!
정말정말 바보같은 로직이었다🥲

val member = Member.of(1L, "AAA", "nickName")

// -- 복잡한 로직.. --

member.copy(name = "BBB").also {
    // 🚨 backing field 초기화 안해주고 있었음!! 🚨
}

// -- 복잡한 로직2.. --
// 근데 backing field(nickName) 값이 없음..🥲

해결방법 2가지

copy().also { ... }에서 also에 초기화해주는 내용을 모두 추가해주면 물론 해결되겠지만, 이건 추후 수정사항이 있게되면 반영된 부분을 다 찾아서 수정해줘야 한다. 실수하면 또 장애로 이어질 수 있기 때문에 상황에 맞춰 아래 2가지 방법 중에 선택하면 되겠다.

backing field도 생성자에 넣어주기

가장 간단한 방법이다. 하지만 backing field로 빼둔 의도가 있을테니까 유용하진 않을 것 같다.

data class Member(
    val id: Long,
    val name: String,
    val nickName: String = "",
)


copy() 역할 메소드 새로 만들기

backing field 까지 복사하는 fullCopy()를 정의해주자!

data class Member(
    val id: Long,
    val name: String,
) {
    var nickName: String = ""
        private set

    fun fullCopy(name: String): Member {
        return this.copy(name = name).also {
            it.nickName = this.nickName
        }
    }

    companion object {
        fun of(id: Long, name: String, nickName: String): Member {
            return Member(id, name).apply {
                this.nickName = nickName
            }
        }
    }
}


테스트도 문제없이 통과한다!

@Test
fun `fullCopy() backing field 복사할  있다`() {
    val member = Member.of(1L, "AAA", "nickName")
    member.fullCopy("BBB").also {
        assertEquals("nickName", it.nickName) // success
    }
}

카테고리:

업데이트:

댓글남기기