
객체의 불변성은 안전한 프로그래밍을 위해 매우 중요하다. 자바스크립트는 이를 위해 Object.freeze라는 내장 메서드를 제공하는데, 이름 그대로 객체를 '얼려서' 변경할 수 없게 만든다.
객체를 동결시켜 속성 추가, 수정, 삭제를 방지
const STATUS = Object.freeze({
PENDING: 'PENDING',
SUCCESS: 'SUCCESS',
FAIL: 'FAIL'
});
// ❌ 수정 시도
STATUS.NEW_PROP = 'P2'; // 작동하지 않음
STATUS.PENDING = 'P'; // 작동하지 않음
delete STATUS.FAIL; // 작동하지 않음
console.log(STATUS); // { PENDING: 'PENDING', SUCCESS: 'SUCCESS', FAIL: 'FAIL' }
// ✅ 동결 확인
console.log(Object.isFrozen(STATUS)); // true
여기까지는 잘 작동한다. 하지만 중첩된 객체에 사용할 때 문제가 생긴다.
const STATUS = Object.freeze({
PENDING: 'PENDING',
SUCCESS: 'SUCCESS',
FAIL: 'FAIL',
OPTIONS: {
GREEN: 'GREEN',
RED: 'RED'
}
});
// 중첩된 객체는 여전히 수정 가능
STATUS.OPTIONS.GREEN = 'G'
STATUS.OPTIONS.YELLOW = 'Y'
delete STATUS.OPTIONS.RED
console.log(STATUS.OPTIONS); // { GREEN: 'G', YELLOW: 'Y' }
왜 이런 일이 발생할까? Object.freeze는 "얕은 동결(shallow freeze)"만 수행하기 때문이다. 즉, 객체의 직접적인 속성만 동결하고 중첩된 객체는 동결하지 않는다.
function deepFreeze(targetObj) {
// 1. 객체를 순회
// 2. 값이 객체인지 확인
// 3. 객체면 재귀
// 4. 그렇지 않으면 Object.freeze
return Object.freeze(targetObj);
}
lodash와 같은 대중적인 유틸리티 라이브러리 활용
readonly 키워드로 컴파일 타임에 불변성 보장
실제 프로젝트에서는 팀의 상황에 맞는 방법을 선택하면 된다. 특히 TypeScript를 사용하는 프로젝트라면 readonly를 활용하는 것이 좋은 선택이다.
결론적으로, Object.freeze는 매우 유용하지만 깊은 동결이 필요한 경우에는 추가적인 처리가 필요하다. 팀 프로젝트에서는 이러한 제한사항을 이해하고, 필요에 따라 deepFreeze를 구현하여 사용하는 것이 안전하다 💪