데이터 타입 관련 글을 보기 전 아래의 상황을 공유하겠습니다.
let a = 10
let b = a
let address = { location:"서울특별시", detail:"1XX동 XXX호"}
let newAddress = address
b = 20
newAddress.location = "대구광역시"
console.log(a === b) // false
console.log(address === newAddress) // true
console.log(a === b)
의 결과는 누구나 쉽게 납득을 할 수있으나 두번째의 경우는 의아할 수 있다.
실제 현업에서 주소록관련 컴퍼넌트를 제작하면서 겪었던 경험이 있다. 기존 address를 newAddress로 수정하면서 address와 newAddress가 다른 경우만 해당 데이터 변경을 위한 api요청을 시도 하려했으나 비교시 일치한다는 결과가 출력되어 api요청을 하지 않았던 적이 있었다
분명 코드상 newAddress의 location의 값만 변경하였는데 address의 location마저 영향을 미치게되었다. 이유가 뭔지 또 이런경우 어떻게 해결을 해야할지 보겠습니다.
위의 상황에서 자바스크립트가 어떻게 데이터 할당을 하는지 확인하겠습니다.
변수 영역 | 주소 | 1001 | 1002 | 1003 | 1004 |
---|---|---|---|---|---|
변수 영역 | 데이터 | 이름: a | |||
값:@5001 | 이름:b | ||||
값:@5001 | 이름:address | ||||
값:@5002 | 이름:newAddress | ||||
값:@5002 | |||||
데이터 영역 | 주소 | 5001 | 5002 | 5003 | 5004 |
데이터 영역 | 데이터 | 10 | @7103 ~ ? | “서울 특별시” | "1XX동 XXX호" |
객체 @5002의 변수영역 | 주소 | 7103 | 7104 | ||
객체 @5002의 변수영역 | 데이터 | 이름:location | |||
값: @5003 | 이름: detail | ||||
값: @5004 |
기본형 데이터, 참조형 데이터 모두 복사시 같은 주소를 봅니다. 여기서 기본형과 참조형의 차이는 참조형은 객체 프로퍼티 영역이 따로 존재한다는 점입니다. 이제 문제의 상황인 복사한 변수에 값을 재할당 하려하는 경우 어떤 일이 생기는지 확인하면 아래와 같습니다.
변수 영역 | 주소 | 1001 | 1002 | 1003 | 1004 | ||
---|---|---|---|---|---|---|---|
변수 영역 | 데이터 | 이름: a | |||||
값:@5001 | 이름:b | ||||||
값:@5001 → @5006 | 이름:address | ||||||
값:@5002 | 이름:newAddress | ||||||
값:@5002 | |||||||
데이터 영역 | 주소 | 5001 | 5002 | 5003 | 5004 | 5005 | 5006 |
데이터 영역 | 데이터 | 10 | @7103 ~ ? | “서울 특별시” | "1XX동 XXX호" | “대구 광역시” | 20 |
객체 @5002의 변수영역 | 주소 | 7103 | 7104 | ||||
객체 @5002의 변수영역 | 데이터 | 이름:location | |||||
값: @5003 → @5005 | 이름: detail | ||||||
값: @5004 |
변수 a와 b는 서로 다른 주소를 바라보게되었고 이로 인해 console.log(a === b)
가 false가 됩니다. 반면 address와 newAddress는 여전히 같은 객체를 바라보고있기에 위와 같은 문제가 발생하였습니다.
이로인해 해당 문제는 기본형은 주솟값을 복사하는 과정이 한번, 참조형은 한단계 더 거쳐 복사하기에 문제가 생긴다는것을 알았습니다.
결론적으로 위와 같이 전달받은 객체에 변경을 가해도 원본 객체는 변하지 말아야하는경우의 문제 해결을 위해선 불변객체가 필요합니다. 여기서 주의 할 점은 참조형 데이터의 가변성은 내부 프로퍼티를 변경할 때 성립합니다. 즉, 새로운 객체를 return해야합니다. 그리고 이과정에서 얕은 복사, 깊은 복사 개념이 나옵니다.
위의 상황의 객체가 아닌 중첩객체의 경우 프로퍼티에 참조형 데이터가 또 할당이 되어 있는경우는 객체는 바로 아래 단계의 값만 복사하는 얕은 복사가 아닌 내부의 모든 값을 하나하나 복사하는 깊은 복사를 해야만 원하는 결과값을 얻을 수 있습니다.
간단하게 위의 문제를 해결하기 위해 깊은 복사를 통해 해결해 보겠습니다.
let a = 10
let b = a
let address = { location: "서울특별시", detail: "1XX동 XXX호" }
let newAddress = JSON.parse(JSON.stringify(address))
b = 20
newAddress.location = "대구광역시"
console.log(a === b) // false
console.log(address === newAddress) // false