[Javascript] 객체의 불변성을 유지하는 방법(freeze, seal, preventExtensions의 차이)
변하지 않는 값을 처리하는 방법은 간단하게는 const 선언자로 상수 선언을 하는 방법이 있습니다.
개별 값은 이렇게 해도 되지만, 중간에 변경되면 안 되는 중요 설정 값을 담은 객체나, 원본 데이터를 담고 있는 객체 같은 경우, const 선언자로는 불변성을 유지할 수 없습니다.
그래서 자바스크립트는 객체에 불변성을 유지할 수 있도록 별도의 객체(Object) 메서드를 지원하고 있으며, 불변성 강도에 따라 사용할 수 있도록 freeze(), seal(), preventExtensions() 3가지를 제공합니다.
불변성을 어느 정도까지 유지해야 하는지에 따라 용도가 조금씩 다르며, preventExtensions < seal < freeze 순으로 불변성이 강하게 유지됩니다.
freeze를 가장 많이 사용하며 freeze로 불변 속성을 부여한 객체는 속성의 값을 수정할 수도 없고, 속성을 추가하거나 삭제할 수도 없습니다.
다음처럼 const 선언자로 선언한 환경 설정 값을 담은 객체의 name 속성 값을 변경해 보겠습니다.
const apiconfig = {
name: 'API서버',
server: {
ip: '192.168.0.3',
port: '8081',
userid: 'apost',
password: 'dpdlvldkdl',
authkey: 'QK#ETRA1*AA',
}
}
apiconfig.name = 'API테스트서버'
console.log(apiconfig);
freeze
객체의 모든 속성에 불변성을 부여하고 싶으면 freeze() 메서드를 사용하면 됩니다.
객체를 얼려서 아무것도 못하게 한다고 생각하면 됩니다.
Object.freeze(apiconfig)
apiconfig.name = 'API테스트서버' // freeze.js:14 Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
불변성이 부여된 객체의 속성을 수정하면 에러가 발생하고 속성 값을 수정할 수 없습니다.
freeze 후에 속성 값이 변경되는 경우는 2가지입니다.
첫 번째. freeze()는 strict 모드에서 정상적으로 동작합니다.
따라서 코드 상단에 "use strict"를 선언해야 freeze()가 동작합니다.
두 번째. freeze()는 인자로 넣은 객체의 속성만 값 변경, 속성 추가/삭제를 제한합니다. 하위에 중첩된 객체가 있으면 하위 객체의 속성은 freeze 제한이 적용되지 않습니다.
따라서 앞서의 객체는 다음과 같이 수정해야 온전히 freeze()가 적용됩니다.
"use strict";
const apiconfig = {
name: 'API서버',
server: {
ip: '192.168.0.3',
port: '8081',
userid: 'apost',
password: 'dpdlvldkdl',
authkey: 'QK#ETRA1*AA',
}
}
Object.freeze(apiconfig)
Object.freeze(apiconfig.server)
프리즈(freeze)된 객체인지를 확인할 수 있도록 헬퍼 메서드 isFrozen() 이 제공됩니다.
console.log(Object.isFrozen(apiconfig)); // true
seal
freeze()와 유사한 기능을 하며, 중첩 객체에 대해서는 하위 객체에도 seal()을 적용해야 하는 점도 동일합니다.
다른 점은 객체에 새 속성을 추가하거나 삭제할 수 없습니다.
기존에 있는 속성의 값은 자유롭게 변경할 수 있습니다.
앞서의 apiconfig 객체를 seal()로 처리해서 속성의 추가 삭제를 막아보겠습니다.
"use strict";
const apiconfig = {
name: 'API서버',
server: {
ip: '192.168.0.3',
port: '8081',
userid: 'apost',
password: 'dpdlvldkdl',
authkey: 'QK#ETRA1*AA',
}
}
Object.seal(apiconfig)
Object.seal(apiconfig.server)
apiconfig.name = 'API테스트서버'
apiconfig.lastupdate = '2023-01-23' // Uncaught TypeError: Cannot add property lastupdate, object is not extensible
apiconfig 객체에 lastupdate 속성을 추가하려고 하면 에러가 발생합니다.
씰(seal) 된 객체인지 확인할 수 있도록 헬퍼 메서드 Object.isSealed() 메서드가 제공됩니다.
console.log(Object.isSealed(apiconfig)); // true
preventExtensions
seal()과 동일하고 차이점은 속성 삭제가 가능합니다. 속성 추가는 할 수 없습니다.
앞서의 apiconfig 객체에 preventExtensions()로 처리를 한 후 더 이상 사용하지 않는 authkey 속성을 삭제해 보겠습니다.
"use strict";
const apiconfig = {
name: 'API서버',
server: {
ip: '192.168.0.3',
port: '8081',
userid: 'apost',
password: 'dpdlvldkdl',
authkey: 'QK#ETRA1*AA',
}
}
Object.preventExtensions(apiconfig)
Object.preventExtensions(apiconfig.server)
delete apiconfig.server.authkey // 성공
apiconfig.lastupdate = '2023-01-23' // Uncaught TypeError: Cannot add property lastupdate, object is not extensible
확장 차단(preventExtensions) 된 객체인지 확인할 수 있도록 헬퍼 메서드 Object.isExtensible() 메서드가 제공됩니다. 결과 불리언 값이 반대라는 것에 주의해야 합니다. 헬퍼 메서드가 확장 가능한가?를 묻는 것이기 때문에 확장이 차단(preventExtensions)된 객체는 false 가 반환됩니다.
console.log(Object.isExtensible(apiconfig)); // false