[JS/React] 얕은 복사와 깊은 복사 / React에서의 활용

kaya·2023년 12월 6일

Javascript

목록 보기
12/13
post-thumbnail

얕은 복사

복사본의 속성이 복사본이 만들어진 원본 객체와 같은 참조를 공유하는 복사
➡️ 원본, 복사본을 변경하면 다른 객체 또한 변경될 수 있음

깊은 복사

복사본의 속성이 복사본이 만들어진 원본 객체와 모두 다른 참조를 공유하는 복사; 내부 값을 가지고 완전히 새로운 객체를 복사해내는 것

  • 깊은 복사는 함수형 프로그래밍에서 불변성 유지에 중요한 역할을 한다

데이터 타입별 복사

기본형 타입(Primitive Type)

  • 바로 값을 할당한다
  • (ex) Number, String, Boolean, undefined, null, Symbol
  • 변수에 값을 할당, 연산 시에 값을 복사한다
    ➡️ = 만 써도 깊은 복사가 가능한다
let a = 10  // (1)
let b = a  // (2)
a = 20  // (3)
  • (1) 변수 a에 값 10이 할당된다
  • (2) 변수 b에 a가 가리키고 있는 참조값을 연결한다
  • (3) 10이 할당된 메모리 주소 공간의 값이 20으로 대체된게 아니라, 20을 할당할 새로운 메모리 주소 공간이 생기고, a가 이 새로운 메모리 공간을 참조 한다
    ➡️ 따라서, b의 값에는 변함이 없다(깊은 복사가 된 것)

참조형 타입(Reference Type)

  • 값이 저장된 주소값을 할당한다
  • obj 객체에는 a가 저장된 주소값, b가 저장된 주소값이 할당된다
let obj1 = {
	a: 10,
   	b: 20
}
  • 참조형의 복사는 객체에 할당된 주소값을 복사한다
  • 위 예제에서 보면 obj1에 할당된 주소값을 복사하는 것이다
    ➡️ 그렇기 때문에 a, b의 값을 변경해도 obj1이 가리키는 a, b의 주소값이 변경된 것이 아니기 때문에 복사본이 변경된다
let obj2 = obj1  // obj1의 참조값이 obj2로 복사
obj1.a = 1020

console.log(obj2.a)  // 1020
  • 그래서 참조형 타입은 =로 원하는 복사를 할 수 없다

복사를 하는 방법

객체가 몇차원이냐에 따라 얕은 복사가 될 수도, 깊은 복사가 될 수도 있다

... 연산자(...iterableObj)

해당 객체의 1차원 단계의 값의 참조값을 복사한다

let arr = [1,2,3]
let copied = [...arr] // (*)
copied.push(9999)

console.log(arr) // [1,2,3]
  • arr의 1차원 단계의 값 1,2,3 의 참조값을 복사하여 새로운 배열 안에 넣었기 때문에 효과적으로 복사가 이루어짐
    • (*) 라인에서 [] 를 통해 새로운 배열 객체가 만들어짐
  • 하지만 2차원 이상으로 가면 제대로 복사가 안된다
let arr = [[1,2,3],[4,5,6]]
let copied = [...arr]
copied[0].push(9999)

console.log(arr[0]) // [1,2,3,9999]
  • arr의 1차원 단계의 값 [1,2,3][4,5,6] 의 참조값을 복사한 것이기 때문에 여전히 그 내부의 값은 같은 참조값을 가지고 있어서 제대로 복사가 안된다

Object.assign()

객체의 속성을 복사해 붙여 넣고, 복사된 객체를 반환하는 메소드

  • ... 연산자와 같은 특징을 가진다, 즉, 1차원 객체는 정상적으로 깊은 복사가 되지만 그 이상 차원에서는 제대로 복사가 되지 않는다

재귀 함수 이용

  • 내부 프로퍼티 개수만큼 돌면서 복사를 한다
var deepCopy = function (obj) {
  var result = {};
  if (typeof obj === 'object' && obj !== null) {
    for (var prop in obj) {
      result[obj] = deepCopy(obj[prop]);
    }
  } else {
    result = obj;
  }
  return result;
};

JSON.parse(JSON.stringfy(obj)) 사용

  • JSON.stringfy(obj) : 값이나 객체를 JSON 문자열로 변환
  • JSON.parse() : 문자열 분석 후 그 결과에서 값이나 객체 생성
    ➡️ 이 과정에서 원본 객체와의 참조가 모두 끊어져서 깊은 복사가 가능하다

React에서의 활용

React component는 props나 state의 값이 변화할 때 DOM을 다시 그린다.
그리고 그 값이 변화하는 걸 확인할 때 얕은 비교를 활용한다.

얕은 비교

  • 해당 객체의 주소가 바뀌었는 지만 확인하고, 내부 값이 바뀌었는 지는 비교하지 않는 방식
  • 왜 얕은 비교를 쓸까? 성능 때문!
    ➡️ 내부 값까지 일일이 다 비교하면 느려질 수 있기 때문에
  • 그렇기 때문에 state 변화에 따라 DOM을 다시 그리려면 해당 객체의 주소를 바꿔야 한다
const [data, setData] = useState({
	name: 'kaya',
  	id: 1234,
  	information: {
    	phone: '1234-1234',
      	email: 'hello@gmail.com'
    }
})

<button onclick={() => {
    	data.information.phone = '5678-5678'
  		setData(data)
	} 
}>
      CLICK HERE
</button>

✅ 이렇게 하면 data 객체의 주소가 변한 것이 아니기 때문에 클릭해도 DOM이 변하지 않는다

<button onclick={() => {
    	setData({  // (*)
        	...data,
          	information : {
            	...information,
              	phone: '5678-5678'
            }
        })
	} 
}>
      CLICK HERE
</button>

이렇게 해줘야 객체가 변했다고 판단해 DOM을 다시 그린다

  • (*) : setData의 인수로 새로운 객체를 넘겨준 것이기 때문에 객체가 변했다고 인식한다
  • ...data, ...information: data, information 내부 값을 그대로 가져온다
  • information: {} : 변화가 생긴 information만 다시 넣어준다
  • phone: '5678-5678' : 변한 부분만 재정의해준다

참고 자료

profile
🏟 튼튼한 성은 튼튼한 벽돌로부터

0개의 댓글