본문 바로가기
Programming/자바스크립트

[자바스크립트] Symbol 타입

by 코딩하는 랄로 2023. 11. 8.
728x90

Symbol 타입

JavaScript의 심볼(symbol) 타입은 원시 타입 중 하나로, ES6 버전의 JavaScript에서 새롭게 추가된 타입이다. 심볼 타입은 객체의 프로퍼티 키를 고유하게 설정함으로써 프로퍼티 키의 충돌을 방지하기 위해 사용된다.

 

다른 타입에 비해 흔하게 사용되는 타입은 아니지만, JavaScript에는 심볼 타입에 대한 이해가 전제되어야 이해하고 사용할 수 있는 몇몇 문법(EX. iterable 객체)들이 존재하기 때문에 공부해두는 것이 좋다.

 

 

 

Symbol 타입 생성

Symbol 타입의 변수는 Symbol 함수를 호출하여 생성할 수 있다. 이 때 생성되는 심볼은 변경이 불가능한 원시 값이다. Symbol 함수를 호출할 때 넘겨주는 인자는 문자열로, 생성되는 심볼 변수에 대한 설명으로 디버깅의 용도로만 사용이 된다.

// Symbol() 
const sym1 = Symbol();
const sym2 = Symbol('second symbol');
const sym3 = Symbol('this is description');

// 심볼 출력시 해당 description도 같이 보여줌
console.log(sym1);  // Symbol()
console.log(sym2);  // Symbol(second symbol)
console.log(sym3);  // Symbol(this is description)

// typeof Symbol()
console.log(typeof Symbol());  // symbol

 

심볼은 객체의 프로퍼티 키를 고유하게 설정할 때 주로 사용된다고 하였는데, 이는 심볼 함수를 호출할 시 매번 고유한 심볼이 생성됨을 의미한다.

// Symbol() 
// 고유한 값을 가짐
const sym1 = Symbol();
const sym2 = Symbol();

console.log(sym1 === sym1);  //true
console.log(sym1 === sym2);  //false
console.log(sym1 == sym2);  //false

 

심볼은 다른 원시 타입과는 달리, new 생성자를 통한 래퍼 객체를 생성할 수 없다. 

// Symbol() 
// new 생성자를 통한 래퍼 객체X
const sym = new Symbol(); // TypeError: Symbol is not a constructor

 

 

 

Symbol의 활용

심볼은 생성될 때 마다 고유한 심볼 값을 갖기 때문에, 객체의 프로퍼티 키의 충돌을 막기 위해 주로 사용이 된다. 아래의 예제를 살펴보자.

const obj = {
    propertyKey : "propertyValue",
    1 : "javascript"
};

/*

여러 줄의 코드
=> 실수로 이미 있는 키값에 새로운 값을 할당...!!

*/

obj["1"] = "java";

 

코드를 작성하다보면, 실수로 기존의 키 값을 덮어씌우는 일이 종종 발생하고는 한다. 왜냐하면, 일반적으로 객체의 키값은 내부적으로 문자열이 반환되기 때문에 위와 같은 상황에서 무의식적으로 1과 "1"을 다르게 생각하여 할당하는 경우도 생기기도 하고, 단순히 코드가 길어지다보니 잊어먹고 덮어씌워 버리는 경우도 생기기 때문이다.

 

이 때에, symbol을 사용하여 객체의 프로퍼티 키를 지정해주면, 위와 같은 프로퍼티 키의 충돌을 막을 수 있다.

const obj = {};

const sym1 = Symbol("first");
const sym2 = Symbol("second");

obj[sym1] = "propertyValue";
obj[sym2] = "javascript";
obj[Symbol("third")] = "java";

 

 

 

내장 심볼

Symbol 함수를 이용하여 직접 심볼을 생성하고 사용할 수도 있지만, 특별한 용도로 사용되기 위해 JavaScript 엔진 내에 미리 생성되어 상수로 존재하고 있는 내장 심볼(Built-in Symbol)들도 존재한다. 이들은 Symbol 함수의 프로퍼티로서 존재한다 (Symbol 함수도 객체의 일종이기 때문에 프로퍼티를 가질 수 있음).

 

내장 심볼의 가장 대표적인 예시가 바로 Symbol.iterator이다. JavaScript 엔진은 이 심볼을 키로 갖는 메소드가 정의된 객체를 iterable 객체로 인식한다. ( iterable 객체에 대해 자세히 알고 싶다면 => 자바스크립트 iterable 객체 ) Symbol.iterator를 키로 갖는 메소드를 정의해둔 내장 iterable 객체들의 예시로는 다음과 같은 것들이 있다.

Array.prototype[Symbol.iterator];

String.prototype[Symbol.iterator];

Map.prototype[Symbol.iterator];

Set.prototype[Symbol.iterator];

arguments[Symbol.iterator];

NodeList.prototype[Symbol.iterator];

HTMLCollection.prototype[Symbol.iterator];

 

 

 

Symbol의 for(), keyFor() 메소드

여러 모듈(객체)들이 하나의 심볼을 공유하기 위해서는, 전역 심볼 레지스트리(Global Symbol Registry)라는 심볼들이 저장될 수 있는 전역 공간을 이용하여야 한다. 또한, 심볼이 키를 가지고 있어야, 해당 키를 통해 전역 심볼 레지스트에 이미 존재하는 심볼을 찾아 공유할 수 있게 된다.

 

하지만, 심볼 함수를 통해 생성된 심볼은 키를 가지고 있지도 않고, 전역 심볼 레지스트리에 저장되지도 않기 때문에 심볼 객체가 가지고 있는 메소드를 활용하여야 한다.

 

 

for()

인자로 전달받은 문자열 값을 키로 갖는 심볼을 전역 심볼 레지스트리에 찾아 반환하고, 만약 없으면 해당 인자를 키 값으로 값는 새로운 심볼을 생성하여 전역 심볼 레지스트리에 저장한 반환한다.

const sym1 = Symbol.for("first");
const sym2 = Symbol.for("first");
const sym3 = Symbol.for("second");

console.log(sym1 === sym2);  // true
console.log(sym2 === sym3);  // false

 

 

keyFor()

인자로 전달받은 심볼을 전역 심볼 레지스트리에서 찾은 뒤, 해당 심볼의 키값을 반환하는 메소드이다. 만약 없다면 undefined를 반환한다. 

const sym1 = Symbol.for("first");
const key1 = Symbol.keyFor(sym1);

console.log(key1);  // first


const sym2 = Symbol("second");
const key2 = Symbol.keyFor(sym2);

console.log(key2);  // undefined

 

위의 코드를 통해서, Symbol() 함수를 통해 일반적으로 생성하는 symbol은 전역 심볼 레지스트리에 저장되지 않고, for 메소드를 사용하여야만 저장되는 것을 확인할 수 있다.

 

 

 

주의할 점

심볼은 주로 객체의 프로퍼티 키로 활용이 된다고 하였는데, 이 때 주의할 점은 for-in 문을 통해 객체를 순회할 때 symbol 타입의 키는 순회하지 않는 다는 점이다. 또한, 객체의 정적 메소드인 getOwnPropertyNames() 메소드도 symbol 타입의 키들은 반환해 주지 않는다.

const symbol = Symbol();

const obj = {
    name : "kim",
    age : 26    
}

// 심볼 키 추가
obj[symbol] = "It's symbol";

// for-in으로 순회
for(const key in obj) {
    console.log(`${key} : ${obj[key]}`);
}
/*
result : 
name : kim
age : 26
*/

// Object.getOwnPropetyNames
console.log(Object.getOwnPropertyNames(obj));  // [ 'name', 'age' ]

 

키가 심볼인 프로퍼티들의 목록을 확인하고 싶다면, Object의 또 다른 정적 메소드인 getOwnPropertySymbols() 메소드를 사용하여야 한다. 심볼인 키가 없을 경우 빈 배열을 반환한다.

// Object.getOwnPropertySymbols
console.log(Object.getOwnPropertySymbols(obj));  // [ Symbol() ]

 

또한, 객체를 JSON으로 변환해주는 JSON.stringify 메소드도 키가 심볼인 프로퍼티는 무시하기 때문에 이것 또한 주의하여야 한다.

const symbol = Symbol();

const obj = {
    name : "kim",
    age : 26    
}

obj[symbol] = "It's symbol";

const json = JSON.stringify(obj);
console.log(json);  // {"name":"kim","age":26}
728x90