[Android, Kotlin] 변수나 인스턴스 값 변경 시 다른 값도 변경 되는 문제 해결

 

[문제 상황]

 

일정을 표시하는 task 인스턴스를 만든 후에  여러 날짜의 task 인스턴스를 동시에 만들어 주고,

그 데이터를 관리하는 코드를 짜고 있었다.

 

fun addTaskData(startNum:Int,endNum:Int,task: Task){
    for (i in startNum..endNum){
        currentMonthArr[i].apply {
            if (this.taskList == null) {
                this.taskList = ArrayList()
            }
            this.taskList!!.add(task)
        }

    }
    taskLiveData.value = currentMonthArr

}

 

시작일과 끝일이 주어지면 전달된 task 인스턴스를 시작일부터 끝일까지 추가해주는 방식이었다.

성공적으로 데이터가 추가 되었으나 큰 문제가 발생했다.

 

아래의 이미지와 같이 각 날짜에 같은 데이터가 추가되었는데,

문제는 하나의 task 데이터를 변경하면  시작일부터 끝일까지의 모든 task 데이터가 변경되는 것이었다.

 

 

18일의 task 데이터를 변경하면 함께 추가된 17, 19일의 task 인스턴스도 변경된다.

 

변경한 데이터가 task A.per( 0 -> 25)라고 했을 때 함께 생성된 task B, task C 등도 per(0 -> 25)으로 함께 변경된 것이다.

 

이유는 깊은 복사를 구분하지 않았기 때문이다.

 

[문제 해결]

 

문제 해결 이전에 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)를 비교해보면,

 

얕은 복사는 객체를 복사할 때 객체의 주솟값을 복사하는 방법이다.

위에서 task 인스턴스를 생성하는 방식이 얕은 복사이다.

 

얕은 복사를 사용하면 주솟값이 복사되기 때문에 두 변수가 같은 주솟값을 참조하면서 같은 값을 공유한다.

 

깊은 복사는 객체를 복사할 때 객체의 주솟값이 아닌 전체 값을 복사하는 방법이다.

kotlin의 data class에서 제공하는 오버 라이딩 메서드 copy()를 통해서 깊은 복사를 진행할 수 있다.

 

하지만 기본형이 아닌 멤버 변수는 copy()를 통한 깊은 복사가 되지 않는다.

이를 해결하기 위해서 새로운 방법을 찾아야 했다.

 

<Gson을 활용한 방식>

 

Gson은 java object -> json으로 변환해주거나, json -> java object로 변환해주는 구글 라이브러리다.

 

이 라이브러리를 사용하기 위해서 의존성을 추가한다.

 

dependencies {
  implementation 'com.google.code.gson:gson:2.8.8'
}

 

Gson 라이브러리에서는 데이터 복사를 위해서 2가지 메서드를 사용한다.

 

1. fromJson : Json 데이터를 지정한 타입으로 변환

2. toJson : 지정한 타입의 데이터를 Json 형식으로 변환

 

toJson을 통한 data class의 값을 Json 형식으로 변환, fromJson을 통한 다시 data class 형식으로 

변환하는 방법을 통해서 깊은 복사를 진행할 수 있다.

 

우선 깊은 복사를 진행하기 위해서 데이터 클래스를 선언한다.

 

// 깊은 복사를 위한 클래스 선언
data class SampleClass(var task:Task)

 

SampleClass에 task 데이터를 넣어서 깊은 복사로 변환해주는 방식을 사용한다.

 

위의 두 함수를 이용해서 깊은 복사를 진행하는 함수를 데이터 클래스 내부에 선언한다.

 

fun deepCopy() : SampleClass{
    return Gson().fromJson(Gson().toJson(this),this::class.java)
}

 

깊은 복사를 진행하는 여러 가지 방법이 있지만 Gson 방식이 제일 편리한 것 같았다.

 

 이제 제작한 data class를 사용한다.

 

fun addTaskData(startNum:Int,endNum:Int,task: Task){
    for (i in startNum..endNum){
        val newTask = SampleClass(task).deepCopy()
        currentMonthArr[i].apply {
            if (this.taskList == null) {
                this.taskList = ArrayList()
            }
            this.taskList!!.add(newTask.task)
        }

    }
    taskLiveData.value = currentMonthArr

}

 

이전 방식과 다른 점은 data 클래스에서 Gson 라이브러리의 도움으로

깊은 복사를 진행한 인스턴스를 생성한다.

 

이후에 newTask data class 안의 task 인스턴스를 리스트에 적용시킨다,

 

 

18일에 추가된 task 데이터를 변경해도 17일의 task 데이터에는 변함이 없음을 확인했다.

 

문제 해결!