본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

symbol과 bigint: 드물지만 대체할 수 없는 기본 타입

~9 min · primitives, symbol, bigint, javascript-evolution

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"매일 쓰지 않는 타입도, 필요한 순간 다른 타입으로 흉내 내면 계약이 무너져."

symbol: 충돌하지 않는 키

Symbol()을 호출할 때마다 다른 값이 만들어져. 같은 설명 문자열을 줘도 두 symbol은 서로 같지 않아. 이 고유성 덕분에 다른 코드와 이름이 겹치지 않는 객체 키, 프로토콜 표식, 메타데이터 키를 만들 수 있어.

JavaScript 표준도 Symbol.iterator, Symbol.asyncIterator 같은 잘 알려진 symbol을 사용해 객체가 특정 동작을 제공한다고 표시해. 문자열 이름으로 같은 일을 했다면 우연한 속성 충돌을 피하기 어려웠을 거야.

unique symbol과 타입 수준의 정체성

const TOKEN: unique symbol = Symbol()처럼 선언하면 그 상수 하나만의 고유 타입을 얻을 수 있어. 다른 symbol과 섞이지 않는 브랜드나 판별 키를 만들 때 유용해. 일반 symbol 타입이 ‘어떤 symbol이든’을 뜻한다면 unique symbol은 ‘바로 이 symbol 하나’를 뜻해.

객체 속성 키로 쓸 때는 대괄호 문법을 사용해. symbol 키는 일반적인 Object.keys 결과에 나오지 않으므로, 열거와 직렬화가 필요한 데이터에는 맞지 않아. 숨긴다는 말과 보안이라는 말도 구별해야 해. 찾기 어렵게 할 뿐 접근 권한을 막는 장치는 아니야.

bigint: 안전한 정수 범위 너머

JavaScript의 number는 부동소수점이라 Number.MAX_SAFE_INTEGER를 넘는 정수를 정확히 표현하지 못해. bigint는 크기에 사실상 제한이 없는 정수 타입이고, 숫자 뒤에 n을 붙여 9007199254740993n처럼 만든다.

number와 bigint는 암묵적으로 섞이지 않아. 1n + 1은 오류고, 어느 쪽으로 계산할지 명시적으로 변환해야 해. 이 엄격함은 정밀도를 실수로 잃는 일을 막아. 다만 JSON은 bigint를 기본으로 직렬화하지 못하므로 문자열 변환 같은 경계 정책이 필요해.

언제 손을 뻗을까

symbol은 라이브러리 프로토콜과 충돌 없는 내부 키에, bigint는 큰 정수의 정확성이 실제 요구인 곳에 써. 평범한 ID가 안전 범위 안에 있고 직렬화가 중요하다면 number나 string이 더 단순할 수 있어. 드문 도구를 쓴다는 사실보다 왜 다른 도구로는 계약을 지킬 수 없는지가 중요해.

원시 타입과 래퍼 객체를 구별해

타입 표기에는 소문자인 symbolbigint를 써. 대문자 SymbolBigInt는 래퍼 객체 쪽 이름이라 원시값 타입 표기로 쓰지 않아. string과 String, number와 Number의 관계와 같은 규칙이야.

symbol은 정체성, bigint는 정밀도를 지켜. 둘 다 일상적인 기본값은 아니지만 필요한 자리에서는 대체품이 없어.

피파의 고백

symbol은 값이 겹치면 안 되는 내부 키에, bigint는 number의 안전한 정수 범위를 넘는 정확한 정수에 써. 드물게 쓰더라도 필요한 경계가 아주 분명한 타입들이야.

Code

symbol — 고유 key, iterator protocol hook·typescript
// symbol — 고유 보장.

const a = Symbol('id');
const b = Symbol('id');
console.log(a === b);             // false — 같은 설명에도

// Object 의 property key 로 사용.
const MY_KEY = Symbol('myKey');
const obj = {
  [MY_KEY]: 'private-ish value',
  publicProp: 'visible',
};

console.log(obj[MY_KEY]);         // 'private-ish value'
console.log(Object.keys(obj));    // ['publicProp'] — symbol key 는 스킵

// well-known symbol 이 object 를 iterable 하게 만들어.
class Range {
  constructor(public start: number, public end: number) {}
  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) yield i;
  }
}

for (const n of new Range(1, 3)) console.log(n);  // 1, 2, 3
bigint — number 의 정밀도 떨어질 때·typescript
// bigint — 임의 정밀도 integer.

const small: bigint = 9007199254740992n;        // n suffix 가 bigint 표시
const big: bigint = small * 2n;                  // 18014398509481984n — 정확

// number 는 이거 정확히 표현 못 함:
const asNumber: number = 9007199254740993;       // 9007199254740992 로 round

// 섞으면 실패 — runtime 도, (annotate 됐으면) compile 시점도.
// const broken = big + 1;                       // ❌ Type 'bigint' not assignable to '1n' (BigInt + number 는 runtime TypeError 이기도)
const fixed = big + 1n;                          // ✅ 둘 다 bigint
const converted = Number(big);                   // ✅ 명시적 cast, 정밀도 잃을 수 있음

// 손 뻗을 때:
//  - 2^53 초과 DB ID
//  - 나노초 timestamp (Node 의 process.hrtime.bigint())
//  - 화폐를 가장 작은 단위로, float 산술 허용 안 될 때

External links

Exercise

배열을 받아 역순으로 값을 내보내는 클래스를 작성해. 제너레이터로 Symbol.iterator를 구현하고 for...of와 전개 문법에서 같은 순서가 나오는지 확인해.
Hint
제너레이터 메서드 *[Symbol.iterator]() 안에서 yield하면 반복 규약을 간단히 구현할 수 있어. 두 소비 문법이 같은 이터레이터를 사용한다는 점을 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 똘이warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.