초보 개발자들이 많은 실수를 범하는 복사에 대하여 한 번 알아보겠습니다. 간단한 복사라고 생각했지만 원본 객체와 복사된 객체가 메모리의 주소를 공유하게 되면서 원하지 않은 렌더링이 발생한 경험이 있을 것 입니다. 이 글을 통해 완벽하게 이해할 수 있을 수 있음 좋겠습니다.

객체는?

객체는 원시 자료형과 다르게 메모리의 주소를 참조하게 되는데 이러한 특성 때문에 얕은 복사가 일어나 객체의 속성을 변경하면 원본과 복사본이 모두 변하는 상황이 일어나게 되는 것입니다.

하지만 항상 이런 일이 일어나는 것은 아닌데 객체 안에 객체 혹은 배열이 존재하는 경우에 일어나게 됩니다.

const object = { name: '홍길동', address: { city: "서울" }}
const otherObject = object;

otherObject.name = "길동홍" 
otherObject.address = "인천";

console.log("원본 객체: ", object) 
// { name: '길동홍', { address: "인천" }}
console.log("복사된 객체: ", otherObject)
// { name: '길동홍', { address: "인천" }}

이렇게 일반적인 할당을 하게 되면 메모리 주소가 그대로 복사되어 복사된 객체의 속성에 값을 할당해도 원본 객체에 영향을 미치게 됩니다.

얕은 복사

그렇다면 얕은 복사를 진행해 보면서 할당과는 어떠한 차이점이 존재하는지 알아보도록 하겠습니다. 우선 얕은 복사에는 spread 연산자, Object.assign() 메서드가 존재합니다.

spread 연산자 같은 경우에는 아래와 같이 사용하며

const object = { name: '홍길동', address: { city: "서울" }}
const otherObject = {...object};

복사된 객체의 최상단의 속성을 변경하여도 원본 객체의 속성과 메모리 주소가 달라졌기 때문에 영향을 미치지 않습니다.

otherObject.name = "길동홍";

console.log("원본 객체: ", object);
// { name: '홍길동', { address: "서울" }}
console.log("복사된 객체: ", otherObject);
// { name: '길동홍', { address: "서울" }}

하지만 중첩된 객체 속성인 address.city를 바꾸게 되면 얕은 복사이기 때문에 중첩된 객체는 메모리 주소를 그대로 참조하게 되어 복사된 객체의 address.city 속성을 변경하면 원본 객체에도 영향을 미치게 됩니다.

otherObject.address.city = "부산";

console.log("원본 객체: ", object);
// { name: '홍길동', { address: "부산" }}
console.log("복사된 객체: ", otherObject);
// { name: '길동홍', { address: "부산" }}

Object.assign() 메서드

또 다른 얕은 복사의 방식으로 Object.assign() 메서드가 있습니다. 이 메서드는 기존 객체를 대사으로 새로운 객체에 속성을 복사해 줍니다.

const otherObject = Object.assgin({}, object);

위 코드는 빈 객체에 object 객체를 복사합니다. 동작 원리는 spread와 거의 비슷하며, 최상위 속성만 복사되고 중첩 객체는 참조로 남습니다.

복사가 중요한 이유는 값 자체를 복사한다는 점 보다는 의존성 해제에 초점을 맞춰서 객체의 불변성을 지켜 의도치 않은 렌더링이 발생하지 않게 하기 위함입니다.