객체 - 심볼

devheyrin·2022년 4월 20일
0

modern javascript

목록 보기
25/26

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

이번에는 프로퍼티 키로 심볼값을 사용해 보면서, 심볼형 키를 사용할 때의 장점을 살펴보자!

심볼

심볼은 유일한 식별자를 만들고 싶을 때 사용한다.

Symbol() 을 사용하면 심볼값을 만들 수 있다. 심볼을 만들 때 심볼 이름이라 불리는 설명을 붙일 수도 있다.

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

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

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

alert(id1 == id2); // false

심볼형은 문자형으로 자동 형 변환되지 않는다.

자바스크립트에서는 문자형으로의 암시적 형 변환이 비교적 자유롭게 일어나기 때문에, alert() 함수가 거의 모든 값을 인자로 받을 수 있다. 그러나 심볼은 다른 자료형으로 암시적 형변환되지 않는다. 따라서 아래 예시에서는 에러가 발생한다.

let id = Symbol("id");
alert(id); 
// TypeError: Cannot convert a Symbol value to a string

자바스크립트에서는 ‘언어 차원의 보호장치'를 마련해 심볼형이 다른 형으로 변환되지 않게 막아놓았다. 심볼을 반드시 출력해주어야 한다면 toString() 메소드를 명시적으로 호출해주면 된다. 또는 description 프로퍼티를 사용해 설명만 보여줄 수도 있다.

let id = Symbol("id");
alert(id.toString()); // Symbol(id)가 얼럿 창에 출력됨
alert(id.description); 

‘숨김' 프로퍼티

심볼을 이용하면 숨김 프로퍼티 를 만들 수 있다. 숨김 프로퍼티는 외부 코드에서 접근이 불가능하고 값도 덮어쓸 수 없는 프로퍼티이다.

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

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

let id = Symbol("id");

user[id] = 1;

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

문자열 id 를 키로 사용해도 되지만, Symbol(”id”)를 키로 사용하면 서드파티 코드가 모르게 user 에 식별자를 부여할 수 있다. 서드파티 코드에서 가지고 온 객체이므로 함부로 새로운 프로퍼티를 추가할 수 없는데, 심볼을 서드파티 코드에서 접근할 수 없으므로 서드파티 코드가 모르게 user 에 식별자를 부여할 수 있기 때문이다.

상황 하나를 더 가정해보자.

제 3의 스크립트에서 user 를 식별해야 하는 상황에서, 제3의 스크립트는 Symbol 을 이용해 전용 식별자를 만들어 사용할 수 있다.

let id = Symbol("id");

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

심볼은 유일성이 보장되므로 우리가 만든 식별자와 제3의 스크립트에서 만든 식별자가 충돌하지 않는다. 만약 심볼 대신 문자열 “id” 를 사용해 식별자를 만들었다면 충돌이 발생했을 가능성이 높다.

let user = { name: "John" };

// 문자열 "id"를 사용해 식별자를 만든다. 
user.id = "스크립트 id 값";

// 제3의 스크립트가 우리 스크립트와 동일하게 문자열 "id"를 이용해 식별자를 만든다. 
user.id = "제3 스크립트 id 값"
// 의도치 않게 값이 덮어 쓰여서 우리가 만든 식별자는 무의미해진다. 

Symbols in a literal

객체 리터럴을 사용해 객체를 만들 경우, 대괄호를 사용해 심볼형 키를 만들 수 있다.

let id = Symbol("id");

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

심볼은 for...in 에서 배제된다.

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

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

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

// 심볼로 직접 접근하면 잘 작동한다. 
alert( "직접 접근한 값: " + user[id] );

Object.keys(user) 에서도 키가 심볼인 프로퍼티는 배제된다. ‘심볼형 프로퍼티 숨기기'라는 원칙 때문에, 외부 스크립트나 라이브러리는 심볼형 키를 가진 프로퍼티에 접근하지 못한다.

그러나 Object.assign 은 키가 심볼인 프로퍼티를 배제하지 않고 객체 내 모든 프로퍼티를 복사한다.

let id = Symbol("id");
let user = {
  [id]: 123
};

let clone = Object.assign({}, user);

alert( clone[id] ); // 123

이는 의도적으로 설계된 것이다. 객체를 복사하거나 병합할 때, 대개 id 같은 심볼을 포함한 프로퍼티 전부를 사용하고 싶어할 것이라는 생각에서 설계된 것이다.

전역 심볼

심볼은 이름이 같더라도 모두 별개로 취급된다. 그런데 이름이 같은 심볼같은 개체를 가리키기를 원하는 경우도 있다. 애플리케이션 곳곳에서 id 라는 심볼을 이용해 특정 프로퍼티에 접근해야 한다고 가정해보자.

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

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

이 메서드를 호출하면 이름이 key 인 심볼을 반환한다. 조건에 맞는 심볼이 레지스트리 안에 없으면 새로운 심볼을 만들고 레지스트리 안에 저장한다.

// 전역 레지스트리에서 심볼을 읽는다. 
let id = Symbol.for("id"); // 심볼이 존재하지 않으면 새로운 심볼을 만든다. 

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

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

전역 심볼 레지스트리 안에 있는 심볼을 전역 심볼이라고 부른다. 애플리케이션 전역에서 광범위하게 사용해야 하는 심볼이라면 전역 심볼을 사용하면 된다!

Symbol.keyFor

전역 심볼의 이름을 찾는 메서드로는 Symbol.keyFor(sym) 이 있다.

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

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

Symbol.keyFor(sym) 는 전역 심볼 레지스트리를 뒤져서 해당 심볼의 이름을 얻어낸다. 전역 심볼이 아닌 심볼에는 사용할 수 없다. 전역 심볼이 아닌 인자가 넘어오면 undefined 를 반환한다.

전역 심볼이 아닌 모든 심볼에는 description 프로퍼티가 있으므로, 일반 심볼에서 이름을 얻고 싶다면 description 프로퍼티를 사용하면 된다.

let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");

alert( Symbol.keyFor(globalSymbol) ); // name, 전역 심볼
alert( Symbol.keyFor(localSymbol) ); // undefined, 전역 심볼이 아님

alert( localSymbol.description ); // name

시스템 심볼

시스템 심볼은 자바스크립트 내부에서 사용되는 심볼이다. 시스템 심볼을 활용해 객체를 미세조정할 수 있다.

잘 알려진 심볼은 다음과 같다.

  • Symbol.hasInstance
  • Symbol.isConcatSpreadable
  • Symbol.iterator
  • Symbol.toPrimitive
  • 기타 등등

객체가 원시형으로 변환되는지 알기 위해서 Symbol.toPrimitive 를 알아야 한다. 각각에 관련된 내용은 차차 알아보기로 하자!

profile
개발자 헤이린 🔜 프로덕트 매니저로 나아가는 중!

0개의 댓글