Object.freeze()와 Object.seal()은 무엇인가?

Murpin·2022년 11월 22일
2

우테코프리코스

목록 보기
4/5
post-thumbnail

refrence : https://ui.toast.com/posts/ko_20220420

우테코 프리코스를 하며 사람들의 코드를 둘러보며 리뷰하더 도중 이러한 코드를 발견할 수 있었다.

cosnt 상수 = Object.freeze({
  a: 'A',
	...//
})

서론

그래서 상수처리하는데 왜 저게 필요한 것이지? 라는 생각과 왜 저런 코드를 작성할까를 궁금해서 탐색을 하게 되었다.

먼저 Object.freeze() 란 무엇인가에 대해 알아보았는데

The Object.freeze()method freezes an object. Freezing an object prevents extensions
and makes existing properties non-writable and non-configurable. A frozen object can no longer be changed: new properties cannot be added, existing properties cannot be removed, their enumerability, configurability, writability, or value cannot be changed, and the object's prototype cannot be re-assigned. freeze()
returns the same object that was passed in.
reference: MDN-Object.freeze

해석하면

Object.freeze() 메서드는 객체를 고정합니다.
즉, 개발자가 생성한 상수 객체를 고정하겠다고 선언하는 과정,

**개체를 고정하면 확장이 방지되고 기존 속성을 쓸 수 없고 구성할 수 없게 됩니다. 고정된 개체는 더 이상 변경할 수 없습니다. 새 속성을 추가할 수 없고 기존 속성을 제거할 수 없으며 열거 가능성, 구성 가능성, 쓰기 가능성 또는 값을 변경할 수 없으며 개체의 프로토타입을 다시 할당할 수 없습니다.
⇒ 자바스크립트에서 객체의 경우에는 const를 해도 변경이 가능합니다.

  • 이유는** constobject 를 할당할 경우, memory heap 에 object 를 위한 메모리를 만들고 해당 주소값을 const 식별자에 할당합니다. 즉, 해당 주소값은 변하진 않지만 참조하고 있는 object 는 값을 변화시킬 수 있습니다.

한마디로 자바스크립트 객체에서의 상수이지만 값이 변경되는 경우를 염려하여 이와 같은 freeze를 활용하여 처리할 수 있다는 것을 알 수 있었다

아하 그렇구나 이해하고 넘어가려던 중!
TOAST UI 팀에서 작성된 Object.freeze()Object.seal() 이라는 내용의 대결을 보고서는 이에 대해 나의 지식으로 정리하고자 이렇게 글을 쓰게 됩니다. (출처는 상단에 표시하였습니다.)

Object.seal()?

seal이라는 의미는 편지를 봉한다는 의미를 지니는데, 전달 받은 객체의 모든 속성을 구성할 수 없게(non-configurable) 만든다

const obj = { a: 100 };
Object.getOwnPropertyDescriptors(obj);
/* {
 *   a: {
 *        configurable: true,
 *        enumerable: true,
 *        value: 100,
 *        writable: true
 *      }
 * }
 *
 */

Object.seal(obj);
Object.getOwnPropertyDescriptors(obj);

/* {
 *   a: {
 *        configurable: false,
 *        enumerable: true,
 *        value: 100,
 *        writable: true
 *      }
 * }
 *
 */

위의 예시해서 보면 configurable이 false로 변경된 것을 확인할 수 있다.

그렇다면 seal을 해두면은 개발자는 무엇이 제한되는 것인가?

// 1. 프로퍼티를 변경하는 경우
obj.a = 200;
console.log(obj.a);
// 200

// 2. 프로퍼티를 제거하는 경우
delete obj.a;
console.log(obj.a);
// 200

// 3. 새로운 프로퍼티를 추가하는 경우
obj.b = 500;
console.log(obj.b);
// undefined

위의 1번을 제외한 나머지 동작들은 실행되지 않고 1번만 동작하게 되었다.

그렇다면 왜 1번만 이루어진 것인가?

봉인된 객체의 configurable이 false가 되어도 기존 속성값은 200으로 변경되게 된다. 앞서 설명했듯 configurable
을 false로 설정하면 속성을 쓸 수 없도록 만들어지지만 writable이 명시적으로 true면 큰 상관 없이 변경이 가능하다.

그렇다면 이 둘의 차이는?

위의 내용들을 정리하면 Object.freeze()Object.seal()의 기능상 목적은 동일하다. 그렇다면 왜 굳이 2개가?

이 둘의 차이점을 설명해보겠다.

Object.freezeObject.sealObject.freeze가 보다 제한이 더 크게 걸린다.

const obj = { a: 100 };
Object.getOwnPropertyDescriptors(obj);
/* {
 *   a: {
 *        configurable: true,
 *        enumerable: true,
 *        value: 100,
 *        writable: true
 *      }
 * }
 *
 */
Object.freeze(obj);
Object.getOwnPropertyDescriptors(obj);
/* {
 *   a: {
 *        configurable: false,
 *        enumerable: true,
 *        value: 100,
 *        writable: false
 *      }
 * }
 *
 */

위의 내용에서 보면 configurable 뿐만아니라 writablefalse로 변경된 것을 확인할 수 있다.

// 1. 프로퍼티를 변경하는 경우
obj.a = 200;
console.log(obj.a);
// 100

// 2. 프로퍼티를 제거하는 경우
delete obj.a;
console.log(obj.a);
// 100

// 3. 새로운 프로퍼티를 추가하는 경우
obj.b = 500;
console.log(obj.b);
// undefined

물론 기능 예시의 기능의 경우를 보면 위에서 1번을 포함한 모든 기능이 작동하지 않는다. 이 결과를 통해 Object.freeze()writable 속성을 false로 변경시키기 때문에 seal 보다 더 엄격하다고 할 수 있다.

Object.freezeObject.seal 정리

공통점

  1. 전달된 객체들은 확장할 수 없게 되어 새로운 속성을 추가할 수 없다.
  2. 전달된 객체 내부의 모든 요소들은 구성 불가능하게 되어 삭제할 수 없다.
  3. 스트릭트 모드에서 두 메서드를 사용한 객체 모두 obj.b = 500 같이 프로퍼티를 추가하는 명령어의 경우에는 undefined성립하지 않는다.

차이점

  1. Object.seal은 프로퍼티를 수정할 수 있지만 Object.freeze는 그렇지 않다.

결함

이 두가지 메서드에서 목적과 맞지 않게 결함이 존재하는데, 바로 중첩된 객체의 경우에 제대로 이루어지지 않는다는 것이다.

const obj = {
  foo: {
    bar: 10
  }
};

Object.seal(obj);

위 와 같은 중첩된 객체를 seal을 통해 잠금했을 때 서술자는 이렇게 변경된다.

Object.getOwnPropertyDescriptors(obj);
/* {
 *   foo: {
 *        configurable: false,
 *        enumerable: true,
 *        value: {bar: 10},
 *        writable: false
 *      }
 * }
 */

Object.getOwnPropertyDescriptors(obj.foo);
/* {
 *   bar: {
 *        configurable: true,
 *        enumerable: true,
 *        value: 10,
 *        writable: true
 *      }
 * }
 */

이 차이를 이해하셨나요? 그렇습니다. 개발자의 의도에서는obj를 seal로 봉하려 했지만 객체 내부에 있는 객체는 아무런 변화가 없음을 알 수 있다.

위와 같은 문제를 해결하기 위해 deepFreeze라는 방식을 추천하는데 코드는 아래와 같다.

function deepFreeze(object) {

  // 객체에 정의된 속성의 이름을 조회한다.
  var propNames = Object.getOwnPropertyNames(object);

  // 자신을 프리징하기 전 속성을 프리징한다.
  for (let name of propNames) {
    let value = object[name];

    if(value && typeof value === "object") {
      deepFreeze(value);
    }
  }

  return Object.freeze(object);
}

결론

Object.freeze와 Object.seal은 확실히 객체를 봉인하여 불변성을 지켜준다는 장점이 있어 상수와 같이 const에서 객체의 불안함을 해결할 수 있는 새로운 메서드가 될 것이다. ****

하지만 중첩된 객체를 동결하기 위해선 deepFreeze를 사용해야 한다는 점을 반드시 고려해야 한다.

profile
모든 것에 배움을 얻고자합니다

0개의 댓글