[JS] 심볼형

MJ·2022년 9월 7일
0

Java Script

목록 보기
36/57
post-thumbnail

객체 프로퍼티 키

객체의 프로퍼티 키로는 '문자형'과 '심볼형'만을 허용합니다. 숫자형, 논리형 모두 불가능
하고 오직 문자형과 심볼형만 가능합니다.

아래 같은 [ ] 대괄호 표기법은 예외로 생각하시면 됩니다. 기본적으로 프로퍼티 키는
'문자형' 또는 '심볼형' 둘 중 하나로만 표기해야 합니다.

🔔 대괄호 표기법으로는 다른 원시값에 접근할 수 있다.

.표기법으로는 프로퍼티의 키가 '문자형' 또는 '심볼형'이 아니라면 접근할 수 없습니다.
예외로, [ ]대괄호 표기법은 다른 자료형('숫자', '논리')형으로된 키여도 접근할 수 있다.


심볼형

심볼형은 Symbol()이라는 함수를 호출하면서 생성할 수 있습니다.
심볼형은 변경이 불가능한 원시값이며, () 괄호에는 코멘트를 작성할 수 있습니다.

심볼에 대한 설명은 다른 식별자들간의 차이를 두므로 디버깅할때 유용합니다.
심볼은 보통 클래스나 객체의 프로퍼티 키로 사용하는데, 심볼을 키로 갖는 프로퍼티는 다른
프로퍼티와 충돌을 발생하지 않습니다.

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

let password = Symbol("비번");	// password는 심볼이 된다. 설명으로는 '비번'이 붙음.


/* */
심볼을 생성할 때 괄호에 문자열을 작성해서 코멘트를 남길 수 있습니다.

심볼은 유일성이 보장됩니다. 즉, 동일한 심볼형을 만들 수 없습니다. 심볼을 생성할 떄 코멘트는
다른 심볼과 동일해도 상관없습니다. 주석과 같은 개념이라고 생각하면 됩니다.

let id1 = Symbol("ID");
let id2 = Symbol("ID");	// 코멘트는 동일해도 상관없다. 

alert( id1 == id2 );	// false, 코멘트가 동일하다고 심볼형이 같아지는게 아닙니다.

⚠️ 심볼형은 문자형으로 자동 형 변환 되지 않습니다.

JS에서는 암시적으로 많은 값들이 자유롭게 문자형으로 형 변환되곤 합니다.
alert함수가 모든 자료형의 값을 받을 수 있는 이유가 자신에게 들어온 자료형을 자동
으로 문자형으로 변경하기 때문입니다.

그러나 심볼만은 예외입니다. 심볼형은 암시적으로 자동 형 변환이 발생하지 않습니다.

let id = Symbol("id");
alert(id);	// 오류 발생 

alert 함수에서 심볼을 값으로 받을 때 문자형으로 형 변환 할 수 없어서 출력할 수
없는 오류가 발생합니다. 그 이유는, JS에서 언어 차원의 보호장치(language guard)를
마련해 심볼형이 다른 자료형으로 변환되지 않게 막아줍니다.


심볼을 반드시 출력해야 하는 경우라면, .toString()메서드를 이용하면 됩니다.

let id = Symbol("id");
alert(id.toString());	// Symbol("id")가 출력됩니다.


❗ 심볼에 대한 설명만 출력하는 경우 .description 프로퍼티를 사용할 수 있습니다.

let id = Symbol("id");
alert(id.description);	// ID 출력



Hidden 프로퍼티

심볼을 사용하면 숨겨진 프로퍼티를 만들 수 있습니다. 숨겨진 프로퍼티는 외부에서 접근할 수
없고 값도 재할당을 할 수 없습니다.

서드파티에서 가지고 온 user라는 객체가 여러 개 있고, 이 객체를 이용해 어떤 작업을 진행
해야 하는 상황에서 user에 식별자를 붙여준다고 가정 해보겠습니다.

🔔 서드파티

서드파티는 제 3자를 뜻하는데, 프로그래밍에서는 라이브러리 프레임워크 플러그인등을
말합니다.

let user = { 			// 외부 라이브러리에서 가지고온 객체
 name: "John"
};

let id = Symbol('id');	// id 심볼 생성

user[id] = 1;			// 심볼을 키로 사용해서 user 객체에 접근할 수 있다

alert( user[id] );		// user 객체 내부에 [id] 심볼 프로퍼티 값이 출력됩니다. ( 1 )

예시에서 'id'를 문자형 대신에 심볼형으로 사용한 이유는 무엇일까요?
user 객체는 외부에서 가져온 객체이므로 함부로 프로퍼티를 추가할 수 없습니다.
다만, 심볼을 사용하면 외부에서 인식하지 못하게 객체에 식별자를 부여할 수 있습니다.

심볼형은 유일성이 보장되기에 개발자가 만든 식별자와, 다른 스크립트의 식별자의 이름이
같더라도 충돌하지 않습니다. 심볼대신 문자형으로 만들었다면 충돌 가능성이 있습니다.

let user = { };

let id = Symbol('id');

user[id] = '다른 PC의 ID';

1) 문자형으로 프로퍼티 생성 시 충돌 발생

외부 라이브러리에서 가져온 객체를, 심볼형이 아니라 문자형으로 접근해서 수정하면
유일성이 보장되지 않기에, 다른 개발자의 스크립트와 충돌이 발생합니다.

let user = { name: "John" };	// 외부 객체 ( 두 개의 PC가 가져와서 사용한다 )


user.id = "첫 번째 PC의 ID";		// 심볼 대신 문자열 사용 


// ↓↓ 다른 PC 환경이라고 가정, 다른 PC에서 첫 번째 PC와 동일하게 문자열로 식별자를 만듬.


user.id ="두 번째 PC의 ID";	  // 첫 번째 PC에서 할당한 user.id에 값을 재할당합니다.

alert( user.id );			  // 값이 덮어씌워져서 "두 번째 PC의 ID" 출력


/* */
만약, 심볼을 사용해서 id = Symbol('~~'); 을 할당했다면 두 PC에서 같은 id를 사용 하더라도
값을 재할당하는 경우가 없어지게 됩니다.



Symbol과 객체 리터럴

객체 리터럴 { .. } 방식으로 객체를 생성한 경우, 심볼형 키는 [ ] 대괄호를 사용해야 한다.

let id = Symbol('id');	// id 심볼 생성

let user = {
 name: "John",
 [id]: 123		// id 심볼을 프로퍼티 키로 생성
};


/* */
심볼을 프로퍼티 키로 사용할 때는 항상 [ ] 대괄호를 사용해줍시다.
"id" 문자열을 사용해버리면, user[id]인 심볼이 아니라 user.id 문자형으로 된다.

심볼형 프로퍼티와 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만 출력되고, id는 심볼형이기에 출력되지 않습니다. 
}

alert(user[id])	// 직접 접근하면 값을 호출할 수 있습니다.

1) 심볼형 프로퍼티 복사

for..in 반복문이나 외부 스크립트에서는 심볼형 프로퍼티를 접근할 수 없습니다.
다만 Object.assign() 같은 복사 방법은 심볼형 프로퍼티도 모두 복사합니다.

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

Object.assign(clone = {}, user);	// clone라는 객체를 생성하고, user 객체를 복사합니다.

alert( clone[id] );	// 심볼형 프로퍼티도 복사되서 접근할 수 있습니다.

🔔 for..in 반복문
심볼형 프로퍼티를 복사할 수 없다.


🔔 Object.assign() 얕은 복사
심볼형 프로퍼티 키를 복사할 수 있다.


for..in 반복문은 복사 대신 단순하게 프로퍼티를 호출하는 용도로도 사용할 수 있어
진짜로 복사하는 방식이 아니라면, 심볼형 키는 복사할 수 없도록 설계한 것 입니다.



전역 심볼

앞서 설명한 Symbol 들은, 일반 심볼이라고 부릅니다.
전역 심볼이란, 전역 레지스트리 공간에서 생성되는 심볼을 말합니다.
전역 심볼은 어느 공간이던지 사용할 수 있으며, 일반 심볼과는 다르게 식별자를 공유할 수
있습니다. 같은 전역 심볼을 사용하는 데이터들은 전역 심볼의 값을 공유받게 됩니다.

전역심볼에서 () 괄호의 값은 코멘트가 아니라 심볼의 키(이름)가 됩니다.
메서드로는 symbol.for()symbol.keyFor()이 있습니다.

🔔 전역 레지스트리는 심볼들이 전역으로 저장되는 공간입니다.


1) Symbol.for()

Symbol.for()은 전역 심볼을 호출 또는 생성할 때 사용하는 메서드 입니다.

() 괄호 값에는 심볼의 키가 들어갑니다. 만약 Symbol.for('id') 라고 심볼을 호출하면
전역 레지스트리 공간에서 id 라는 심볼을 찾고 가져옵니다. 해당되는 심볼이 없는 경우
전역 레지스트리에 id 라는 심볼을 생성하고, 자신을 호출한 곳으로 갖고옵니다.

let id = Symbol.for('id');	// id 변수에는 전역 심볼 id가 할당됨
/* 
(1) 'id'라는 전역심볼을 호출한다 > 전역 레지스트리에서 검색함 > 값이 없음
(2) 전역 레지스트리에 `id`라는 심볼을 생성 > 생성 후 자신을 호출한 곳에 반환 한다.
(3) id 변수는 id 전역 심볼을 갖게 된다.
*/

let idAgain = Symbol.for('id');	// idAgain 변수에는 전역 심볼 id가 할당 됨
/* 
(1) 'id'라는 전역심볼을 호출한다 > 전역 레지스트리에서 검색 > 값이 있음
(2) idAgain 변수는 id 전역 심볼을 갖게 된다.
(3) 변수 id와 동일한 전역 심볼을 공유한다.
*/

alert( id == idAgain );	// true, 동일한 전역 심볼 id를 갖고 있기에

2) Symbol.keyFor()

Symbol.keyFor() 메서드는 () 인자로 받은 값을 검색합니다. 인자로 전달된 값에 전역심볼
이 있다면, 심볼형의 키(이름)를 호출합니다. 인자로는 심볼형만 전달해야 합니다. 아니면 오류남

let sym = Symbol.for("name");	// 'name' 이름을 전역에서 찾고 없다면 생성해서 할당
let sym2 = Symbol.for("id");	// 'id' 이름을 전역에서 찾고 없다면 생성해서 할당

// 심볼을 이용해 이름을 얻음
alert( Symbol.keyFor(sym) ); // name, sym 변수에 저장되있는 전역심볼의 이름을 호출
alert( Symbol.keyFor(sym2) ); // id, sym2 변수에 저장되있는 전역심볼의 이름을 호출

Symbol.keyFor()은 전역 심볼의 키를 알아낼 때 사용하는 메서드입니다. 전역 심볼이 아닌
일반 심볼은 사용할 수 없습니다. 전역 심볼이 아닌 인자가 넘어오면 undefined를 반환합니다.

일반 심볼에 대한 설명을 확인 하려면, description 프로퍼티를 사용할 수 있습니다.

let globalSymbol = Symbol.for("name");	// 전역 심볼 호출 ( name )
let localSymbol = Symbol("name");	// 일반 심볼 생성 ( localSymbol )

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

alert( localSymbol.description ); // name ( 여기서 name은 키가 아니라 주석입니다. )



시스템 심볼 (내부 심볼)

시스템 심볼은 JS 내부에서 사용되는 심볼입니다. 하드 코딩으로 생성하는 것이 아니라,
JS로 인해서 미리 만들어진 심볼들을 말합니다. 이들은 상수로 존재되어 있으며, Symbol
함수의 프로퍼티로서 존재합니다.

대표적으로 사용하는 심볼들은 이곳 명세서에서 확인 합시다.
내부 심볼은 이후 포스팅에서 다루겠습니다.



정리


Symbol은 원시값으로 유일무이한 식별자를 만드는 용도로 사용합니다.


Symbol을 호출하면 일반 심볼을 만들 수 있고, 인자 값으로 설명을 추가할 수 있습니다.
Symbol('안녕 나는 일반심볼이야');


일반 심볼
1. () 괄호 안에 설명이 들어간다.
2. 일반 심볼에서 () 괄호의 설명이 동일해도, 같은 심볼이 아니다.


전역 심볼
1. () 괄호 안에 심볼의 키(이름)이 할당 된다.
2. 전역 심볼을 생성이 아니라, 전역 레지스트리에서 괄호안에 심볼 이름이 있는지 탐색하고
심볼이 존재하면 가져오고, 존재하지 않으면 전역 레지스트리 공간에 생성한 뒤 갖고옵니다.

Symbol.for('id')
전역 레지스트리에서 id라는 심볼을 찾고 없다면 생성하고 반환한다.
전역 심볼이 생성되면, 이 심볼을 호출한 변수나 데이터등은 동일한 값을 사용할 수 있습니다.


심볼의 유스 케이스

1. 숨김 프로퍼티 : 외부 스크립트나 라이브러리에 속한 객체에 새로운 프로퍼티를 추가하고
싶다면, 심볼을 만들고 이를 프로퍼티 키로 사용하면 됩니다. 키가 심볼인 경우에는
for..in 반복문의 대상이 되지 않아서, 의도치 않게 프로퍼티가 수정되는 것을 막을 수
있습니다. 외부 스크립트나 라이브러리는 심볼을 갖고 있지 않기 때문에 심볼형 프로퍼티에
접근이 불가능합니다. 이런 특성을 이용하면 특정 프로퍼티를 객체에 숨길 수 있습니다.
외부 스크립트에선 우리가 숨긴 심볼형 프로퍼티를 볼 수 없습니다.


2. JS 내부에서 정의된 시스템 심볼은 Symbol.*로 접근할 수 있습니다.
시스템 심볼을 이용하면 내장 메서드 등의 기본 동작을 자유롭게 변경할 수 있습니다.


사실 심볼을 완전히 숨길 방법은 없다. 내장 메서드 Object.getOwnPropertySymbols(obj)을
사용 하면 모든 심볼을 볼 수 있고, 메서드 Reflect.ownKeys(obj)는 심볼형 키를 포함한 객체의
모든 키를 반환 합니다. 그런데 대부분의 라이브러리나 내장함수 등은 이런 메서드를 사용 안함.

profile
프론트엔드 개발자가 되기 위한 학습 과정을 정리하는 블로그

0개의 댓글