[Javascript] freeze, seal에 대하여

박기영·2022년 12월 4일
0

Javascript

목록 보기
24/45

객체의 수정을 제한하는 freeze(), seal() 둘의 차이를 알아보자!

객체(object)

우선, 객체는 속성을 수정할 수도, 추가할 수도 있다.

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, configabletrue로 되어있다.
즉, 속성의 변경도 가능하고, 추가, 삭제도 가능하다는 것이다.

Object.freeze

우선 MDN 문서를 살펴보자.

Object.freeze() 메서드는 객체를 동결합니다. 동결된 객체는 더 이상 변경될 수 없습니다. 즉, 동결된 객체는 새로운 속성을 추가하거나 존재하는 속성을 제거하는 것을 방지하며 존재하는 속성의 불변성, 설정 가능성(configurability), 작성 가능성이 변경되는 것을 방지하고, 존재하는 속성의 값이 변경되는 것도 방지합니다. 또한, 동결 객체는 그 프로토타입이 변경되는것도 방지합니다. freeze()는 전달된 동일한 객체를 반환합니다.
- MDN docs -

속성을 변경하는 것도 삭제하는 것도, 새로운 속성을 추가하는 것도 막아버린다.
즉, writable, configablefalse로 만들어버린다는 것이다.

const obj = {
  name: "Rki0",
  age: 26,
};

Object.freeze(obj);

console.log(Object.getOwnPropertyDescriptor(obj, "name"));
// { value: 'Rki0', writable: false, enumerable: true, configurable: false }

확인해보니 실제로 writable, configablefalse로 변경된 것을 볼 수 있다.
음..그럼 속성 값을 변경해보자. 어떻게 될까?

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()를 써줬더니
중첩된 객체도 동결이 된 것을 확인할 수 있다!

Object.seal

우선 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 }

writableture이다. 즉, 속성의 변경은 가능하다.
그러나 configurablefalse이다. 즉, 속성의 추가/삭제는 불가능하다.

정리

Object.freeze()는 객체의 속성 추가/삭제, 변경이 모두 불가능하게 만든다.
단, 중첩된 객체에는 적용되지 않는다.
중첩된 객체도 모두 동결하고자 한다면 커스텀 함수를 작성해야한다.

Object.seal()은 객체의 속성 추가/삭제는 안되지만, 변경은 가능하다.

참고 자료

Object.freeze() - MDN docs
Object.seal() - MDN docs
Dev Story님 블로그
dreamfulbud님 블로그

profile
나를 믿는 사람들을, 실망시키지 않도록

0개의 댓글