Published on

Private Properties in Javascript

Authors
  • avatar
    Name
    Luffy Yeon
    Twitter

Private Properties in Javascript

Javascript에서 숨기고 싶은 Private Properties를 가지고 싶을 때가 있습니다. Javascript에서 Private 한 Member를 가지는 방법을 몇가지 정리해 봅니다.




Unprotected Senario

아래는 Properties가 보호되지 못하는 경우를 살펴 보도록 구성된 코드입니다. 생성된 객체의 Member에 쉽게 접근하여 제거가 가능한 것을 보실 수 있습니다.

const Person = (function () {
function Person(name) {
this.name = name
}
Person.prototype.getName = function () {
return this.name
}
return Person
})()
const p = new Person('Yeon')
console.log(p.getName()) // Yeon
delete p.name
console.log(p.getName()) // undefined

다음과 같이 생성된 객체의 Member가 손쉽게 지워진다면 코드상에서 존재하지 않는 Member에 접근하게 되는 경우가 생길 수 있고 오류를 유발 할 수도 있을 것입니다.



Naming Convention

숨긴다고 하기엔 부족하지만 개발자들끼리 약속하여 Private 한 Memeber로 사용하고 싶은 이름 앞에 ' _ '를 붙여 외부에서 사용하는 것을 약속하는 형태가 있습니다. 해당 방법은 간단하지만 약속이 잘 지켜지는지... 그리고 외부에서 손쉽게 Member에 접근이 가능하기 때문에 조금은 허술한 방법이라 추천드리지 않습니다.



Using Closure

Closure를 이용하여 Member를 감추어 외부에서 접근 할 수 없도록 하는 방법입니다. 내부 Closure에 포함된 Member는 Private하게 사용하기는 좋지만 Closure를 사용할 경우 객체 생성시마다 해당 인스턴스를 위한 Closure를 생성해야 하므로 메모리 누수 등의 문제점이 있어 사용시 유의가 필요합니다.

const Person = (function () {
function Person(name) {
this.getName = function () {
return name
}
}
return Person
})()
const p = new Person('Yeon')
console.log(p.getName()) // Yeon
delete p.name
console.log(p.getName()) // Yeon


Using Symbol

다음 방법은 ES6 Symbol을 사용하여 Member를 Private하게 숨길 수 있습니다. Symbol을 객체의 키 값으로 사용하여 Member를 맵핑 할 경우 해당 Symbol을 통해서만 Member에 접근이 가능하도록 하는 방법입니다.

const Person = (function () {
const nameSymbol = Symbol()
function Person(name) {
this[nameSymbol] = name
}
Person.prototype.getName = function () {
return this[nameSymbol]
}
return Person
})()
const p = new Person('Yeon')
console.log(p.getName()) // Yeon
delete p.name
console.log(p.getName()) // Yeon

위의 방법으로 Symbol을 이용한 Member 접근으로 Private하게 Member를 만들수 있었습니다. 하지만 해당 방법은 Symbol이 노출될 경우 Member에게 접근 할 수 있습니다. Object.getOwnPropertySymbols를 사용하는 경우 객체의 Symbol을 가져와 Symbol로 맵핑된 Member에 접근할 수 있게 됩니다.

const Person = (function () {
const nameSymbol = Symbol()
function Person(name) {
this[nameSymbol] = name
}
Person.prototype.getName = function () {
return this[nameSymbol]
}
return Person
})()
const p = new Person('Yeon')
console.log(p.getName()) // Yeon
const pSymbol = Object.getOwnPropertySymbols(p)[0]
delete p[pSymbol]
console.log(p.getName()) // undefined

이렇게 Symbol을 알 수 있다면 Member에 접근 할 수 있게 되버리므로 완벽한 Private 한 Member로는 보장 받지 못합니다.



Using WeakMap

마지막으로 가장 강력하게 Private 한 Member를 만들 수 있는 방법은 WeakMap을 사용하는 방법이 있습니다.

const Person = (function () {
const private = new WeakMap()
function Person(name) {
const privateProperties = {
name: name,
}
private.set(this, privateProperties)
}
Person.prototype.getName = function () {
return private.get(this).name
}
return Person
})()
const p = new Person('Yeon')
console.log(p.getName()) // Yeon
delete p.name
console.log(p.getName()) // Yeon

추가로 WeakMap을 사용할 경우 얻을 수 있는 이점으로는
Javascript에서 Map -> Key Value 형태의 Object에서의 구성인 경우 Key로 사용되던 Object가 메모리에서 사라질 경우 값에 대한 Key의 역할이 불필요해집니다. 그래서 Key에 해당하는 내용을 제거해주는 작업이 필요합니다. 하지만 WeakMap을 사용할 경우 메모리에서 사라진 Key에 대한 삭제가 자동으로 이루어집니다.