본문 바로가기
JavaScript

[PoiemaWeb] 변경불가성

by pds6370 2023. 3. 9.

변경불가성(Immutability)는 객체가 생성된 이후 그 상태를 변경할 수 없는 디자인 패턴을 의미한다.

 

객체는 참조 형태로 전달하고 전달 받는다. 객체가 참조를 통해 공유되어 있다면 그 상태가 언제든지 변경될 수 있기 때문에 문제가 될 가능성도 커지게 된다. 

1. immutable value vs. mutable value

var str = 'Hello';

str = 'world';

 

위 코드에서 첫번째 구문이 실행되면 메모리에 문자열 'Hello'가 생성되고 식별자 str은 메모리에 생성된 문자열의 주소를 가리킨다. 그리고 두번째 구분이 실행되면 이전에 생성된 문자열 'Hello'을 구정하는 것이 아니라 새로운 문자열 'world'를 메모리에 생성하고 식별자 str은 이것을 가리킨다. 이때 문자열 'Hello'와 'world'는 모두 메모리에 존재하고 있다.

2행에서 String 객체의 slice() 메소드는 statement 변수에 저장된 문자열을 변경하는 것이 아니라 사실은 새로운 문자열을 생성하고 반환하고 있다. 그 이유는 문자열은 변경할 수 없는 immutable value이기 때문이다.

복사본을 리턴하는 문자열의 메소느 slice()와 달리 배열(객체)의 메소드 push()는 직접 대상 배열을 변경한다. 그 이유는 배열은 객체이고 객체는 immutable value가 아닌 변경 가능한 값이기 때문이다.

위 코드에서 user.name의 값을 변경했지만 변수 myName의 값은 변경되지 않았다. 이는 변수 myName에 user.name을 할당했을 때 user.name의 참조를 할당하는 것이 아니라 immutable한 값 'Lee'가 메모리에 새로 생성되고 myname은 이것을 참조하기 때문이다.

위으 경우 객체 user2의 name 프로퍼티에 새로운 값을 할당하면 객체는 변경 불가능한 값이 아니므로 객체 user2는 변경된다. 그런데 변경하지 않은 객체 user1도 동시에 변경된다.

이는 user1과 user2가 같은 주소를 참조하고 있기 때문이다.

 

2. 불변 데이터 패턴(immutable data pattern)

의도하지 않은 객체의 변경이 발생하는 원인의 대다수는 "레퍼번스를 참조한 다른 객체에서 변경"하기 때문이다.

이 문제의 해결 방법은 객체를 불변객체로 만들어 프로퍼티의 변경을 방지하며 객체의 변경이 필요한 경우에는 참조가 아닌 객체의 방어적 복사(defensive copy)를 통해 새로운 객체를 생성한 후 변경한다. 또는 Observer 패턴으로 객체의 변경에 대처할 수도 있다.

  • 객체의 방어적 복사(defensive copy) - object.assign
  • 불변객체화를 통한 객체 변경 방지 - Object.freeze

  2.1 Object.assign

  • Object.assign은 타겟 객체로 소스 객체의 프로퍼티를 복사한다. 이때 소스 객체의 프로퍼티와 동일한 프로퍼티를 가진 타겟 객체의 프로퍼티들은 소스 객체의 프로퍼티로 덮어쓰기된다. 리턴값으로 타겟 객체를 반환한다.

//Syntax

Object.assing(target, ... source)

  • object.assign을 사용하여 기존 객체를 변경하지 않고 객체를 복사하여 사용할 수 있다. Object.assign은 완전한 deep copy를 지원하지 않는다. 객체 내부의 객체(Nested Object)는 Shallow copy된다.

  • user1 객체를 빈객체에 복사하여 새로운 객체 user2를 생성하였다. user1과 user2는 주소를 공유하지 않으므로 한 객체를 변경하여도 다른 객체에 아무런 영향을 주지 않는다.

  2.2 Object.freeze

Object.freeze()를 사용하여 불변(immutable) 객체로 만들수 있다.

  • 객체 내부의 객체(Nested Object)는 변경가능하다.
  • 내부 객체까지 변경 불가능하게 만들려면 Deep freeze를 하여야한다.