Symbol

김기훈·2023년 3월 7일
0

자바스크립트

목록 보기
10/17

심볼형

자바스크립트는 객체 프로퍼티 키로 오직 문자형과 심볼형만을 허용한다.

'심볼(symbol)'은 유일한 식별자(unique identifier)를 만들고 싶을 때 사용한다.

Symbol()을 사용하면 심볼값을 만들 수 있다.

// id는 새로운 심볼이 된다.
let id = Symbol();`

// 심볼 id에는 "id"라는 설명이 붙는다.
let id = Symbol("id");

심볼은 유일성이 보장되는 자료형이기 때문에, 설명이 동일한 심볼을 여러 개 만들어도 각 심볼값은 다르다. 심볼에 붙이는 설명은 어떤 것에도 영향을 주지 않는 이름표 역할만을 한다.

설명이 같은 심볼 두 개를 만들고 이를 비교해보면 false가 반환되는 것을 확인할 수 있다.

let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false

프로퍼티 은닉

심볼을 이용하면 프로퍼티를 은닉 하여 외부 코드에서 접근이 불가능하게 할 수 있다.

서드파티 코드에서 가지고 온 user라는 객체가 여러 개 있고, user를 이용해 어떤 작업을 해야 하는 상황이라고 가정해보자.

// 서드파티 코드에서 가져온 객체
let user = { 
  name: "John"
};

let id = Symbol("id");

user[id] = 1;

alert( user[id] ); 
// 심볼을 키로 사용해 데이터에 접근할 수 있다.

문자열 "id"가 아닌 Symbol("id")을 사용한 이유가 무엇일까?

user는 서드파티 코드에서 가지고 온 객체이므로 함부로 새로운 프로퍼티를 추가할 수 없다. 그런데 심볼을 사용하면 서드파티 코드가 모르게 user에 식별자를 부여할 수 있다.

상황 하나를 더 가정해보자. user의 원천인 서드파티 코드, 현재 작성 중인 스크립트, 제3의 스크립트가 각자 서로의 코드도 모른 채 user를 식별해야 하는 상황이 벌어졌다.

제3의 스크립트에선 아래와 같이 Symbol("id")을 이용해 전용 식별자를 만들어 사용할 수 있다.

// ...

let id = Symbol("id");

user[id] = "제3 스크립트 id 값";

이때 심볼은 유일성이 보장되므로 우리가 만든 식별자와 제3의 스크립트에서 만든 식별자가 이름이 같더라도 충돌하지 않는다.

만약 심볼 대신 문자열 "id"를 사용해 식별자를 만들었다면 충돌이 발생할 가능성이 있다.

for…in 에서의 symbol

키가 심볼인 프로퍼티는 for..in 반복문에서 배제된다.

let id = Symbol("id");
let user = {
  name: "John",
  age: 30,
  [id]: 123
};

// name과 age만 출력되고, 심볼은 출력되지 않는다.
for (let key in user) console.log(key); 
// name 
// age


// 심볼로 직접 접근하면 잘 작동한다.
console.log( user[id] );       // 123

또한 Object.keys(user)에서도 키가 심볼인 프로퍼티는 배제된다.


객체 리터럴 {...}을 사용해 객체를 만든 경우

대괄호를 사용해 심볼형 키를 만들어야 한다.

let id = Symbol("id");

let user = {
  name: "John",
  [id]: 123 // "id": 123은 안됨
};

전역 심볼

앞서 살펴본 것처럼, 심볼은 이름이 같더라도 모두 별개로 취급된다. 그런데 이름이 같은 심볼이 같은 개체를 가리키길 원하는 경우도 가끔 있다.

전역 심볼 레지스트리는 이런 경우를 위해 만들어졌다. 전역 심볼 레지스트리 안에 심볼을 만들고 해당 심볼에 접근하면, 이름이 같은 경우 항상 동일한 심볼을 반환해준다.

레지스트리 안에 있는 심볼을 읽거나, 새로운 심볼을 생성하려면 Symbol.for(key)를 사용하면 된다.

let id = Symbol.for("id"); 
// 심볼이 존재하지 않으면 새로운 심볼을 만든다.

// 동일한 이름을 이용해 심볼을 다시 읽는다.
let idAgain = Symbol.for("id");

// 두 심볼은 같다.
alert( id === idAgain ); // true

전역 심볼 레지스트리 안에 있는 심볼은 전역 심볼이라고 불린다.

Symbol.for()와 반대로 Symbol.keyFor()을 사용해 심볼 값의 키를 추출할 수 도 있다.

// 키를 이용해 심볼을 찾음
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");

// 심볼을 이용해 키를 얻음
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id

Symbol.keyFor는 해당 심볼의 이름을 얻어낸다. 검색 범위가 전역 심볼 레지스트리이기 때문에 전역 심볼이 아닌 심볼에는 사용할 수 없다.


정리

  1. Symbol은 원시형 데이터로, 유일무이한 식별자를 만드는 데 사용된다.

  2. Symbol()을 호출하면 심볼을 만들 수 있다. 설명(이름)은 선택적으로 추가할 수 있다.

  3. 심볼은 이름이 같더라도 값이 항상 다르다. 이름이 같을 때 값도 같길 원한다면 전역 레지스트리를 사용해야 한다. Symbol.for(key)는 key라는 이름을 가진 전역 심볼을 반환한다. key라는 이름을 가진 전역 심볼이 없으면 새로운 전역 심볼을 만들어주고 key가 같다면 Symbol.for는 어디서 호출하든 상관없이 항상 같은 심볼을 반환해 준다.

  1. 객체의 ‘숨김’ 프로퍼티 – 외부 스크립트나 라이브러리에 ‘속한’ 객체에 새로운 프로퍼티를 추가해 주고 싶다면 심볼을 만들고, 이를 프로퍼티 키로 사용하면 된다. 키가 심볼인 경우엔 for..in의 대상이 되지 않아서 의도치 않게 프로퍼티가 수정되는 것을 예방할 수 있다. 외부 스크립트나 라이브러리는 심볼 정보를 갖고 있지 않아서 프로퍼티에 직접 접근하는 것도 불가능하기 때문에 프로퍼티가 우연히라도 사용되거나 덮어씌워 지는 걸 예방할 수 있다.

  2. 이런 특징을 이용하면 원하는 것을 객체 안에 ‘은밀하게’ 숨길 수 있다. 외부 스크립트에선 우리가 숨긴 것을 절대 볼 수 없다.

  3. 자바스크립트 내부에서 사용되는 시스템 심볼은 Symbol.*로 접근할 수 있다. 시스템 심볼을 이용하면 내장 메서드 등의 기본 동작을 입맛대로 변경할 수 있다.

profile
평생 공부하기

0개의 댓글

관련 채용 정보