본문 바로가기
프로그래밍

JavaScript에서 Symbol과 고유성 활용

by 이음코드 2024. 12. 30.
반응형

JavaScript는 ES6(ECMAScript 2015)부터 Symbol이라는 새로운 원시 데이터 타입을 도입했습니다. Symbol은 객체의 속성 키로 사용할 수 있는 고유하고 변경 불가능한 값을 생성하는데 사용됩니다. 본 글에서는 Symbol의 개념, 주요 특징, 사용 방법, 그리고 고유성을 활용하는 실질적인 사례를 상세히 설명합니다.

1. Symbol 이란?

Symbol은 객체의 속성 키로 사용되는 고유한 값을 생성하는 특별한 데이터 타입입니다.
- 특징

  • 고유성 보장 : 생성된 Symbol 값은 항상 고유하며, 동일한 값이 중복되지 않습니다.
  • 변경 불가능 : Symbol은 원시 값으로, 생성 후 수정할 수 없습니다.
  • 객체 속성 키로 활용 가능 : 일반 문자열 키와 달리 Symbol은 고유성을 가지므로 충돌 가능성이 없습니다.


(1) Symbol 생성 방법
Symbol은 Symbol() 함수를 사용하여 생성합니다.  다음은 기본적인 예제입니다.

const sym1 = Symbol();
const sym2 = Symbol();

console.log(sym1 === sym2); // false (항상 고유함)

Symbol() 함수는 선택적으로 “설명(description)”을 받을 수 있습니다.  이 설명은 디버깅 목적으로만 사용되며 Symbol의 고유성에 영향을 미치지 않습니다.

const symWithDesc = Symbol("example");
console.log(symWithDesc.description); // "example"


2. Symbol의 주요 용도

(1) 객체 속성 키로 활용
JavaScript 객체의 속성 키는 기본적으로 문자열 또는 Symbol로 사용할 수 있습니다. Symbol을 키로 사용하면 이름 충돌을 방지할 수 있습니다.

const uniqueKey = Symbol("unique");
const obj = {
  [uniqueKey]: "This is a unique value"
};

console.log(obj[uniqueKey]); // "This is a unique value"
console.log(Object.keys(obj)); // [] (Symbol 키는 열거되지 않음)
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(unique)]


(2) Symbol과 열거 불가능한 속성
Symbol로 정의된 객체의 속성은 for … in 루프나 object.keys()로 열거되지 않습니다. 따라서 민감한 데이터나 내부적으로만 사용해야 하는 속성을 정의하는데 유용합니다.

const secretKey = Symbol("secret");
const user = {
  name: "John",
  age: 30,
  [secretKey]: "Sensitive Data"
};

for (const key in user) {
  console.log(key); // name, age (Symbol은 출력되지 않음)
}

console.log(Object.keys(user)); // ["name", "age"]
console.log(user[secretKey]); // "Sensitive Data"


3. Symbol의 고유성 활용 사례

(1) 이름 충돌 방지
여러 모듈이나 라이브러리에서 동일한 키를 사용하여 객체를 확장하려 할 때 이름 충돌이 발생할 수 있습니다. Symbol을 사용하면 이러한 문제를 방지할 수 있습니다.

const LIBRARY_KEY = Symbol("libraryKey");
const obj = {};

obj[LIBRARY_KEY] = "Library-specific value";

console.log(obj[LIBRARY_KEY]); // "Library-specific value"

(2) 메타프로그래밍과 Symbol
JavaScript는 내장된 Symbol을 통해 객체의 동작을 재정의하거나 커스터마이징할 수 있습니다. 이들을 Well-Known Symbols라고 하며, JavaScript 엔진에서 특별한 의미를 가집니다.
- Symbol.iterator

const iterableObj = {
  data: [1, 2, 3],
  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => {
        if (index < this.data.length) {
          return { value: this.data[index++], done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
};

for (const value of iterableObj) {
  console.log(value); // 1, 2, 3
}

- Symbol.toStringTag
Symbol.toStringTag를 사용하면 Object.prototype.toString의 출력값을 사용자 정의할 수 있습니다.

const customObj = {
  [Symbol.toStringTag]: "CustomObject"
};

console.log(Object.prototype.toString.call(customObj)); // [object CustomObject]


4. 실무에서의 Symbol 활용 예제

(1) 전역 레지스트리(Symbol.for)
Symbol.for 메서드는 전역 Symbol 레지스트리에서 공유되는 Symbol을 생성하거나 검색합니다. 동일한 키로 생성된 Symbol은 항상 동일한 값을 반환합니다.

const globalSym1 = Symbol.for("sharedKey");
const globalSym2 = Symbol.for("sharedKey");

console.log(globalSym1 === globalSym2); // true

이 기능은 여러 모듈 간에 동일한 Symbol을 공유할 때 유용합니다.

5. Symbol의 한계

Symbol은 고유성과 캡슐화를 제공하지만 몇 가지 한계도 있습니다.
(1) 직렬화 불가능 : Symbol은 JSON.stringify로 직렬화할 수 없습니다.

const obj = { key: Symbol("value") };
console.log(JSON.stringify(obj)); // "{}"

(2) 디버깅 어려움 : Symbol 값은 디버깅 시 표시되지 않는 경우가 많아 코드 가독성이 떨어질 수 있습니다.
(3) 대중적 사용 부족 : 일부 개발자는 Symbol의 사용에 익숙하지 않아 협업시 이해의 어려움이 있을 수 있습니다.

6. 결론

Symbol은 JavaScript에서 고유성과 충돌 방지를 보장하는 강력한 도구입니다. 객체의 속성을 안전하게 정의하거나 메타프로그래밍을 통해 동작을 커스터마이징하는 등 다양한 용도로 활용할 수 있습니다. 그러나 Symbol의 고유성에 따른 한계도 존재하므로 상황에 따라 적절히 사용하는 것이 중요합니다.

* 참고 자료

  • MDN Web Docs : Symbol
  • ECMA-262 Specification : Symbol Objects

이글은 공개된 자료를 기반으로 작성되었습니다.

반응형