정보를 은닉화할 수 있는 방법
1. underscore (_)
필드앞에 밑줄을 붙여서 외부외서 접근할 수 없는 숨겨진 필드임을 나타내는 방식이다.
const obj = {
_private: 'foo',
get public() {
return this._private;
},
}
obj._private // 'foo'
obj.public // 'foo'
자바스크립트의 모든 객체는 외부에서 접근이 허용되기 때문에 위의 예시처럼 실제로 은닉이 되지는 않는다. 하지만 쉽게 적용할 수 있고 널리 알려진 컨벤션이라 앞에 밑줄이 붙은 변수나 프로퍼티를 종종 볼 수 있다.
그렇지만 시간이 지나면서 다른 방법들도 등장했고 만약 모듈을 만들어서 배포하는 경우, 사용자들이 모든 필드에 접근할 수 있는 것은 큰 문제이기 때문에 사용하지 않는 것이 좋다. 에어비앤비 스타일 가이드에서도 밑줄을 사용하지 않도록 하고 있다.
2. closure
정의된 스코프의 환경을 참조하는 클로저의 특징을 활용하면 외부에서 접근할 수 없도록 은닉할 수 있다.
function ObjFactory() {
const private = 'foo';
return {
get public() {
return private;
},
};
}
const obj = ObjFactory();
obj.private // undefined
obj.public // 'foo'
ObjFactory가 생성하는 객체의 클로저(public 게터)에서 private 값에 접근할 수 있지만 외부에서는 접근하려고 하면 undefined로 출력되면서 접근할 수 없는 것을 볼 수 있다.
객체 인스턴스가 생성될 때마다 서로 다른 클로저 함수가 만들어지기 때문에 인스턴스를 많이 생성할 경우 메모리 문제가 생길 수 있다.
3. Symbol
ES2015에서 추가된 Symbol타입을 사용해서 값을 은닉시켜볼 수도 있다.
function ObjFactory() {
const private = Symbol('private');
return {
[private]: 'foo',
}
}
const obj = ObjFactory();
obj[Symbol('private')] // undefined
객체를 생성하는 함수 내에서 만든 심볼을 프로퍼티 키값으로 지정하면 ObjFactory 외부에서는 일반적인 접근으로 프로퍼티 값을 읽을 수 없다.
하지만 Object.getOwnPropertySymbols() 메소드로 심볼로 된 프로퍼티 키를 얻을 수 있으므로 실제로 프로퍼티에 접근할 수 없는 것은 아니다.
Object.getOwnPropertySymbols(obj) // [Symbol(private)]
4. WeakMap
앞서 보았듯이 클로저를 활용한 데이터 은닉방식에는 객체마다 함수가 생성되므로 메모리 누수가 발생할 수 있다.
이를 prototype을 사용해서 메소드를 공유하게 하여 방지할 수 있다.
function makeObjFactory() {
const privates = new Map();
function ObjFactory(initialVal) {
privates.set(this, { private: initialVal })
}
ObjFactory.prototype.public = function() {
return privates.get(this).private;
}
return ObjFactory;
}
const ObjFactory = makeObjFactory();
const obj1 = new ObjFactory('foo');
const obj2 = new ObjFactory('bar');
// 숨겨진 데이터에 접근할 수 없다
obj1.privates // undefined
obj1.private // undefined
// 공개된 API로만 캡슐화된 데이터에 접근할 수 있다
obj1.public() // 'foo'
obj2.public() // 'bar'
// 두 객체는 하나의 메소드(public)을 프로토타입으로 공유하고 있다
obj1.public === obj2.public // true
하지만 여기서 발생할 수 있는 또다른 문제점은privates Map에 저장된 숨겨진 데이터들이 객체가 사라진 이후에도 가비지 컬렉팅이 되지 않아 메모리 누수를 일으킬 수 있다는 점이다.
예를 들어 obj1 = null을 수행하여 obj1 객체를 제거해도 privates의 'foo'값은 GC에 의해 제거되지 않고 남아있다.
이를 해결하기 위해 WeakMap을 사용할 수 있다. WeakMap은 약한 참조를 하고 있기 때문에 프로퍼티 키인 객체가 메모리에서 제거되면 가비지 컬렉팅이 되도록한다.
사용방법도 Map과 흡사하기 때문에 아래와 같이 privates 선언만 바꿔주면 된다.
const privates = new WeakMap();
5. Private class fields (#)
ES2019부터 클래스에 private field가 추가되었다.
샵#을 필드 또는 메소드 명 앞에 프리픽스로 붙여주면 된다.
class Factory {
#privateField = 'foo'
publicField = 'bar'
#privateMethod() {
return 'baz'
}
publicMethod() {
return this.#privateField;
}
}
const obj = new Factory()
obj.#privateField // Uncaught SyntaxError: Private field '#privateMehtod' must be declared in an enclosing class
obj.publicField // 'bar'
obj.#privateMethod() // Uncaught SyntaxError: Private field '#privateMehtod' must be declared in an enclosing class
obj.publicMethod() // 'foo'
'Script > JavaScript' 카테고리의 다른 글
[JavaScript] Chapter 38. 추상 클래스, 추상 메소드 (0) | 2023.03.31 |
---|---|
[JavaScript] Chapter 36. 고차 함수와 콜백(Callback) – 일급 객체란 (0) | 2023.02.07 |
[JavaScript] Chapter 35. 일반 함수 vs 익명 함수 (0) | 2023.01.26 |
[JavaScript] Chapter 34. 맵 객체(Map Object) (0) | 2023.01.16 |
[JavaScript] Chapter 33. 객체 복사하기 - 앝은 복사, 깊은 복사 (0) | 2023.01.16 |
댓글