Symbol

<aside> ๐Ÿ’ก

๋‹ค๋ฅธ ๊ฐ’๊ณผ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” ์œ ์ผ๋ฌด์ดํ•œ ๊ฐ’

๋ฌธ์ž์—ด, ์ˆซ์ž, ๋ถˆ๋ฆฌ์–ธ, ๊ฐ์ฒด, null, undefined, Symbol

์›๋ž˜ 6๊ฐœ๋งŒ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด์—ˆ๋Š”๋ฐ ES6๋ถ€ํ„ฐ Symbol ํƒ€์ž…์ด ์ถ”๊ฐ€๋˜์–ด ๋ฐ์ดํ„ฐ ํƒ€์ž…์€ ์ด 7๊ฐœ๋‹ค.

</aside>

1. ์‹ฌ๋ณผ ๋งค์„œ๋“œ

์‹ฌ๋ณผ ์ƒ์„ฑ Symbol(str)

// ์‹ฌ๋ณผ ์ƒ์„ฑํ•˜๊ธฐ
let id1 = Symbol();
let id2 = Symbol();
// ์‹ฌ๋ณผ ๊ฐ๊ฐ์€ ์œ ์ผํ•˜๊ธฐ์— ๋‹ค์Œ ์ˆ˜์‹์€ false๋กœ ํ‰๊ฐ€๋œ๋‹ค
console.log(id1 == id2);
 
// ์ด๋ ‡๊ฒŒ new๋ฅผ ์“ฐ๋ฉด TypeError ๋ฐœ์ƒ
let newSymbol = new Symbol();

Symbol() ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค.

new Symbol()์€ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

let id1 = Symbol("id");
console.log(id1); // Symbol(id)

Symbol์— String ๊ฐ’์„ ์ „๋‹ฌํ•˜์—ฌ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์„ค๋ช… ์ธ์ž๋ผ ๋ถ€๋ฅธ๋‹ค.

์ „์—ญ ์‹ฌ๋ณผ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ Symbol.for(str), Symbol.keyFor(sym)

์œ„์™€ ๊ฐ™์ด ์‹ฌ๋ณผ์„ ์ƒ์„ฑํ•˜๋ฉด ๋˜์ง€๋งŒ, ์ ‘๊ทผํ•˜๊ธฐ ์–ด๋ ค์šด ๊ตฌ์กฐ์ด๋‹ค. ๊ฐ์ฒด์˜ ํ‚ค๋กœ Symbol์„ ์ง€์ •ํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์„๊นŒ?

// ์ „์—ญ ์‹ฌ๋ณผ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— id ์‹ฌ๋ณผ ๋“ฑ๋ก๋จ
let id = Symbol.for("id");
// ์ด๋ฏธ ๋“ฑ๋ก๋œ ์‹ฌ๋ณผ์„ ๋ฐ˜ํ™˜ํ•จ
let id2 = Symbol.for("id");
console.log(id === id2); // true

Symbol.for ๋งค์„œ๋“œ๋ฅผ ํ†ตํ•ด ์‹ฌ๋ณผ์„ ์ƒ์„ฑํ•˜๋ฉด, ์ „์—ญ ์‹ฌ๋ณผ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋ก๋œ๋‹ค. ๋งŒ์•ฝ ์ „์—ญ ์‹ฌ๋ณผ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ํ•ด๋‹น ํ‚ค๊ฐ€ ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค๋ฉด, ์ด๋ฏธ ๋“ฑ๋ก๋œ ์‹ฌ๋ณผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

let id = Symbol.for("id");
let witch = Symbol.for("witch");
console.log(Symbol.keyFor(id)); // id
console.log(Symbol.keyFor(witch)); // witch

Symbol.keyFor() ๋งค์„œ๋“œ๋ฅผ ํ†ตํ•ด ์‹ฌ๋ณผ ํ‚ค๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

2. ์‹ฌ๋ณผ์ด ๋งŒ๋“ค์–ด์ง„ ๋ชฉ์ 

๋‚˜๋Š” ์‹ค๋ฌด์—์„œ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ Symbol ํ‚ค์›Œ๋“œ๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ๋งŒ๋“ค์–ด๋ณธ ์ ์ด ๋‹จ ํ•œ๋ฒˆ๋„ ์—†๋‹ค. ์ด๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ด๊ฒŒ ์™œ ํ•„์š”ํ•˜์ง€? ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์ง€๋งŒ, ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€์˜ ์žฅ์ ์ด ์žˆ๋‹ค.

<aside> ๐Ÿ’ก

์‹ฌ๋ณผ์˜ ์žฅ์ : ๊ณ ์œ ์„ฑ ๋ณด์žฅ, ๋น„๊ณต๊ฐœ ์†์„ฑ ๊ตฌํ˜„

</aside>

1. ๊ณ ์œ ์„ฑ ๋ณด์žฅ

const Direction = Object.freeze({
  UP: "up",
  DOWN: "down",
  LEFT: "left",
  RIGHT: "right",
});

const Mood = Object.freeze({
  UP: "up",
  DOWN: "down",
  HAPPY: "happy",
});

console.log(Direction.UP === Mood.UP); // true

๋‹ค์Œ ์˜ˆ์ œ์—์„œ, Direction์€ ๋ฐฉํ–ฅ์„, Mood๋Š” ๊ธฐ๋ถ„์„ ์˜๋ฏธํ•œ๋‹ค. value์˜ ๋ณ„ ์‚ฌ์šฉ์ฒ˜๊ฐ€ ์—†๊ธฐ์— ์ ๋‹นํ•œ ๊ฐ’์„ ์ผ์„ ๋ฟ์ธ๋ฐ ์ „ํ˜€ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ๊ฐ’์ด ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์ดˆ๋ž˜ํ•œ๋‹ค.

const Direction = Object.freeze({
  UP: Symbol("up"),
  DOWN: Symbol("down"),
  LEFT: Symbol("left"),
  RIGHT: Symbol("right"),
});

const Mood = Object.freeze({
  UP: Symbol("up"),
  DOWN: Symbol("down"),
  HAPPY: Symbol("happy"),
});

console.log(Direction.UP === Mood.UP); // false

์ด๋ฅผ Symbol()๋กœ ์ •์˜ํ•˜๋ฉด ๊ฐ’์ด ๊ณ ์œ ํ•œ ๊ฐ’์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๊ณ ์œ ์„ฑ ๋ณด์žฅํ•˜๋ ค๊ณ  ๊ตณ์ด Symbol()์„ ์ถ”๊ฐ€ํ–ˆ์„๊นŒ? uuid ์จ๋„ ๊ณ ์œ ์„ฑ์€ ๋ณด์žฅ๋œ๋‹ค.

const { v4: uuidv4 } = require('uuid');

const Direction = Object.freeze({
  UP: uuidv4(),
  DOWN: uuidv4(),
  LEFT: uuidv4(),
  RIGHT: uuidv4(),
});

const Mood = Object.freeze({
  UP: uuidv4(),
  DOWN: uuidv4(),
  HAPPY: uuidv4(),
});

console.log(Direction.UP === Mood.UP); // false

2. ๋น„๊ณต๊ฐœ ์†์„ฑ ๊ตฌํ˜„

๋งŒ์•ฝ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ ํŠน์ • ๊ฐ’์˜ ์ˆœํšŒ๋ฅผ ํ•˜์ง€ ์•Š๊ณ  ์‹ถ์œผ๋ฉด ์–ด๋–ป๊ฒŒํ• ๊นŒ?

const obj = {};

Object.defineProperties(obj, {
  name: {
    value: 'jihyup',
    enumerable: true,
  },
  age: {
    value: '31',
    enumerable: true,
  },
  year: {
    value: '1993',
    enumerable: false,
  }
});

for (let i in obj) {
  console.log(i); // name age
}

์•ž์„œ ๋ฐฐ์šด Object.defineProperties() ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด enumerable ์†์„ฑ์„ false๋กœ ์ง€์ •ํ•˜์—ฌ ์ˆœํšŒํ•˜์ง€ ์•Š๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ Symbol๋กœ ๊ฐ์ฒด์˜ ํ‚ค๋ฅผ ์ง€์ •ํ•˜๋ฉด enumerable ์†์„ฑ์ด ์ž๋™์œผ๋กœ false์ด๊ธฐ์— ์œ„ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

const obj = {
  name: 'jihyup',
  age: '31',
  [Symbol('year')]: '1993'
}

for (let i in obj) {
  console.log(i); // name age
}

3. Well-known Symbol

๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ฐ์ฒด, ๋ฐฐ์—ด, Map()์„ ์กฐํšŒํ•ด๋ณด๊ฒ ๋‹ค.

๋ฐฐ์—ด ์กฐํšŒ

๋ฐฐ์—ด ์กฐํšŒ

๊ฐ์ฒด ์กฐํšŒ

๊ฐ์ฒด ์กฐํšŒ

Map ์กฐํšŒ

Map ์กฐํšŒ

๊ณตํ†ต์ ์œผ๋กœ Symbol(Symbol.iterator) ์†์„ฑ์ด ์กด์žฌํ•œ๋‹ค.

// Map ์˜ˆ์ œ
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
console.log("Map iteration:");
for (const [key, value] of map) {
    console.log(key, value); // a 1 b 2 c3
}

// ๋ฐฐ์—ด ์˜ˆ์ œ
const array = [1, 2, 3, 4, 5];
console.log("\\nArray iteration:");
for (const item of array) {
    console.log(item); // 1 2 3 4 5
}

// ๊ฐ์ฒด ์˜ˆ์ œ (์—๋Ÿฌ ๋ฐœ์ƒ)
const obj = {a: 1, b: 2, c: 3};
console.log("\\nObject iteration (will throw an error):");
try {
    for (const item of obj) {
        console.log(item);
    }
} catch (error) {
    console.log("Error:", error.message); // Error: obj is not iterable
}

Symbol(Symbol.iterator) ์†์„ฑ์€ for โ€ฆ of ๋ฌธ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ค€๋‹ค. ์—ฌ๊ธฐ์„œ Map๊ณผ ๊ฐ์ฒด์˜ ์ฐจ์ด์ ์ด ๋ฐœ์ƒํ•œ๋‹ค. ๊ฐ์ฒด๋Š” iterator ์†์„ฑ์ด ์—†๊ณ , Map์€ iterator ์†์„ฑ์ด ์žˆ์–ด for โ€ฆ of ๋ฌธ์—์„œ ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

console.dir(Symbol)์„ ํ†ตํ•ด ์ž˜ ์•Œ๋ ค์ง„ ์‹ฌ๋ณผ์„ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.

์ž˜ ์•Œ๋ ค์ง„ ์‹ฌ๋ณผ ํ”„๋กœํผํ‹ฐ ๋ฐ Symbol ๋งค์„œ๋“œ

์ž˜ ์•Œ๋ ค์ง„ ์‹ฌ๋ณผ ํ”„๋กœํผํ‹ฐ ๋ฐ Symbol ๋งค์„œ๋“œ

Quiz

#1. ๋‹ค์Œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š”?

try {
  let newSymbol = new Symbol();
  console.log("์ •์ƒ์ ์œผ๋กœ ์ƒ์„ฑ๋จ");
} catch {
  console.log("์—๋Ÿฌ ๋ฐœ์ƒ");
}

#2. ๋‹ค์Œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š”?

let id = Symbol.for("id");
let id2 = Symbol.for("id");
console.log(id === id2);

์ฐธ๊ณ  ๋ฌธํ—Œ

<aside> ๐Ÿ“Ž

https://witch.work/posts/javascript-symbol-usage#12-์‹ฌ๋ณผ์˜-์„ค๋ช…-์ธ์ž

</aside>