js 타입의 복사에 관한 내용

Duboo·2023년 9월 18일
0

자바스크립트

목록 보기
5/7
post-thumbnail

js 원시/참조 타입

원시 타입과 참조(객체) 타입의 복사

원시 타입참조(객체) 타입
복사값에 의한 전달참조에 의한 전달

원시 타입의 복사

let origin = "hello";
const copy = origin;

console.log(origin); // "hello"
console.log(copy); // "hello"

origin = "hi";

console.log(origin); // "hi"
console.log(copy); // "hello"

원시 타입(숫자, 문자열, 불리언...등)은 값 그대로 저장/할당되고 복사하며 값에 의한 전달 혹은 값에 의한 복사라고 합니다.


참조 타입의 복사

참조 타입을 복사는 얕은 복사와 깊은 복사로 나뉠 수 있습니다.

얕은 복사(Shallow Copy)

1차원적인 객체의 경우 Object.assign() 혹은 spread 연산자{...origin}를 사용하면 객체 자체는 깊은 복사를 수행할 수 있습니다.

const origin = {say: 'hello'};
const copy = {...origin};
// or => const copy = Object.assign({}, origin);

console.log(origin === copy); // false

이때 객체 안에 객체가 있을 경우 한 개의 객체라도 원본 객체를 참조 즉, 같은 힙 메모리 주소를 사용하고 있다면 모두 얕은 복사라고 합니다.

얕은 복사의 취약점

1차원적인 객체라면 상관 없겠지만 위 설명과 같이 객체 안에 객체가 객체가 있을 경우를 코드로 살펴봅니다.

const origin = ["string", 100, {name: "origin"}];
const copy = [...origin];

console.log(origin); // ["string", 100, {name: "origin"}]
console.log(copy); // ["string", 100, {name: "origin"}]

console.log(origin === copy); // false

copy[2].name = "copy";
copy[0] = "number"

console.log(origin); // ["string", 100, {name: "copy"}]
console.log(copy); // ["number", 100, {name: "copy"}]

console.log(origin === copy); // false의 결과로 봤을 때 분명 두 코드는 다른 메모리 주소를 참조한다고 생각할 수 있지만

위 코드에서 알 수 있는건 원시 값(혹은 1차원 객체)의 경우 의도한 바와 같이 깊은 복사가 되어서 개별적으로 사용할 수 있지만 객체 안의 객체(위 코드에선 2번째 인덱스 값인 {name: "origin"})을 수정할 경우 원본인 origin도 동일하게 수정된걸 확인할 수 있습니다.

Object.assign({}, origin);

{...origin}

1차원적인 객체의 경우 위와 같이 사용해도 상관 없겠지만 2차원 이상인 객체에는 의도한바와 같이 사용할 수 없습니다.


깊은 복사(Deep Copy)

깊은 복사(Deep Copy)는 복사된 객체가 다른 주소를 참조하며 내부의 값만 복사합니다.

얕은 복사의 취약점인 객체 안에 객체가 있을 경우에도 원본 객체 내부의 값만 복사하기 때문에 원본과의 참조가 완전히 끊어져 의도한 바와 같이 사용할 수 있습니다.

const origin = ["string", 100, {name: "origin"}];
const copy = JSON.parse(JSON.stringify(origin));

console.log(origin); // ["string", 100, {name: "origin"}]
console.log(copy); // ["string", 100, {name: "origin"}]

console.log(origin === copy); // false

copy[2].name = "copy";
copy[0] = "number"

console.log(origin); // ["string", 100, {name: "origin"}]
console.log(copy); // ["number", 100, {name: "copy"}]

위 코드의 JSON.stringify()는 객체를 json 문자열로 변환 시키며, 해당 과정에서 origin 객체와의 참조가 모두 끊어지게 됩니다.

const origin = ["string", 100, {name: "origin"}];

const copy = JSON.stringify(origin); 
console.log(copy); // '["string", 100, {name: "origin"}]'
console.log(typeof copy); // string

이렇게 문자열로 변환된 origin 객체를 다시 JSON.parse()를 사용해서 다시 객체 형태로 만들어줍니다.

let parserCopy = JSON.parse(copy);
console.log(parserCopy); // ["string", 100, {name: "origin"}]
console.log(typeof parserCopy); // 'object'

깊은 복사를 수행할 수 있는 방법은 위 방법 말고도 다양한 방법이 있습니다.

profile
둡둡

0개의 댓글