클로저를 활용한 비공개 멤버의 기본 원리
JavaScript에서는 생성자 함수 내부에 let 또는 var로 변수를 선언하면, 클로저(closure) 덕분에 외부에서 직접 접근할 수 없는 비공개(private) 멤버를 만들 수 있습니다. 이렇게 선언된 변수는 함수 내부에서 정의된 메서드(소위 특권 메서드)를 통해서만 접근할 수 있으며, 객체 속성처럼 obj.personName 형태로 접근하려 하면 undefined가 반환됩니다.
흥미로운 점은 각 인스턴스가 생성될 때마다 새로운 클로저 스코프가 만들어진다는 것입니다. 따라서 아래 예제처럼 여러 인스턴스가 존재할 경우, 각 인스턴스는 서로에게 영향을 주지 않는 독립적인 비공개 멤버를 가지게 됩니다.
예제 코드
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
.result {
font-size: 18px;
font-weight: 500;
color: blueviolet;
}
</style>
</head>
<body>
<h1>Share private members among common instances in JavaScript</h1>
<div class="result"></div>
<br />
<button class="Btn">Click Here</button>
<h3>Click on the above button to display the personName property</h3>
<script>
let resEle = document.querySelector(".result");
let BtnEle = document.querySelector(".Btn");
function Person(name) {
// let으로 선언했기 때문에 외부에서 직접 접근 불가
let personName = name;
// 클로저를 통해 personName에 접근하는 특권 메서드
this.returnName = function () {
return personName;
};
}
let person1 = new Person("Rohan");
let person2 = new Person("Shawn");
BtnEle.addEventListener("click", () => {
resEle.innerHTML =
"person1.returnName() = " + person1.returnName() + "<br>";
resEle.innerHTML +=
"person2.returnName() = " + person2.returnName() + "<br>";
resEle.innerHTML +=
"person1.personName = " + person1.personName + "<br>";
resEle.innerHTML +=
"person2.personName = " + person2.personName + "<br>";
});
</script>
</body>
</html>실행 결과
페이지를 처음 열었을 때의 초기 화면입니다.

'Click Here' 버튼을 클릭하면 다음과 같이 결과가 표시됩니다.

결과 해석
- returnName() 호출:
person1.returnName()은 'Rohan',person2.returnName()은 'Shawn'을 반환합니다. 즉, 각 인스턴스가 자신만의 비공개 멤버 값을 유지하고 있습니다. - 직접 접근 시도:
person1.personName과person2.personName은 모두undefined를 출력합니다. 이는personName이 객체의 속성이 아니라 클로저 내부의 지역 변수이기 때문에 외부에 전혀 노출되지 않았음을 의미합니다.
참고: 최신 JavaScript의 비공개 필드
ES2022부터는 클래스 문법에서 # 접두사를 사용한 비공개 필드(private field)가 표준으로 도입되었습니다. 예를 들어 #personName처럼 선언하면 별도의 클로저 없이도 진짜 비공개 멤버를 만들 수 있습니다. 다만 구형 브라우저 호환성이나 생성자 함수 기반 패턴이 필요한 경우라면, 위 예제처럼 클로저를 활용한 방식이 여전히 널리 사용되는 검증된 접근법입니다.