오늘은 모던 자바스크립트 튜토리얼 객체의 심볼형에 대해 공부하겠습니다.
자바스크립트는 객체 프로퍼티 키로 오직 문자형과 심볼형만을 허용한다. 숫자형, 불린형 모두 불가능하고 오직 문자형과 심볼형만 가능하다.
지금까지는 프로퍼티 키가 문자형인 경우만 살펴보았다. 이번엔 프로퍼티 키로 심볼값을 사용해 보면서, 심볼형 키를 사용할 때의 이점에 대해 살펴보도록하자.
심볼을 만들 때 심볼 이름이라 불리는 설명을 붙일 수도 있다. 심볼 이름은 디버깅 시 아주 유용하다.
심볼은 유일성이 보장되는 자료형이기 때문에, 설명이 동일한 심볼을 여러 개 만들어도 각 심볼값은 다르다. 심볼에 붙이는 설명(심볼 이름)은 어떤 것에도 영향을 주지 않는 이름표 역할만을 한다.
설명이 같은 심볼 두 개를 만들고 이를 비교해보자. 동일 연산자(==)로 비교 시 false가 반환된느 것을 확인할 수 있다.
참고로 Ruby 등의 언어에서도 '심볼'과 유사한 개념을 사용하는데, 자바스크립트의 심볼은 이들 언어에 쓰이는 심볼과는 다르기 때문에 혼동하지 말자.
아래 예시에서 alert은 에러를 발생시킨다.
문자열과 심볼은 근본이 다르기 때문에 우연히라도 서로의 타입으로 변환돼선 안된다.
자바스크립트에선 '언어 차원의 보호장치(language guard)'를 마련해 심볼형이 다른 형으로 변환되지 않게 막아 준다.
심볼은 반드시 출력해줘야 하는 상황이라면 아래와 같이 .toString() 메서드를 명시적으로 호출해주면 된다.
symbol.description 프로퍼티를 이용하면 설명만 보여주는 것도 가능하다.
서드파티 코드에서 가지고 온 user라는 객체가 여러 개 있고, user를 이용해 어떤 작업을 해야 한느 상황이라고 가정해보자. user에 식별자를 붙여주도록 하자.
식별자는 심볼을 이용해 만들도록한다.
그런데 문자열 "id"를 키로 사용해도 되는데 Symbol("id")을 사용한 이유가 무엇일까
user는 서드파티 코드에서 가지고 온 객체이므로 함부로 새로운 프로퍼티를 추가할 수 없다.
그런데 심볼은 서드파티 코드에서 접근할 수 없기 때문에, 심볼을 사용하면 서드파티 코드가 모르게 user에 식별자를 부여할 수 있다.
상황 하나를 더 가정해보자. 제3의 스크립트(자바스크립트 라이브러리 등)에서 user를 식별해야 하는 상황이 벌어졌다고 해보자. user의 원천인 서드파티 코드, 현재 작성 중인 스크립트, 제3의 스크립트가 각자 서로의 코드도 모른 채 user를 식별해야 하는 상황이 벌어졌다.
제3의 스크립트에선 아래와 같이 Symbol("id")을 이용해 전용 식별자를 만들어 사용할 수 있다.
심볼은 유일성이 보장되므로 우리가 만든 식별자와 제3의 스크립트에서 만든 식별자가 충돌하지 않는다. (이름이 같더라도)
만약 심볼 대신 문자열 "id"를 사용해 식별자를 만들었다면 충돌이 발생할 가능성이 있다.
"id: 123"이라고 하면, 심볼 id가 아니라 문자열 "id"가 키가 된다.
Object.keys(user)에서도 키가 심볼인 프로퍼티는 배제된다. '심볼형 프로퍼티 숨기기(hiding symbolic property)'라 불리는 이런 원칙 덕분에 외부 스크립트나 라이브러리는 심볼형 키를 가진 프로퍼티에 접근하지 못한다.
그런데 Object.assign은 키가 심볼인 프로퍼티를 배제하지 않고 객체 내 모든 프로퍼티를 복사한다.
뭔가 모순이 있는 것 같아 보이지만, 이는 의도적으로 설계된 것이다. 객체를 복사하거나 병합할 때, 대개 id 같은 심볼을 포함한 프로퍼티 전부를 사용하고 싶어 할 것이라는 생각에서 이렇게 설계되었다.
전역 심볼 레지스크리(global symbol registry)는 이런 경우를 위해 만들어졌다. 전역 심볼 레지스트리 안에 심볼을 만들고 해당 심볼에 접근하면, 이름이 같은 경우 항상 동일한 심볼을 반환해준다.
레지스트리 안에 있는 심볼을 읽거나, 새로운 심볼을 생성하려면 Symbol.for(key)를 사용하면 된다.
이 메서드를 호출하면 이름이 key인 심볼을 반환한다. 조건에 맞는 심볼이 레지스트리 안에 없으면 새로운 심볼 Symbol(key)을 만들고 레지스트리 안에 저장한다.
전역 심볼 레지스트리 안에 있는 심볼은 전역 심볼이라고 불린다.
애플리케이션에서 광범위하게 사용해야 하는 심볼이라면 전역 심볼을 사용하자.
Symbol.keyFor는 전역 심볼 레지스트리를 뒤져서 해당 심볼의 이름을 얻어낸다.
검색 범위가 전역 심볼 레지스트리이기 때문에 전역 심볼이 아닌 심볼에는 사용할 수 없다.
전역 심볼이 아닌 인자가 넘어오면 Symbol.keyFor는 undefined를 반환한다.
전역 심볼이 아닌 모든 심볼은 description 프로퍼티가 있다. 일반 심볼에서 이름을 얻고 싶으면 description 프로퍼티를 사용하면 된다.
명세서 내의 표, 잘 알려진 심볼(well-known symbols)에서 어떤 시스템 심볼이 있는지 살펴보자.