[JavaScript] 7번째 타입, Symbol

유진·2021년 1월 30일
0
post-thumbnail

1. Symbol은 왜 탄생했는가?

ES6 이전에는 자바스크립트에 undefined, null, boolean, number, string, object 6가지 타입이 존재했다. 그리고 ES6에서 새로운 일곱번째 타입인 Symbol이 등장했다.

Symbol은 문자열도, 객체도 아닌 새로운 타입이다. 그렇다면, ES6에서 왜 Symbol이 추가되었을까?

- 당신이 만든 객체 키는 안전하지 않다.

Symbol은 객체의 키(key)로 사용하기 위해 만들어졌다.

가령, 우리가 짠 코드의 일부가 다음과 같다고 하자.

if (obj.isRunning) {
 callValues(obj); 
}
obj.isRunning = false;

평범한 코드 같지만, 사실 상당한 위험을 가지고 있는 코드이다.

  • 만약에 새롭게 불러온 라이브러리에 isRunning이 전역변수로 선언되어 있다면 내 코드의 isRunning과 라이브러리의 isRunning은 충돌하게 될 것이다.
  • 아니면, 기존에는 충돌하지 않았지만, 내가 잘 쓰던 라이브러리가 업데이트 되면서 isRunning이 추가되면서 충돌을 발생시킬 수 있다.
  • 혹은 자바스크립트에 갑자기 isRunning이라는 객체 메서드가 추가될...수도... 있다!

위의 경우가 발생한다면 유지보수 하기가 엄청나게 어려울 것이다.

이렇게, 다른 사람의 라이브러리를 가져와 작업할 때는 이름 충돌로 인해 생기는 문제가 발생한다.

그렇다고 이 문제를 해결하기 위해 변수 이름을 특이하게 짓는다면.. 직관적이지 않은 변수명으로 인해 유지보수하기 어려울 것이다.

이러한 객체 키 이름 중복으로 인한 충돌 문제를 해결하기 위해 심볼이 태어났다.

- 심볼로 문제 해결하기

자바스크립트는 Symbol()을 호출하여 새로운 심볼 값을 생성한다. 이 값은 다른 어떤 값들과도 겹치지 않는 고유한 값이다.

let newSymbol = Symbol('new');

우리가 문자열이나 숫자를 객체의 키로 사용하는 것처럼, 심볼 값도 객체의 키로 사용할 수 있다. 또한, 모든 심볼 값은 고유하기 때문에, 다른 객체의 속성과 충돌할 염려도 없다.

Symbol()의 인자로 들어가는 문자열은 심볼을 구별하는 기능이 아니다. 이 인자는 주석으로, 이 심볼이 무엇을 의미하는지 설명할 수 있도록 하기 위해 존재한다. 디버깅 시에 유용히며, 그 자체로 특별한 기능을 하지 않는다.

let oneSymbol = Symbol('one');
let anotherSymbol = Symbol('one');
console.log(oneSymbol);
// Symbol('one')
console.log(anotherSymbol);
// Symbol('one')
console.log(oneSymbol === anotherSymbol);
// false

앞서 문제가 된 그 코드를 심볼을 이용해 다음과 같이 수정할 수 있다.

let isRunning = Symbol('isRunning');

if (obj[isRunning]) {
 callValues(obj); 
}
obj[isRunning] = false;

isRunning은 심볼 값이며, obj[isRunning]은 심볼값을 키로 가지는 속성값이다. 키의 타입이 심볼 값이라는 점만 빼면 다른 속성들과 똑같다. 다만, isRunning 변수로만 접근할 수 있다. isRunning에 들어있는 고유한 심볼 값으로 객체의 속성 키를 지정했기 때문이다. 따라서 다른 코드와 충돌할 수 없다.

2. 심볼 사용시 유의사항

- Symbol()과 Symbol.for(string)

Symbol()은 호출할 때마다 새롭고 고유한 심볼을 반환한다.

반면, Symbol.for(string)심볼 레지스트리(symbol registry) 라는 공간을 참조한다. 심볼 레지스트리는 심볼들의 목록으로, 전역 공간에 있다. 이곳에 Symbol.for(string)이 있다면 해당 심볼을 불러오고, 없으면 심볼 레지스트리에 Symbol.for(string)을 추가한다.

/* 다음의 Symbol('nittre')들은 서로 다른 고유한 값을 가진 다른 심볼들이다 */ 
Symbol('nittre');
Symbol('nittre');
Symbol('nittre');

/* 다음의 Symbol.for('nittre')는 모두 같은 심볼이다. */

Symbol.for('nittre');
Symbol.for('nittre');
Symbol.for('nittre');

Symbol.for(string)을 사용하면 같은 심볼을 코드 내 여러 공간에서 사용할 수 있다는 특징이 있다.

- 심볼을 형변환하는 방법

심볼은 다른 문자열과 합칠 수 없다. 심볼을 문자열처럼 사용하기 위해서는 Symbol.toString()으로 문자열로 변환해야 한다.

let sym = Symbol('sym');
console.log(`hi this is cute ${sym}`); // TypeError
console.log(`hi this is cute ${sym.toString()}`); // hi this is cute Symbol('sym')

심볼은 숫자로는 변환할 수 없다.

let sym = Symbol('sym');
console.log(Number(sym)); // TypeError

- for...in 에서는 심볼을 건너뛴다

객체에 심볼을 키로 사용하면 해당 속성은 for..in에서 제외한다. 따라서 남은 심볼들은 Object.getOwnPropertySymbols()를 이용해 가져와야 한다.

const object1 = {};
const a = Symbol('a');
const b = Symbol.for('b');

object1[a] = 'localSymbol';
object1[b] = 'globalSymbol';
object1.c = 'normalStringC';
object1.d = 'normalStringD';

for (let el in object1) {
  console.log(el)
};

console.log(Object.getOwnPropertySymbols(object1));

// expected Output
// 'normalStringC'
// 'normalStringD'
// [Symbol('a'), Symbol('b')]

- JSON.stringify()도 심볼을 무시한다

JSON.stringify()는 심볼을 키로 사용한 속성을 무시한다.

JSON.stringify({[Symbol("foo")]: "foo"});
// '{}'

- 자바스크립트 내장 심볼

자바스크립트 언어 내부의 동작을 나타내는 내장 심볼들이 있다. 목록들은 MDN - Symbol() 잘 알려진 심볼들 참고.

참고문헌

profile
제가 또 기가막힌 한 줌의 트러플 소금 같은 존재그등요

0개의 댓글