React를 사용하며 변수 선언 시 예상치 못 한 결과를 방지하기 위해 작성한 글입니다.
변수 선언을 할 때 배열 또는 객체 값을 복사하는 경우가 많은데, JS에는 정말 다양한 복사 방법이 있다.
타입에 따라 다 다르지만 spread operator
, push
, assign
, ... 등 다 결과만 봤을 때 유사하게 작용하는 것 같지만 복사에도 종류가 있으므로 예상치 못한 코드 결과를 마주칠 수 있다. React에서 아주 민감하게 작용하는 부분이라 꼭 알고 넘어가야 한다.
복사에는 얕은 복사, 깊은 복사 라고 칭하는 복사가 있으며 이에 대해 자세히 살펴보고자 한다.
- 두 변수가 동일한 메모리 주소를 가리키게 된다.
- 원본 객체는 하나인 상태에서 참조 값만 복사한다.
→ 동일한 레퍼런스(참조값, 메모리 주소)를 가진다는 의미
- 동일한 값을 가지지만 다른 메모리 주소를 가리킨다.
- 다른 레퍼런스를 가진다. (= 독립적인 주소를 가지게 됨)
- 원시 타입처럼 완전한 복사본을 만든다.
추가로, 객체의 경우 변수가 참조 값을 저장하고 있기 때문에 const로 선언해도 내부 프로퍼티 값 변경이 가능한 것이다. (참조 값은 변경 불가, 참조 값이 가리키는 객체는 변경 가능)
개념적으로는 이해가 갈 수 있지만, 실제 코드 결과를 보면서 더 깊게 이해해보자.
const array = [1,2,3,4,5]
const test = array
test[0]=100
// test, array의 0 인덱스 모두 변경
console.log(test)
// output [100,2,3,4,5]
console.log(array)
// output [100,2,3,4,5]
console.log(test===array)
// output true
변수 test
에 array
를 그대로 할당하면 같은 참조값을 따르므로 두 변수를 비교했을 때 같은 값이라 출력된다.
const person = {
name: 'minngki',
age: '2',
language: 'javascript'
}
const copiedPerson={...person};
console.log(person===copiedPerson)
// output false
spread operator
를 사용하여 변수 copiedPerson
에 person
을 얕은 복사한다면 두 변수는 다른 값이라 출력된다.
또한 공부하면서 가장 헷갈렸던 부분은 spread operator
를 얕은 복사로서 소개가 많이 되고 있다는 것이다. 아주 틀린 말은 아니지만 얕은 복사로서 사용하면 안 되는 구문이다.
📌 "얕은 복사"와 React의 효율적인 상태업데이트를 위한 "얕은 비교"는 다르다.
★
spread operator
,assign
메서드 - 2 depth 이상부터는 참조 값을 전달하는 얕은 복사로 동작한다.
즉, 가장 바깥의 값들만 깊은 복사가 가능하고, 안 쪽의 값들은 같은 참조값을 가져 얕은 복사로만 작용한다.
이게 무슨 말이냐 하면,
const person = {
name: 'minngki',
age: '2',
language: {first: 'javascript', second: 'python'}
}
const copiedPerson={...person};
console.log(person===copiedPerson)
// output false
아까 깊은 복사 예시코드에서 2 depth로 객체를 선언해보았다.
1 depth의 객체를 비교했을 때 똑같이 깊은 복사가 된 것 처럼 보이지만,
console.log(person.language===copiedPerson.language)
// output true
2 depth 객체에서부터는 얕은 복사로서 동작하는 것이다.
그렇다면 2depth에서도 복사를 실행하려면 어떻게 코드를 수정하면 될까 ?
...
const copiedPerson={...person, language: {...person.language}};
console.log(person.language===copiedPerson.language)
// output false
그러나 더 복잡한 객체 구조에서는 앞서 소개한 방식이 상당히 노가다스러운 작업일 수 있다.
완벽하게 깊은 복사를 하기 위해서는 다음 세 가지 방법을 참고하면 된다.
말 그대로 새로 함수를 선언해서 재귀적으로 수행하는 것을 말한다.
const person = {
name: 'minngki',
age: '2',
language: {first: 'javascript', second: 'python'}
}
const copyObj = (original) => {
const newObj = {}
for (const prop in original) {
newObj[prop] = original[prop]
}
return newObj
}
console.log(copyObj(person)===person)
// output false
가장 단순하지만, 이 또한 depth가 길어질수록 Time Complexity(시간 복잡도)가 늘어난다.
알고리즘 공부할 때 저 방식대로 해서 시간 초과로 머리 부여잡고 다시 풀었던 기억이 남.
JSON.parse()
와 JSON.stringify()
메서드 사용JSON.stringify
을 통해 객체 전체를 문자열로 변환 후, 다시 JSON.parse
을 이용하여 문자열을 객체 형태로 변환하는 것이다.
문자열로 변환하는 순간 참조값이 변경되므로 새로운 객체로서 사용할 수 있다.
const copyObj = (original)=>{
return JSON.parse(JSON.stringify(original))
}
console.log(copyObj(person)===person)
// output false
고차함수 집합 및 함수형 라이브러리라고 하는데, 아직 사용할 일이 없어서 나중에 공부할 예정.. 참고 링크
https://choar816.tistory.com/154
https://velog.io/@shin6403/Javascript-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%99%80-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%ACPart.1