Object의 값 등을 수정하지 못하도록 하도록 하는 방법은 다양하다.
왜 이러한 경우가 필요한지 궁금한 사람들을 위해 예시 케이스를 설명하자면,
const CONFIG = { IS_DEV: true, API_APTH: '/my-api', }; export default CONFIG;
위와 같은 config 값이 있다고 할때 JS에서는 이 값을 import하여 수정할 수 있다.
import * as CONFIG from '@/constants'; CONFIG.IS_DEV = false; CONFIG.API_PATH = null;
이렇게 되면 서비스 코드가 의도대로 동작하지 않을 수 있을 뿐더러 코드가 오염될 수 있다.
TypeScript 등을 사용하면 간단하게 값을 수정하지 못하도록 만들 수 있지만 일반 JS에서는 아래에서 설명할 방법들을 사용해야한다.
1. Object.freeze()
let obj = {}; Object.freeze(obj); // 오브젝트는 수정할 수 없는 상태가 된다. obj.test = 1; // obj.test에 임의로 변수를 대입한다. console.log(typeof obj.test); // "undefined" 하지만 오브젝트가 freeze 상태라 변경이 불가능하다.
Object.freeze(
obj
)
는 해당 오브젝트를 더 이상 수정할 수 없는 상태로 만든다.말 그대로 오브젝트를 동결시킨다는 의미이다.
새로운 property를 추가할 수 없으며 새로운
__proto__
를 추가할 수도 없다.배열에도 역시 사용이 가능하다.
let obj = [1, 2, 3]; Object.freeze(obj); obj.push(4) // Uncaught TypeError: Cannot add property 3, object is not extensible
하지만 freeze 되는 것은 정말 대상 오브젝트 뿐이이고, 중첩되어 내부에 있는 오브젝트에는 접근할 수 있다.
이를 얕은 동결이라고 한다.
let obj = { test: {} }; // 이 오브젝트에는 test라는 객체가 있다. Object.freeze(obj); // 이 오브젝트를 freeze 시키지만 obj.test.a = 1; // obj.test에 새로운 property를 추가할 수 있는 모습이다. console.log(obj.test.a); // 1 Object.isFrozen(obj); // true Object.isFrozen(obj.test); // false
만약 중첩된 오브젝트 역시 동결시키고 싶다면 mdn에서 권장하는 deepFreeze 함수를 정의해 사용하면 좋을 것 같다.
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); }
2. Object.seal()
Object.freeze()
와 비슷하지만 Object.seal()
을 사용하면 이미 존재하는 Object의 값을 수정할 수 있지만, 추가하거나 삭제는 불가능하다.let obj = { a: '' }; Object.seal(obj); // Object.seal을 통해 seal 상태로 만든다. obj.a = 'HELLO'; // obj.a의 내용은 "HELLO"로 변경되지만 obj.b = 'WORLD!'; // obj.b는 새로 추가되지 않았다. delete obj.a; // obj.a는 삭제되지 않는다. console.log(obj); // {a: "HELLO"}
배열에도 역시 사용이 가능하다.
let obj = [1, 2, 3]; Object.seal(obj); test.push(4); // Uncaught TypeError: Cannot add property 3, object is not extensible test[2] = 4; // 정상적으로 작동한다. console.log(test) // [1, 2, 4]
Object.seal() 역시 얕은 동결과 같이 중첩된 오브젝트에는 접근이 가능하다.
let obj = { a: {} }; Object.seal(obj); Object.isSealed(obj) // true Object.isSealed(obj.a) // false obj.a.test = "HELLO WORLD!"; console.log(obj); // { a: { test: "HELLO WORLD!" } }
이 문제도 위와 같은 방식으로 deepSeal로 처리하면 될 것 같다.
function deepSeal(object) { var propNames = Object.getOwnPropertyNames(object); for (let name of propNames) { let value = object[name]; object[name] = value && typeof value === "object" ? deepSeal(value) : value; } return Object.seal(object); }
3. Object.defineProperty()
Object에 값을 할당할 때에는 아래와 같이 할당 연산자를 통해 할당하는 것이 일반적이다.
const obj = {}; obj.key = 'value';
다른 방법으로
Object.defineProperty()
메소드를 통해서 값을 할당할 수 있다.이때 세 번째 parameter로 옵션이 들어가는데,
writable
및 configurable
값을 할당해줄 수 있다.이 값을
false
로 변경하면 obj.key
의 값을 외부에서 수정할 수 없다.const obj = {}; Object.defineProperty(obj, 'key', { value: 10, writable: false, // 값 수정 가능 여부 configurable: false, // 값 속성 수정 가능 여부 (값의 삭제 및 추가 불가) }); console.log(obj.value); // 10 obj.key = 20; console.log(obj.value); // 10, writable이 false이기 때문에 속성 값을 변경할 수 없음 delete obj.key; // false, configurable가 false이기 때문에 속성을 제거 할 수 없음 console.log(obj.value); // 10