객체의 수정을 제한하는
freeze()
,seal()
둘의 차이를 알아보자!
우선, 객체는 속성을 수정할 수도, 추가할 수도 있다.
const obj = {
name: "Rki0",
age: 26,
};
obj.age = 19;
console.log(obj); // { name: 'Rki0', age: 19 }
obj.job = "백수";
console.log(obj); // { name: 'Rki0', age: 19, job: '백수' }
delete obj.age;
console.log(obj); // { name: 'Rki0', job: '백수' }
이런 보통의 예시에서는 값이 변경되어도 별 상관이 없지만,
프로젝트를 진행할 때는 이게 큰 문제가 될 수 있다.
우선 객체가 왜 변할 수 있는지부터 이해하고 넘어가자.
const obj = {
name: "Rki0",
age: 26,
};
console.log(Object.getOwnPropertyDescriptor(obj, "name"));
// { value: 'Rki0', writable: true, enumerable: true, configurable: true }
객체 속성을 확인 할 수 있는 getOwnPropertyDescriptor()
를 통해
일반적인 객체가 어떤 특징을 가지고 있는지 살펴보자.
여기서 주목해야할 것은 writable
, configurable
이다.
각각 작성 가능성
, 설정 가능성
을 의미한다.
작성 가능성(writable)
: 해당 속성의 값을 변경할 수 있는지 여부.
설정 가능성(configurable)
: 속성을 추가하거나 삭제할 수 있는지 여부.
현재 콘솔로 찍어본 내용을 보면 알 수 있듯이
기본적으로 객체는 writable
, configable
이 true
로 되어있다.
즉, 속성의 변경도 가능하고, 추가, 삭제도 가능하다는 것이다.
우선 MDN 문서를 살펴보자.
Object.freeze() 메서드는 객체를 동결합니다. 동결된 객체는 더 이상 변경될 수 없습니다. 즉, 동결된 객체는 새로운 속성을 추가하거나 존재하는 속성을 제거하는 것을 방지하며 존재하는 속성의 불변성, 설정 가능성(configurability), 작성 가능성이 변경되는 것을 방지하고, 존재하는 속성의 값이 변경되는 것도 방지합니다. 또한, 동결 객체는 그 프로토타입이 변경되는것도 방지합니다. freeze()는 전달된 동일한 객체를 반환합니다.
- MDN docs -
속성을 변경하는 것도 삭제하는 것도, 새로운 속성을 추가하는 것도 막아버린다.
즉, writable
, configable
을 false
로 만들어버린다는 것이다.
const obj = {
name: "Rki0",
age: 26,
};
Object.freeze(obj);
console.log(Object.getOwnPropertyDescriptor(obj, "name"));
// { value: 'Rki0', writable: false, enumerable: true, configurable: false }
확인해보니 실제로 writable
, configable
이 false
로 변경된 것을 볼 수 있다.
음..그럼 속성 값을 변경해보자. 어떻게 될까?
const obj = {
name: "Rki0",
age: 26,
};
Object.freeze(obj);
obj.age = 19;
console.log(obj); // { name: 'Rki0', age: 26 }
obj.job = "백수";
console.log(obj); // { name: 'Rki0'. age: 26 }
delete obj.age;
console.log(obj); // { name: 'Rki0'. age: 26 }
맨 위에서 봤던 예제와 다르게 이번에는 수정, 추가/삭제 그 무엇도 되지않는 것을 볼 수 있다!
Object.freeze()
는 객체의 수정, 추가/삭제를 못하게 한다는 것을 알았다.
그러나...절대적인 것이 아니다.
?????????
이게 뭔 소리지.
말그대로 Object.freeze()
를 썼다고 해서 수정, 추가/삭제가 막히는게 아니라는 것이다.
예제를 살펴보자.
const obj = {
name: "Rki0",
age: 26,
inner: {
name: "Rki1",
age: 30,
},
};
Object.freeze(obj);
obj.inner.age = 19;
console.log(obj.inner); // { name: 'Rki1', age: 19 }
obj.inner.job = "백수";
console.log(obj.inner); // { name: 'Rki1', age: 19, job: '백수' }
delete obj.inner.age;
console.log(obj.inner); // { name: 'Rki1', job: '백수' }
obj
객체 내에 inner
객체를 만들고 obj
객체를 Object.freeze()
를 시켜보았다.
inner
객체는 freeze()
의 영향을 전혀 받지않는 것을 확인할 수 있다.
즉, 가장 바깥 속성에 대해서만 freeze()
가 유효하다는 것이다.
const obj = {
name: "Rki0",
age: 26,
inner: {
name: "Rki1",
age: 30,
},
};
Object.freeze(obj.inner);
obj.inner.age = 19;
console.log(obj.inner); // { name: 'Rki1', age: 30 }
obj.inner.job = "백수";
console.log(obj.inner); // { name: 'Rki1', age: 30 }
delete obj.inner.age;
console.log(obj.inner); // { name: 'Rki1', age: 30 }
이렇게 해당 객체를 또 freeze()
를 해줘야 동결이 되는 것을 확인 할 수 있다.
음...골치 아픈 문제다.
그러면 객체 안에 객체가 하나 더 있는 경우라면 매번 이렇게 하나하나 동결 시켜줘야하는걸까?
아니다! 커스텀 함수를 작성하면 이를 해결 할 수 있다.
무려 MDN 문서에 적혀 있는 커스텀 함수이다.
function deepFreeze(object) {
// 객체에 정의된 속성명을 추출
var propNames = Object.getOwnPropertyNames(object);
// 스스로를 동결하기 전에 속성을 동결
for (let name of propNames) {
let value = object[name];
object[name] =
value && typeof value === "object" ? deepFreeze(value) : value;
}
return Object.freeze(object);
}
이 함수를 사용하여 obj
를 동결시키면, 그 내부의 다른 객체들도 모두 동결된다.
제대로 적용되는지 확인해보자.
const obj = {
name: "Rki0",
age: 26,
inner: {
name: "Rki1",
age: 30,
},
};
deepFreeze(obj);
obj.inner.age = 19;
console.log(obj.inner); // { name: 'Rki1', age: 30 }
obj.inner.job = "백수";
console.log(obj.inner); // { name: 'Rki1', age: 30 }
delete obj.inner.age;
console.log(obj.inner); // { name: 'Rki1', age: 30 }
오! 아까와는 다르게 obj
객체에만 deepfreeze()
를 써줬더니
중첩된 객체도 동결이 된 것을 확인할 수 있다!
우선 MDN 문서를 살펴보자.
Object.seal() 메서드는 객체를 밀봉합니다. 객체를 밀봉하면 그 객체에는 새로운 속성을 추가할 수 없고, 현재 존재하는 모든 속성을 설정 불가능 상태로 만들어줍니다. 하지만 쓰기 가능한 속성의 값은 밀봉 후에도 변경할 수 있습니다(역자 주 : 바로 이 점이 Object.freeze()와의 차이라고 할 수 있습니다).
MDN 문서에서도 seal()
과 freeze()
의 차이점을 명시하고 있다.
그 것은 바로 writable
속성을 true
로 유지한다는 것이다.
그 외에는 freeze()
와 같다.
예시를 살펴보자.
const obj = {
name: "Rki0",
age: 26,
};
Object.seal(obj);
console.log(Object.getOwnPropertyDescriptor(obj, "name"));
// { value: 'Rki0', writable: true, enumerable: true, configurable: false }
writable
속성이 freeze()
때와는 달리 true
를 유지하고 있다는 것을 알 수 있다.
그렇다면 속성의 변경, 추가/삭제는 어떻게 될까?
const obj = {
name: "Rki0",
age: 26,
};
Object.seal(obj);
obj.age = 19;
console.log(obj); // { name: 'Rki0', age: 19 }
obj.job = "백수";
console.log(obj); // { name: 'Rki0'. age: 19 }
delete obj.age;
console.log(obj); // { name: 'Rki0'. age: 19 }
writable
은 ture
이다. 즉, 속성의 변경은 가능하다.
그러나 configurable
은 false
이다. 즉, 속성의 추가/삭제는 불가능하다.
Object.freeze()
는 객체의 속성 추가/삭제, 변경이 모두 불가능하게 만든다.
단, 중첩된 객체에는 적용되지 않는다.
중첩된 객체도 모두 동결하고자 한다면 커스텀 함수를 작성해야한다.
Object.seal()
은 객체의 속성 추가/삭제는 안되지만, 변경은 가능하다.
Object.freeze() - MDN docs
Object.seal() - MDN docs
Dev Story님 블로그
dreamfulbud님 블로그