JavaScript에서의 캡슐화(Encapsulation)와 정보 은닉(Information Hiding)

Novelike·2025년 5월 11일
0

Tech

목록 보기
8/8
post-thumbnail

📚 목차

  1. 캡슐화와 정보 은닉: 정의와 차이점
  2. JavaScript에서의 구현 방법
  3. ES6 이후 개선점 정리
  4. 실제 사용 예시 및 상황별 팁

캡슐화와 정보 은닉: 정의와 차이점

캡슐화(Encapsulation)는 데이터와 함수를 하나의 컴포넌트(예: 클래스)에 묶고, 해당 컴포넌트에 대한 접근을 제어하여 객체를 블랙박스처럼 만드는 개념이다. 정보를 은닉(Information Hiding)은 모듈이나 객체의 내부 구현 세부사항을 외부에 노출하지 않고, 인터페이스만 제공하여 변경 가능성을 최소화하는 원칙이다. 따라서 캡슐화는 데이터를 물리적으로 묶어 관리하는 방법이라면, 정보 은닉은 그 내부 동작을 숨기는 행위를 말한다. 객체지향 설계에서 캡슐화는 정보 은닉을 실현하기 위한 수단으로 사용된다.

JavaScript에서의 구현 방법

자바스크립트는 프로토타입 기반 언어이므로 전통적인 클래스 기반 언어의 접근 제어와는 다르다. 자바스크립트에서 캡슐화/정보 은닉을 구현하는 주요 방법은 다음과 같다.

클로저를 통한 은닉

클로저는 함수가 선언된 어휘적 환경(렉시컬 환경)을 기억하므로, 내부 변수를 외부에서 직접 접근할 수 없도록 한다. 이를 통해 상태(state)와 함수를 번들링하여 객체와 유사한 구조를 만들 수 있다. 예를 들어 즉시 실행 함수(IIFE) 패턴을 사용해 카운터를 구현하면 다음과 같다:

function createCounter() {
  let count = 0;
  return {
    increment() { count++; },
    getCount() { return count; }
  };
}
const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 1

위 예제에서 count 변수는 createCounter 함수 내부에 존재하므로 외부에서 직접 접근할 수 없다. incrementgetCount 메서드만 공개되어 데이터의 변경과 조회를 제어한다. 이러한 패턴은 모듈 패턴 또는 Revealing Module Pattern으로 불리며, 자바스크립트에서 널리 사용된다.

심볼(Symbol)을 통한 은닉

Symbol은 고유한 식별자를 생성하여 객체의 프로퍼티 키로 사용할 수 있다. MDN에 따르면 심볼 키는 일반적인 방법으로 접근하기 어려운 형태로 숨길 수 있어 약한 형태의 캡슐화나 정보 은닉을 구현할 수 있다. 예를 들어:

const _secret = Symbol('secret');
class MyClass {
  constructor(secretValue) {
    this.name = 'Alice';
    this[_secret] = secretValue;
  }
  getSecret() {
    return this[_secret];
  }
}
const obj = new MyClass('hidden');
console.log(Object.keys(obj)); // ["name"] (심볼 키는 표시되지 않음)
console.log(obj.getSecret());  // "hidden"

위 코드에서 _secret은 심볼이기 때문에 Object.keys()나 JSON 변환 등에 노출되지 않는다. 그러나 주의할 점은 심볼도 완전한 비공개를 제공하지 않는다는 것이다. Symbol로 선언한 프로퍼티는 런타임에 리플렉션 API(Object.getOwnPropertySymbols 등)로 접근할 수 있으며, 내부 값이 노출될 수 있다.

# Private 필드를 통한 은닉

최신 자바스크립트(ES2022)에서는 클래스 내부에 # 접두사를 붙여 진정한(private) 필드/메서드를 선언할 수 있다. MDN에 따르면 #로 만든 private 속성은 클래스 외부에서 합법적으로 참조할 수 없으며, 자바스크립트 엔진이 접근을 차단한다. 또한 클래스 외부에서 # 필드를 접근하면 문법 오류가 발생한다. 예를 들어:

class SecretHolder {
  #secret;
  constructor(secret) {
    this.#secret = secret;
  }
  reveal() {
    return this.#secret;
  }
}
const sh = new SecretHolder('top secret');
console.log(sh.reveal()); // 'top secret'
console.log(sh.#secret);  // SyntaxError: private field '#secret' must be declared in an enclosing class

위 예제에서 #secret은 클래스 외부에서 접근할 수 없으며, 자바스크립트가 이를 강제한다. 이 문법 도입 이전에는 WeakMap이나 클로저로 비공개 필드를 흉내 냈지만, MDN에 따르면 이러한 방법은 # 문법만큼 편리하지 않다.

기타 은닉 기법

ES6 이전에는 _ 접두사(named convention)나 WeakMap을 사용해 비공개 프로퍼티를 구현하기도 했다. 예를 들어 WeakMap을 사용하면 인스턴스별 비공개 데이터를 안전하게 저장할 수 있다:

const privates = new WeakMap();
class Example {
  constructor(secret) {
    privates.set(this, { secret });
  }
  getSecret() {
    return privates.get(this).secret;
  }
}

또한 ES6 모듈(import/export)을 사용하면 모듈 범위 자체가 은닉을 제공한다. 모듈에서 export하지 않는 변수는 모듈 외부에서 접근할 수 없으므로 구현 세부사항이 자연히 숨겨진다.

ES6 이후 개선점 정리

  • 클래스 문법 도입: ES6의 class와 이후 클래스 필드(initializer), # private 필드/메서드 등이 추가되어 문법적으로 캡슐화 지원이 강화되었다.
  • Symbol: ES6 도입 이후 Symbol이 지원되며, 객체 프로퍼티 충돌 방지와 약한 은닉에 사용된다.
  • WeakMap: ES6부터 사용할 수 있는 WeakMap은 객체를 키로 사용해 비공개 데이터를 저장하기에 유용하며, 키 객체가 가비지 콜렉션 대상이 되면 자동 소멸되어 메모리 누수를 줄인다.
  • 모듈 스코프: ES6 모듈 도입 이후 파일 자체가 모듈로 동작하므로, export되지 않은 식별자는 외부에서 보이지 않는다. 이를 통해 자연스럽게 은닉을 달성할 수 있다.
  • 비공개 메서드 및 접근자: ES2022 이후 #privateMethod, #privateGetter/#privateSetter 등이 지원되어 클래스 내부에 완전한 은닉 메서드/접근자를 정의할 수 있다.

실제 사용 예시 및 상황별 팁

  • 모듈/클로저: 독립된 코드 영역이나 라이브러리 내부 상태를 감추고 싶을 때 유용하다. 예를 들어 Node.js나 브라우저의 ES 모듈에서는 파일 범위가 기본적으로 은닉을 제공하므로, 파일 밖으로 노출하지 않으면 해당 데이터가 은닉된다.
  • 클로저 vs 클래스: 간단한 함수 팩토리나 즉시 실행 함수 패턴은 레거시 브라우저 호환성이나 간단한 모듈에서 선호된다. 반면 ES6+ 문법을 사용할 수 있다면 class# private 필드를 사용하는 것이 명시적이고 유지보수가 쉽다.
  • 심볼 사용 시기: 심볼은 외부 라이브러리와 키 충돌을 피하거나, 완전 비공개가 필요 없는 메타데이터를 저장할 때 사용한다. 그러나 리플렉션 API로 접근 가능하므로 절대 노출되면 안 되는 데이터에는 적합하지 않다.
  • 언더스코어(_) 컨벤션: 실제로 강제 은닉을 제공하지는 않지만, 코드 스타일 가이드로서 변수나 메서드명 앞에 _를 붙여서 암묵적으로 비공개임을 표시하는 관습이 있다.
  • TypeScript private: TypeScript의 private 접근제어자는 컴파일 타임 검사를 위한 것일 뿐 런타임에는 일반 프로퍼티이므로 진정한 은닉을 보장하지 않는다. 진짜 은닉이 필요하면 # 문법이나 클로저를 사용해야 한다.
  • 퍼포먼스 고려: 클로저나 WeakMap을 지나치게 사용하면 메모리 사용량에 영향을 줄 수 있으므로, 빈번한 객체 생성/삭제가 일어나는 경우 유의가 필요하다.
profile
주니어 개발자

0개의 댓글