Object.freeze() vs Object.seal()

Object.freeze() vs Object.seal()

Object.freeze()와 Object.seal()의 차이점과 사용 방법 그리고 deepFreeze 구현 방법에 대해서 작성했어요.

4 min read

JS의 객체는 기본적으로 변경 가능한(mutable) 데이터 구조입니다. 즉, 객체의 속성에 대한 추가, 수정, 삭제가 자유롭다는 것을 의미해요.

하지만, 필요에 따라 객체를 불변(immutable)으로 만들어야 할 때가 있어요.

예를 들면, 설정 관련 데이터라던가 사용자에게 보내는 메시지 등등 데이터의 무결성을 보장해야할 때 객체를 불변으로 만들어야 해요.

var,let 말고 const 쓰면 해결되는거 아닌가요?

우선 const로 선언된 변수는 재할당이 불가능해요.

하지만 객체와 배열같은 참조형 데이터에서는 값이 훅훅 변할 수 있어요.

const number = 42

number = 50 // 오류 발생: Assignment to constant variable.

원시 데이터 타입(number, string, boolean, ...)을 const로 선언하면 다른 값을 재할당할 수 없어요.

const person = {
  name: 'Minha',
  age: 25,
}

person.age = 21 // 수정 가능
console.log(person.age) // 21

person.city = 'Seoul' // 추가 가능
console.log(person.city) // 'Seoul'

하지만 객체를 const로 선언할 경우, 객체의 속성을 변경할 수 있어요. 즉 const불변성을 제공하지는 않습니다.

그러면 불변성은 어떻게 보장하죠?

Object.freeze()메서드를 사용하면 객체의 속성을 추가, 삭제, 수정할 수 없게 만들어요.

const person = {
  name: 'Minha',
  age: 25,
}

Object.freeze(person)

person.age = 26 // 수정 불가
console.log(person.age) // 25

person.city = 'Seoul' // 추가 불가
console.log(person.city) // undefined

위와 같이 Object.freeze()메서드는 객체를 동결하여 해당 객체의 속성을 변경할 수 없도록 해요.

Object.seal()메서드도 불변성을 다루는 메서드예요.

편지지에서 봉인의 표시로 붙이는 종이라는 뜻인 seal 처럼 해당 메서드는 객체를 봉인해 새로운 속성을 추가할 수 없게 해요.
하지만 Object.freeze()와는 달리 기존 속성의 수정이 가능해요!

const person = {
  name: 'Minha',
  age: 25,
}

Object.seal(person)

person.age = 21 // 수정 가능
console.log(person.age) // 21

person.city = 'Seoul' // 추가 불가
console.log(person.city) // undefined

하지만... 두 메서드가 완벽한 불변성을 제공하지는 않아요.

Object.seal()Object.freeze() 모두 얕은 동결만 수행하기 때문에 중첩된 객체의 속성에 대해서는 변경이 가능해요.

const person = {
  name: 'Minha',
  age: 25,
  address: {
    city: 'Seoul',
    country: 'Korea',
  },
}

// 객체를 동결
Object.freeze(person)

// 중첩된 객체의 속성 수정 시도
person.address.city = 'Busan' // 수정 가능
console.log(person.address.city) // "Busan"

address안에 중첩된 city 속성은 동결이 되지 않았고, 수정이 가능한걸 볼 수 있어요.

중첩된 객체까지 모두 동결해야하는 상황이라면 deepFreeze를 구현해야 해요.

function deepFreeze(object) {
  const propNames = Object.getOwnPropertyNames(object)

  for (let name of propNames) {
    const value = object[name]
    if (value && typeof value === 'object') {
      deepFreeze(value)
    }
  }

  return Object.freeze(object)
}

// 사용 예
const person = {
  name: 'Minha',
  address: {
    city: 'Seoul',
    country: 'Korea',
  },
}

deepFreeze(person)

person.address.city = 'Busan' // 무시됨
console.log(person.address.city) // "Seoul"

deepFreeze()함수를 사용하면 중첩된 객체까지 모두 동결되는걸 확인할 수 있어요!

중첩된 객체를 동결해야 한다면 deepFreeze 사용을 고려해보고, 외에는 Object.freeze()Object.seal()을 상황에 맞게 잘 사용한다면 객체의 불변성을 보장하며, 예기치 않은 오류를 방지할 수 있어요.