JavaScript 불변성

박희주·2022년 9월 1일
0

오늘의 지식

목록 보기
4/4

JavaScript에서의 불변성

여태까지 자바스크립트에서는 데이터의 불변성을 지켜야 한다는 얘기를 많이들으면서 프로그래밍 공부를 해왔다. 하지만 불변성을 지켜야 한다는 의미는 정확하게 모른채 데이터가 있으면 map을 돌려서 데이터를 가지고 필요한 부분에 적용을 했었는데 이번에는 불변성에 대해 공부를 해보았다.

1. 불변성이란?

자바스크립트에서의 불변성이란 객체가 생성된 이후 그 상태를 변경 할 수 없는 것을 의미한다.

과거 ES6이전에 자바스크립트에서 일반적으로 변수를 선언할 때는 var 키워드를 사용했었는데 이는 값이 변할 수 있고 참조하는 변수의 명도 변해도 에러를 도출하지 않고 그대로 사용할 수 있어 원본훼손에 대한 위험성이 매우 높았으나 ES6이후 letconst의 도입부터 이를 방지할 수 있게 되었다.

1-1. 참조명을 불변하게 하는 법

let키워드나 const키워드를 활용해 변수명을 선언해 주면 된다.

1-2. 값을 불변하게 하는 법

먼저 값에는 원시형 타입과 객체형 타입이 존재한다.

  • 원시형 타입(Primitive Type)
    • Number
    • String
    • Boolean
    • Null
    • Undefined
    • Symbol
  • 객체형 타입(Object Type)
    • Object
    • Array
    • Function 등...

원시형 타입을 제외하곤 나머지는 모두 객체형 타입이라고 생각하면 된다.

2. 원시형 타입과 객체형 타입에서의 불변성

2-1. 원시형 타입

let a = 1;
let b = a;
a = 2;
console.log(a === b); // false

a라는 변수에 1이라는 값을 할당하고 b라는 변수는 a라는 변수와 같은 값을 할당하고나서 a변수의 값을 2로 바꾸고 비교했을때 false가 나오는 이유는 무엇일까?
우리가 눈으로 보고 있을 때는 b가 a의 값을 공유하기 때문에 a의 값이 바뀌어도 b도 같이 변한다고 생각 할 수 있다.

만약 b에게 a를 할당해주는 시점에서 비교를 한다면 true가 나온다. 하지만 도중에 a의 값을 재할당해주었는데 여기서 false가 나오는 시점이다.

변수를 선언하고 값을 할당하는 과정은 컴퓨터의 측면에서 봤을 때 a라는 변수에 1이라는 값이 저장되는 것이 아니다.
1이라는 값이 컴퓨터 메모리에 저장이 되고 a라는 변수는 1이 저장되어 있는 메모리의 주소를 바라보게 되는 것이다. 이를 a라는 변수가 1이라는 값을 참조(Reference)한다라고 한다.

코드를 순서대로 설명하자면
1. a라는 변수는 1이라는 메모리 주소를 참조하고 있다.
2. b라는 변수는 a가 참조하는 1이라는 메모리 주소를 같이 참조하고 있다.
3. a 변수가 참조하던 메모리 주소가 2라는 값의 메모리 주소를 참조하고 있다.(a = 2;)
그래서 최종적으로 a와 b를 비교했을 때 b는 1의 메모리 주소를 보고 있고 a는 바뀐 2의 메모리 주소를 바라보고 있기 때문에 a과 b를 비교하면 false가 나오는 것이다.

2-2. 객체형 타입

let obj1 = {
  name: 'Park'
}

let obj2 = {
  name: 'Park'
}

console.log(obj1 === obj2) // false

console.log(obj1.name === obj2.name) // true

위의 코드를 보자마자 1번 객체와 2번객체를 비교했을 때 false가 왜 나오는지 의아할 수 있다. 왜냐하면 객체안에 있는 값이 동일한데 이 둘을 비교했을 때 false가 나왔기 때문이다.

객체형 타입은 그 안의 값이 무엇이 있던간에 객체 자체에 메모리 주소가 할당된다. 그래서 1번객체와 2번객체는 객체자체의 메모리 주소가 다르기 때문에 비교를 하게 되면 false가 나오게 되는 것이다.

반면 객체내부의 값을 비교했을때는 true가 나오게 되는데 이는 객체 자체에 할당된 메모리 주소가 있고 또 객체 내부의 속성값들끼리 다른 메모리 주소를 할당되기 때문이다.

결론적으로 객체 껍데기 자체로는 메모리 주소가 다르기 때문에 객체 자체를 비교하는 것은 그 내부의 값이 어떻던간에 false가 도출되게 되는 것이고 속성값은 메모리 주소가 동일하기 때문에 값을 비교했을 때는 true가 나오게 되는 것이다.

3. 객체를 불변하게 만드는 법?

객체형 타입의 메모리 주소 할당 특성에 의해 원본을 훼손할 가능성이 원시형 타입보다 훨씬 높아지게 된다. 그래서 불변성을 유지해주는 방법이 있는데 그 중 가장 대표적인 방법 몇 가지를 찾아봤다.

  1. Object.assign

    let obj1 = { name: 'Park' }
    
    let obj2 = Object.assign({}, obj1);
    
    obj2.name = "Kim"
    
    console.log(obj1) // { name: 'park' }
    console.log(obj2) // { name: 'Kim' }

    Object.assign은 객체를 복사하는 작업이다.
    기존 객체를 복사해서 새로운 변수에 할당 해주는 것으로 위의 코드 처럼 원본은 변하지 않으면서 새로운 객체가 생성되는 것을 확인 할 수 있다.

  2. Object.freeze

    let obj1 = { name: 'Park' }
    Object.freeze(obj1);
    obj1.name = 'Kim';
    console.log(obj1); // { name: 'Park' }

    Object.freeze는 freeze의 뜻처럼 원본을 얼려버려서 다른 곳에서 못건드리게 막아버리는 것이다. 1번 객체를 freeze시키고 속성값을 바꿔도 에러는 나오지 않지만 1번 객체가 불변성을 유지해서 원본을 도출해내게 되는 것이다.

3-1. 이와 별개로 const

var, let, const를 공부했을 때 var는 변수명도 값도 자유자재로 변하는게 가능하고 let은 값은 변할 수 있으나 변수명은 제한적이고 const는 그 두 가지를 아예 못하게 막는것으로 이해하고 있을 것이다.

위에서 메모리 할당과 참조를 설명했던 부분의 관점에서 보면 const는 상수의 개념으로 const로 변수명을 선언하고 값을 할당하는 것은 const 키워드가 붙은 변수명은 오로지 자신에게 할당된 메모리 주소 하나만 바라보게끔하고 참조하는 이름 자체도 그 메모리 주소에게 아예 고정을 시켜버리는 것으로 생각하면 될 것 같다.

profile
하나부터 열까지, 머리부터 발 끝까지

0개의 댓글