๐Ÿ—ƒ๏ธ 12. Map, Set, WeakMap โ€” '๊ทธ๋ƒฅ ๊ฐ์ฒด'๋ฅผ ์“ฐ๋ฉด ์•ˆ ๋˜๋Š” ๊ฒฐ์ •์  ์ˆœ๊ฐ„๋“ค

2026๋…„ 3์›” 8์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

์ผ๋ฐ˜ ๊ฐ์ฒด ๋Œ€์‹  Map์„, ๋ฐฐ์—ด ๋Œ€์‹  Set์„ ์จ์•ผ ํ•  ์ƒํ™ฉ, WeakMap์˜ ๋ฉ”๋ชจ๋ฆฌ ์ „๋žต, ์‹ค๋ฌด ์บ์‹ฑยท์ค‘๋ณต ์ œ๊ฑฐ ํŒจํ„ด์„ ์™„์ „ ์ •๋ณตํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ์ผ๋ฐ˜ ๊ฐ์ฒด({}) ๋Œ€์‹  Map์„, ์ผ๋ฐ˜ ๋ฐฐ์—ด ๋Œ€์‹  Set์„ ์“ฐ๋Š” ํŒ๋‹จ ๊ธฐ์ค€์„ ์•ˆ๋‹ค.
  • WeakMap์ด ์™œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€์— ์ตœ์ ์ธ์ง€ ์ดํ•ดํ•œ๋‹ค.
  • ์บ์‹ฑ, ์ค‘๋ณต ์ œ๊ฑฐ, ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰ ๋“ฑ ์‹ค๋ฌด ํŒจํ„ด์—์„œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•œ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 18๋ถ„(์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 11๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
[Map vs ๊ฐ์ฒด] โ†’ [Set vs ๋ฐฐ์—ด] โ†’ [WeakMap ๋ฉ”๋ชจ๋ฆฌ ์ „๋žต] โ†’ [์ž๋ฃŒ๊ตฌ์กฐ ์„ ํƒ ๊ธฐ์ค€]

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • "O(1) ์กฐํšŒ๊ฐ€ ํ•„์š”ํ•œ๋ฐ" โ†’ Map vs ๊ฐ์ฒด ์ค‘ ๋ฌด์—‡์„ ์“ธ์ง€ ํŒ๋‹จํ•œ๋‹ค.
  • ์ค‘๋ณต ํƒœ๊ทธ ์ œ๊ฑฐ์— Set์„ ์“ฐ๊ณ , ๊ทธ Set์„ ๋‹ค์‹œ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
  • WeakMap์œผ๋กœ DOM ์š”์†Œ์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋ถ™์ด๋Š” ํŒจํ„ด์„ ์ดํ•ดํ•œ๋‹ค.

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

๐Ÿฃ ์˜์ฒ : "์˜ํ˜ธ ๋‹˜, ์œ ์ € ID๋ฅผ ํ‚ค๋กœ ํ•ด์„œ ์œ ์ € ์ •๋ณด๋ฅผ ์บ์‹ฑํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์งฐ๋Š”๋ฐ์š”. ์ผ๋ฐ˜ ๊ฐ์ฒด๋กœ ํ–ˆ๋”๋‹ˆ ํ‚ค๊ฐ€ ์ˆซ์ž์ธ๋ฐ obj[42]์ฒ˜๋Ÿผ ์“ฐ๋ฉด ๋ฌธ์ž์—ด "42"๋กœ ๋ณ€ํ™˜๋˜๋Š” ๊ฒŒ ๋งˆ์Œ์— ๊ฑธ๋ ค์š”. ๊ทธ๋ฆฌ๊ณ  ์บ์‹œ์—์„œ ํ‚ค ๊ฐœ์ˆ˜๋ฅผ ์„ธ๋ ค๋ฉด Object.keys(obj).length๋ฅผ ์จ์•ผ ํ•˜๋Š”๋ฐ ์ด๊ฒƒ๋„ ์ข€ ๋ฒˆ๊ฑฐ๋กญ๊ณ ์š”."

๐Ÿฆ ์˜ํ˜ธ: "๋”ฑ Map์„ ์“ธ ๋•Œ์•ผ. Map์€ ์–ด๋–ค ํƒ€์ž…์ด๋“  ํ‚ค๋กœ ์“ธ ์ˆ˜ ์žˆ๊ณ , ์‚ฝ์ž… ์ˆœ์„œ๋ฅผ ์œ ์ง€ํ•˜๊ณ , .size๋กœ ๋ฐ”๋กœ ํฌ๊ธฐ๋ฅผ ์•Œ ์ˆ˜ ์žˆ์–ด. ๊ทธ๋ฆฌ๊ณ  ์ˆซ์ž ํ‚ค๊ฐ€ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜๋˜๋Š” ๋ฌธ์ œ๋„ ์—†์–ด. ๊ฐ์ฒด๋ฅผ ํ‚ค๋กœ ์จ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋ผ๋ฉด WeakMap๋„ ์žˆ๊ณ . ์ž๋ฃŒ๊ตฌ์กฐ ์„ ํƒ์€ ์ƒ๊ฐ๋ณด๋‹ค ์„ฑ๋Šฅ๊ณผ ๋ฉ”๋ชจ๋ฆฌ์— ํฐ ์˜ํ–ฅ์„ ์ค˜."


๐Ÿค” 1. ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€?

์˜์ฒ ์ด๊ฐ€ ์˜คํ”„๋‹์—์„œ ๊ฒช์—ˆ๋˜ ํ‚ค ๋ณ€ํ™˜ ๋ฌธ์ œ๋Š” ์ผ๋ฐ˜ ๊ฐ์ฒด์˜ ๊ตฌ์กฐ์  ํ•œ๊ณ„ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์€ "์ ์žฌ์ ์†Œ์— ๋งž๋Š” ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ๋ฒ„๊ทธ์˜ ์ ˆ๋ฐ˜์ด ์‚ฌ๋ผ์ง„๋‹ค"๊ณ  ์กฐ์–ธํ•˜์…จ์ฃ .

// ์ผ๋ฐ˜ ๊ฐ์ฒด์˜ ํ•œ๊ณ„
 
const cache = {};
cache[1] = "๊ฒŒ์‹œ๊ธ€ 1";
cache[2] = "๊ฒŒ์‹œ๊ธ€ 2";
 
// ๋ฌธ์ œ 1: ์ˆซ์ž ํ‚ค๊ฐ€ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
console.log(Object.keys(cache)); // ["1", "2"] โ€” ์ˆซ์ž๊ฐ€ ๋ฌธ์ž์—ด์ด ๋จ
 
// ๋ฌธ์ œ 2: ํ”„๋กœํ† ํƒ€์ž… ์†์„ฑ๊ณผ ์ถฉ๋Œ ๊ฐ€๋Šฅ
const dangerousKey = "constructor"; // Object.prototype.constructor์™€ ์ถฉ๋Œ!
cache[dangerousKey] = "์œ„ํ—˜";
 
// ๋ฌธ์ œ 3: ํฌ๊ธฐ ์กฐํšŒ ๋ถˆํŽธ
const size = Object.keys(cache).length; // ๋งค๋ฒˆ ๋ฐฐ์—ด ์ƒ์„ฑ โ†’ ๋น„ํšจ์œจ
 
// ๋ฌธ์ œ 4: ๊ฐ์ฒด๋ฅผ ํ‚ค๋กœ ์“ธ ์ˆ˜ ์—†์Œ
const userObj = { id: 42 };
cache[userObj] = "๋ฐ์ดํ„ฐ"; // cache["[object Object]"] โ€” ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ ๊ฐ™์€ ํ‚ค!

๐Ÿ—บ๏ธ 2. Map โ€” ์ง„์ •ํ•œ ํ‚ค-๊ฐ’ ์ž๋ฃŒ๊ตฌ์กฐ

๊ฐ์ฒด vs Map ๋น„๊ต

ํŠน์„ฑ์ผ๋ฐ˜ ๊ฐ์ฒด {}Map
ํ‚ค ํƒ€์ž…๋ฌธ์ž์—ด/์‹ฌ๋ณผ๋งŒ๋ชจ๋“  ๊ฐ’ (๊ฐ์ฒด, ํ•จ์ˆ˜ ํฌํ•จ)
ํ‚ค ๋ณ€ํ™˜์ˆซ์ž โ†’ ๋ฌธ์ž์—ด ์ž๋™ ๋ณ€ํ™˜๊ทธ๋Œ€๋กœ ์œ ์ง€
์‚ฝ์ž… ์ˆœ์„œ๋ณด์žฅ ์•ˆ ๋จ (์ˆซ์ž ํ‚ค๋Š” ์ •๋ ฌ)โœ… ๋ณด์žฅ
ํฌ๊ธฐ ํ™•์ธObject.keys(obj).lengthmap.size
์ˆœํšŒfor...in (ํ”„๋กœํ† ํƒ€์ž… ํฌํ•จ ์ฃผ์˜)for...of, .forEach
ํ”„๋กœํ† ํƒ€์ž…Object.prototype ์ƒ์†์—†์Œ (์ˆœ์ˆ˜ ๋ฐ์ดํ„ฐ)
์„ฑ๋Šฅ (๋นˆ๋ฒˆํ•œ ์ถ”๊ฐ€/์‚ญ์ œ)๋ณดํ†ตโœ… ์ตœ์ ํ™”๋จ

Map ๊ธฐ๋ณธ ์‚ฌ์šฉ

๊ฐ์ฒด๋ณด๋‹ค ํ›จ์”ฌ ๋˜‘๋˜‘ํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” Map์˜ ๊ธฐ์ดˆ ์‚ฌ์šฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ‚ค์˜ ํƒ€์ž…์„ ๊ฐ€๋ฆฌ์ง€ ์•Š๊ณ , ๋„ฃ์€ ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌํ•ด์ฃผ๋Š” ๋“ฌ์งํ•œ ๋…€์„์ด์ฃ .

// Map ๊ธฐ๋ณธ ์‚ฌ์šฉ
const userCache = new Map();
 
// ์ถ”๊ฐ€
userCache.set(42, { name: "์˜์ฒ ", role: "junior" }); // ์ˆซ์ž ํ‚ค
userCache.set("์˜ํ˜ธ", { role: "lead" });              // ๋ฌธ์ž์—ด ํ‚ค
const keyObj = { type: "special" };
userCache.set(keyObj, "ํŠน์ˆ˜ ๋ฐ์ดํ„ฐ");                 // ๊ฐ์ฒด ํ‚ค
 
// ์กฐํšŒ
userCache.get(42);     // { name: "์˜์ฒ ", role: "junior" }
userCache.get("์˜ํ˜ธ");  // { role: "lead" }
userCache.get(keyObj); // "ํŠน์ˆ˜ ๋ฐ์ดํ„ฐ"
userCache.get(43);     // undefined (์—†๋Š” ํ‚ค)
 
// ํ™•์ธ
userCache.has(42);   // true
userCache.has(999);  // false
userCache.size;      // 3
 
// ์‚ญ์ œ
userCache.delete(42); // true (์‚ญ์ œ ์„ฑ๊ณต)
 
// ์ „์ฒด ์ˆœํšŒ (์‚ฝ์ž… ์ˆœ์„œ ๋ณด์žฅ)
for (const [key, value] of userCache) {
  console.log(key, "โ†’", value);
}
 
// ๋ณ€ํ™˜
const keysArr = [...userCache.keys()];   // ํ‚ค ๋ฐฐ์—ด
const valsArr = [...userCache.values()]; // ๊ฐ’ ๋ฐฐ์—ด
const entries = [...userCache.entries()]; // [key, value] ๋ฐฐ์—ด
 
// ๊ฐ์ฒด โ†’ Map ๋ณ€ํ™˜
const obj = { a: 1, b: 2 };
const map = new Map(Object.entries(obj));
 
// Map โ†’ ๊ฐ์ฒด ๋ณ€ํ™˜
const backToObj = Object.fromEntries(map);

Map ์‹ค๋ฌด ํŒจํ„ด

์˜์ฒ ์ด๊ฐ€ "๊ทธ๋Ÿผ Map์œผ๋กœ ์บ์‹œ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์š”?"๋ผ๊ณ  ๋ฌผ์—ˆ์„ ๋•Œ, ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ํ™”์ดํŠธ๋ณด๋“œ์— ์“ฑ์“ฑ ๊ทธ๋ ค์ค€ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์œ ํšจ ๊ธฐ๊ฐ„(TTL)๊นŒ์ง€ ๊ณ ๋ คํ•œ ์‹ค์ „ํ˜• ์บ์‹œ ๋กœ์ง์ž…๋‹ˆ๋‹ค.

// ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ โ€” ์š”์ฒญ ์บ์‹ฑ
 
class PostCache {
  #cache = new Map();
  #maxSize;
  #ttl; // Time To Live (ms)
 
  constructor({ maxSize = 100, ttl = 5 * 60 * 1000 } = {}) {
    this.#maxSize = maxSize;
    this.#ttl = ttl;
  }
 
  set(postId, data) {
    // ์ตœ๋Œ€ ํฌ๊ธฐ ์ดˆ๊ณผ ์‹œ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ํ•ญ๋ชฉ ์ œ๊ฑฐ (LRU ๊ฐ„์ด ๊ตฌํ˜„)
    if (this.#cache.size >= this.#maxSize) {
      const firstKey = this.#cache.keys().next().value;
      this.#cache.delete(firstKey);
    }
 
    this.#cache.set(postId, {
      data,
      expireAt: Date.now() + this.#ttl,
    });
  }
 
  get(postId) {
    const entry = this.#cache.get(postId);
    if (!entry) return null;
 
    if (Date.now() > entry.expireAt) {
      this.#cache.delete(postId); // TTL ๋งŒ๋ฃŒ
      return null;
    }
 
    return entry.data;
  }
 
  get size() {
    return this.#cache.size;
  }
}
 
const postCache = new PostCache({ maxSize: 50, ttl: 3 * 60 * 1000 });
 
async function getPost(postId) {
  const cached = postCache.get(postId);
  if (cached) return cached;
 
  const post = await fetchPost(postId);
  postCache.set(postId, post);
  return post;
}

๐Ÿ”ต 3. Set โ€” ์ค‘๋ณต ์—†๋Š” ์ปฌ๋ ‰์…˜

๋˜‘๊ฐ™์€ ๊ฐ’์ด ๋“ค์–ด์˜ค๋ฉด ์ž๋™์œผ๋กœ ๋ฌด์‹œํ•ด์ฃผ๋Š” ์•„์ฃผ ์˜๋ฆฌํ•œ ์ปฌ๋ ‰์…˜์ž…๋‹ˆ๋‹ค. ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ์†๋„๋„ ๋ฐฐ์—ด๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฅด์ฃ .

// ๊ธฐ๋ณธ ์‚ฌ์šฉ
const tagSet = new Set();
tagSet.add("javascript");
tagSet.add("react");
tagSet.add("javascript"); // ์ค‘๋ณต โ€” ๋ฌด์‹œ๋จ
tagSet.add("css");
 
tagSet.size;                    // 3 (์ค‘๋ณต ์ œ์™ธ)
tagSet.has("react");            // true โ€” O(1)
tagSet.has("python");           // false โ€” O(1)
tagSet.delete("css");           // true
 
// ๋ฐฐ์—ด๋กœ๋ถ€ํ„ฐ Set ์ƒ์„ฑ (์ค‘๋ณต ์ œ๊ฑฐ)
const tags = ["js", "react", "js", "css", "react", "ts"];
const uniqueTags = new Set(tags);
// Set { "js", "react", "css", "ts" }
 
// Set โ†’ ๋ฐฐ์—ด ๋ณ€ํ™˜
const uniqueTagsArr = [...uniqueTags]; // ["js", "react", "css", "ts"]
// ๋˜๋Š”
const uniqueTagsArr2 = Array.from(uniqueTags);
 
// ๊ฐ€์žฅ ์งง์€ ์ค‘๋ณต ์ œ๊ฑฐ ํŒจํ„ด
const unique = [...new Set(arr)];

Set ์‹ค๋ฌด ํŒจํ„ด

๋ฐฉ๋ฌธํ•œ ๊ฒŒ์‹œ๊ธ€์„ ์ถ”์ ํ•˜๊ฑฐ๋‚˜, ์ฒดํฌ๋ฐ•์Šค์˜ ์„ ํƒ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ Set์€ ์ตœ๊ณ ์˜ ํšจ์œจ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๋ฐฐ์—ด์˜ includes๊ฐ€ ํž˜๊ฒจ์›Œํ•  ๋•Œ Set์ด ๋“ฑํŒํ•  ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค.

// ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ โ€” ์‹ค๋ฌด Set ํ™œ์šฉ
 
// 1. ๋ฐฉ๋ฌธํ•œ ๊ฒŒ์‹œ๊ธ€ ์ถ”์  (O(1) ํ™•์ธ)
const visitedPostIds = new Set();
 
function markAsVisited(postId) {
  visitedPostIds.add(postId);
}
 
function isVisited(postId) {
  return visitedPostIds.has(postId); // O(1) โ€” ๋ฐฐ์—ด์˜ includes()๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฆ„
}
// ๋ฐฐ์—ด.includes()๋Š” O(n), Set.has()๋Š” O(1) โ€” ๊ทœ๋ชจ๊ฐ€ ํด์ˆ˜๋ก ์ฐจ์ด๊ฐ€ ๊ทน๋ช…
 
// 2. ์„ ํƒ๋œ ํ•ญ๋ชฉ ๊ด€๋ฆฌ (์ฒดํฌ๋ฐ•์Šค UI)
const selectedPostIds = new Set();
 
function toggleSelect(postId) {
  if (selectedPostIds.has(postId)) {
    selectedPostIds.delete(postId);
  } else {
    selectedPostIds.add(postId);
  }
}
 
const isSelected = (postId) => selectedPostIds.has(postId);
const selectedCount = selectedPostIds.size;
const selectedArray = [...selectedPostIds]; // API ์š”์ฒญ ์‹œ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜
 
// 3. ์ง‘ํ•ฉ ์—ฐ์‚ฐ
const followingSet = new Set([1, 2, 3, 4]); // ๋‚ด๊ฐ€ ํŒ”๋กœ์šฐํ•˜๋Š” ์œ ์ €
const followerSet = new Set([2, 3, 5, 6]);  // ๋‚˜๋ฅผ ํŒ”๋กœ์šฐํ•˜๋Š” ์œ ์ €
 
// ๊ต์ง‘ํ•ฉ (์„œ๋กœ ํŒ”๋กœ์šฐ)
const mutualFollowers = new Set(
  [...followingSet].filter((id) => followerSet.has(id))
); // Set { 2, 3 }
 
// ์ฐจ์ง‘ํ•ฉ (๋‚ด๊ฐ€ ํŒ”๋กœ์šฐํ•˜๋Š”๋ฐ ๋‚˜๋Š” ์•ˆ ํŒ”๋กœ์šฐํ•˜๋Š”)
const notFollowingBack = new Set(
  [...followingSet].filter((id) => !followerSet.has(id))
); // Set { 1, 4 }
 
// ํ•ฉ์ง‘ํ•ฉ
const allUsers = new Set([...followingSet, ...followerSet]);
// Set { 1, 2, 3, 4, 5, 6 }

๐Ÿ‘ป 4. WeakMap & WeakSet โ€” ์•ฝํ•œ ์ฐธ์กฐ์˜ ํž˜

์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด "DOM ์š”์†Œ์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ถ™์ผ ๋•Œ ์ผ๋ฐ˜ Map ์“ฐ๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์ƒ๊ฒจ์š”"๋ผ๊ณ  ํ–ˆ์„ ๋•Œ, ์˜์ฒ ์ด๋Š” ์ฒ˜์Œ์— "๊ทธ๋ƒฅ element.dataset์— ๋„ฃ์œผ๋ฉด ์•ˆ ๋˜๋‚˜์š”?"๋ผ๊ณ  ๋ฌผ์—ˆ๋‹ค. ์˜ํ˜ธ ๋‹˜์˜ ๋‹ต: "DOM์— ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ฌ๋Š” ๊ฑด ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ ์œ„๋ฐ˜์ด์•ผ. WeakMap ์ด ๋ฐ”๋กœ ์ด ๋ฌธ์ œ๋ฅผ ์œ„ํ•ด ์žˆ๋Š” ๊ฑฐ์•ผ."

WeakMap ์˜ ํ‚ค๋Š” ๋ฐ˜๋“œ์‹œ ๊ฐ์ฒด ์—ฌ์•ผ ํ•˜๊ณ , ํ‚ค ๊ฐ์ฒด๊ฐ€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜(GC)๋˜๋ฉด WeakMap์˜ ํ•ญ๋ชฉ๋„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐ ๋œ๋‹ค.

WeakMap ์‹ค๋ฌด ํŒจํ„ด

์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ์˜์ฒ ์ด์—๊ฒŒ "๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ง‰์œผ๋ฉด์„œ DOM์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•"์œผ๋กœ ์ถ”์ฒœํ•ด์ฃผ์‹  ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์š”์†Œ๊ฐ€ ์‚ฌ๋ผ์ง€๋ฉด ๋ฐ์ดํ„ฐ๋„ ์กฐ์šฉํžˆ ํ•จ๊ป˜ ์‚ฌ๋ผ์ง€๋Š” ์šฐ์•„ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฒ•์ด์ฃ .

// WeakMap์˜ ํ•ต์‹ฌ ํŠน์„ฑ:
// 1. ํ‚ค๋Š” ๋ฐ˜๋“œ์‹œ ๊ฐ์ฒด
// 2. ํ‚ค๊ฐ€ GC๋˜๋ฉด ๊ฐ’๋„ ์ž๋™ ์ œ๊ฑฐ (์•ฝํ•œ ์ฐธ์กฐ)
// 3. ์ดํ„ฐ๋Ÿฌ๋ธ”ํ•˜์ง€ ์•Š์Œ (.keys(), .values(), .size ์—†์Œ)
// 4. ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€์— ์ตœ์ 
 
// ์‹ค๋ฌด ํŒจํ„ด 1: DOM ์š”์†Œ์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์—ฐ๊ฒฐ
const domMetadata = new WeakMap();
 
function trackElement(element) {
  domMetadata.set(element, {
    clickCount: 0,
    lastInteracted: null,
    postId: element.dataset.postId,
  });
}
 
function onElementClick(element) {
  const meta = domMetadata.get(element);
  if (meta) {
    meta.clickCount++;
    meta.lastInteracted = new Date();
  }
}
 
// element๊ฐ€ DOM์—์„œ ์ œ๊ฑฐ๋˜๋ฉด WeakMap์˜ ํ•ญ๋ชฉ๋„ ์ž๋™ GC
// ์ผ๋ฐ˜ Map์„ ์“ฐ๋ฉด element๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ deleteํ•ด์•ผ ํ–ˆ์Œ
 
// ์‹ค๋ฌด ํŒจํ„ด 2: ์ธ์Šคํ„ด์Šค๋ณ„ private ๋ฐ์ดํ„ฐ ์ €์žฅ (ES2022 # ์ด์ „ ๋ฐฉ์‹)
const _privateData = new WeakMap();
 
class UserSession {
  constructor(userId, token) {
    // ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ ๋ถˆ๊ฐ€ํ•œ private ๋ฐ์ดํ„ฐ
    _privateData.set(this, { userId, token, loginAt: new Date() });
  }
 
  get userId() {
    return _privateData.get(this).userId;
  }
 
  isTokenValid() {
    const { token } = _privateData.get(this);
    return validateToken(token);
  }
}
 
const session = new UserSession(42, "secret-token");
session.userId; // 42 (getter๋กœ ์ ‘๊ทผ)
_privateData.get(session); // ์™ธ๋ถ€์—์„œ ์ด๋ ‡๊ฒŒ ์ง์ ‘ ์ ‘๊ทผ์€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์„ค๊ณ„์ƒ ๋ง‰์Œ
// session์ด GC๋˜๋ฉด _privateData ํ•ญ๋ชฉ๋„ ์ž๋™ ์ œ๊ฑฐ โ†’ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์—†์Œ

๐Ÿ“Š 5. ์ž๋ฃŒ๊ตฌ์กฐ ์„ ํƒ ๊ฐ€์ด๋“œ

ํ•„์š”ํ•œ ๊ฒƒ์„ ํƒ์ด์œ 
ํ‚ค-๊ฐ’ ์ €์žฅ, ํ‚ค๊ฐ€ ๋ฌธ์ž์—ด/์ˆซ์ž๋งŒ์ผ๋ฐ˜ ๊ฐ์ฒด {}๋” ๊ฐ„๊ฒฐํ•˜๊ณ  JSON ํ˜ธํ™˜
ํ‚ค๊ฐ€ ์ˆซ์ž/๊ฐ์ฒด/ํ•จ์ˆ˜ ๋“ฑ ๋‹ค์–‘ํ•จMapํƒ€์ž… ๋ณด์กด, ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ์—†์Œ
๋นˆ๋ฒˆํ•œ ์ถ”๊ฐ€/์‚ญ์ œ + ํฌ๊ธฐ ์กฐํšŒMap์„ฑ๋Šฅ ์ตœ์ ํ™”
์ค‘๋ณต ์ œ๊ฑฐSet์ž๋™ ์ค‘๋ณต ์ œ๊ฑฐ
O(1) ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธSethas() O(1) vs includes() O(n)
DOM ์š”์†Œ์— ๋ฐ์ดํ„ฐ ์—ฐ๊ฒฐWeakMap๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€
๊ฐ์ฒด ์ƒ์กด ๊ธฐ๊ฐ„์— ๋”ฐ๋ฅธ ์บ์‹œWeakMap์ž๋™ GC

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. Map๊ณผ ์ผ๋ฐ˜ ๊ฐ์ฒด({})๋ฅผ ์–ธ์ œ ๊ฐ๊ฐ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๊ฐ€?

โœ… ์ •๋‹ต: ํ‚ค๊ฐ€ ๋ฌธ์ž์—ด์ด๊ณ  JSON ์ง๋ ฌํ™”๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ๊ฐ์ฒด, ํ‚ค ํƒ€์ž…์ด ๋‹ค์–‘ํ•˜๊ฑฐ๋‚˜ ๋นˆ๋ฒˆํ•œ ์ถ”๊ฐ€/์‚ญ์ œ/ํฌ๊ธฐ ์กฐํšŒ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด Map.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ๊ฐ์ฒด๊ฐ€ ๋” ๋‚˜์€ ๊ฒฝ์šฐ: JSON.stringify() ์ง์ ‘ ์ ์šฉ, ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด๋กœ ์ดˆ๊ธฐํ™”, ์„œ๋ฒ„์™€ JSON ์ฃผ๊ณ ๋ฐ›์„ ๋•Œ
  • Map์ด ๋” ๋‚˜์€ ๊ฒฝ์šฐ: ์ˆซ์ž/๊ฐ์ฒด๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉ, ์‚ฝ์ž… ์ˆœ์„œ๊ฐ€ ์ค‘์š”, .size๋กœ ํฌ๊ธฐ ๋น ๋ฅด๊ฒŒ ํ™•์ธ, ํ”„๋กœํ† ํƒ€์ž… ์†์„ฑ๊ณผ ์ถฉ๋Œ ์šฐ๋ ค
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ๋Š” Map, ์„œ๋ฒ„์— ๋ณด๋‚ผ ๋•Œ๋Š” ๊ฐ์ฒด. ์ค‘๊ฐ„์— Object.fromEntries(map)์œผ๋กœ ๋ณ€ํ™˜."

Q2. Set.has()๊ฐ€ Array.includes()๋ณด๋‹ค ์„ฑ๋Šฅ์ƒ ์œ ๋ฆฌํ•œ ์ด์œ ๋Š”?

โœ… ์ •๋‹ต: Set.has()๋Š” ํ•ด์‹œ ๊ธฐ๋ฐ˜์œผ๋กœ O(1) ์‹œ๊ฐ„๋ณต์žก๋„, Array.includes()๋Š” ์ˆœ์ฐจ ํƒ์ƒ‰์œผ๋กœ O(n) ์‹œ๊ฐ„๋ณต์žก๋„๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ๋ฐฐ์—ด 100๊ฐœ์—์„œ ํŠน์ • ๊ฐ’ ์กด์žฌ ์—ฌ๋ถ€: ์ตœ์•…์˜ ๊ฒฝ์šฐ 100๋ฒˆ ๋น„๊ต
  • Set 100๊ฐœ์—์„œ ํŠน์ • ๊ฐ’ ์กด์žฌ ์—ฌ๋ถ€: ํ•ด์‹œ ๊ณ„์‚ฐ ํ›„ 1๋ฒˆ ๋น„๊ต (๊ฑฐ์˜ ์ƒ์ˆ˜ ์‹œ๊ฐ„)
  • ๋ฐ์ดํ„ฐ๊ฐ€ ํด์ˆ˜๋ก (์ˆ˜์ฒœ, ์ˆ˜๋งŒ ๊ฐœ), ๋นˆ๋ฒˆํ•œ ์กฐํšŒ์ผ์ˆ˜๋ก Set์˜ ์ด์ ์ด ๊ทน๋Œ€ํ™”๋œ๋‹ค.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "'์ด๊ฒŒ ํฌํ•จ๋˜์–ด ์žˆ๋‚˜?'๋ฅผ ์ž์ฃผ ๋ฌผ์–ด์•ผ ํ•œ๋‹ค๋ฉด Set์œผ๋กœ. ๋ฐฐ์—ด์€ '๋ชฉ๋ก'์ด๊ณ , Set์€ '์‚ฌ์ „'์ด๋‹ค."

Q3. ์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„ โ€” ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ํŒ€ ํšŒ์˜

์˜์ˆ˜ PM: "์˜์ฒ  ๋‹˜, ์œ ์ €๊ฐ€ '์ข‹์•„์š”'๋ฅผ ๋ˆ„๋ฅธ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์„ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด์š”. ์ข‹์•„์š”/์ทจ์†Œ๊ฐ€ ๋นˆ๋ฒˆํ•˜๊ณ , ํŠน์ • ๊ฒŒ์‹œ๊ธ€์— ์ข‹์•„์š” ๋ˆŒ๋ €๋Š”์ง€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•ด์•ผ ํ•ด์š”. ์–ด๋–ค ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ์ ํ•ฉํ•œ๊ฐ€์š”?"

โœ… ์ •๋‹ต: Set<number> (๊ฒŒ์‹œ๊ธ€ ID Set). ์ข‹์•„์š” ์ถ”๊ฐ€/์‚ญ์ œ๊ฐ€ O(1), ํŠน์ • ๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š” ์—ฌ๋ถ€ ํ™•์ธ์ด O(1)์ด๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

// โœ… Set์„ ์‚ฌ์šฉํ•œ ์ข‹์•„์š” ๊ด€๋ฆฌ
class LikeManager {
  #likedPostIds = new Set();
 
  toggle(postId) {
    if (this.#likedPostIds.has(postId)) {
      this.#likedPostIds.delete(postId);
      return false; // ์ข‹์•„์š” ์ทจ์†Œ
    } else {
      this.#likedPostIds.add(postId);
      return true; // ์ข‹์•„์š”
    }
  }
 
  isLiked(postId) {
    return this.#likedPostIds.has(postId); // O(1)
  }
 
  getLikedIds() {
    return [...this.#likedPostIds]; // ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ (API ์š”์ฒญ ๋“ฑ)
  }
 
  get count() {
    return this.#likedPostIds.size;
  }
}
  • ๋ฐฐ์—ด๋กœ ๊ด€๋ฆฌํ•˜๋ฉด: includes() O(n), ์ข‹์•„์š” ์ทจ์†Œ ์‹œ filter() O(n) โ†’ ํ•ญ๋ชฉ์ด ๋งŽ์•„์งˆ์ˆ˜๋ก ๋А๋ ค์ง
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "๋นˆ๋ฒˆํ•œ ์ถ”๊ฐ€/์‚ญ์ œ/์กด์žฌ ํ™•์ธ = Set์˜ ์„ธ ๊ฐ€์ง€ ์žฅ์ . ์ด ์„ธ ๊ฐ€์ง€๊ฐ€ ํ•„์š”ํ•˜๋ฉด Set์ด ๋‹ต์ด๋‹ค."

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜ Set์œผ๋กœ '์ข‹์•„์š”' ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๋Š” ์ฝ”๋“œ ๋ณด๋ฉด์„œ, ๊ทธ๋™์•ˆ ๋ฐฐ์—ด๋กœ ํ–ˆ๋˜ ๊ฒŒ ์™œ ๋А๋ ธ๋Š”์ง€ ์ดํ•ด๋๋‹ค. Array.includes()๋ฅผ ๋ฃจํ”„ ์•ˆ์—์„œ ์“ฐ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, ๊ทธ๊ฒŒ O(n * m)์ด์—ˆ๋‹ค๋Š” ๊ฑธ... ์ด์ œ ์•ˆ๋‹ค.

WeakMap๋„ ์‹ ๊ธฐํ–ˆ๋‹ค. "์•ฝํ•œ ์ฐธ์กฐ"๋ผ๋Š” ๊ฐœ๋… ์ž์ฒด๊ฐ€ ์ƒˆ๋กœ์› ๋Š”๋ฐ, DOM ์š”์†Œ๊ฐ€ ์‚ฌ๋ผ์ง€๋ฉด ๊ทธ ๋ฐ์ดํ„ฐ๋„ ์ž๋™์œผ๋กœ GC๋œ๋‹ค๋Š” ๊ฒŒ ์ง„์งœ ์šฐ์•„ํ•œ ์„ค๊ณ„์ธ ๊ฒƒ ๊ฐ™๋‹ค.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "์ž๋ฃŒ๊ตฌ์กฐ๋Š” ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” ๊ทธ๋ฆ‡์ด ์•„๋‹ˆ๋ผ, ์šฐ๋ฆฌ์˜ ๋กœ์ง์„ ๊ฒฐ์ •ํ•˜๋Š” ๋‚˜์นจ๋ฐ˜์ž…๋‹ˆ๋‹ค. Map, Set, WeakMap์˜ ํŠน์„ฑ์„ ์ดํ•ดํ•˜๊ณ  ์ ์žฌ์ ์†Œ์— ๋ฐฐ์น˜ํ•˜๋Š” ๊ฒƒ, ๊ทธ๊ฒƒ์ด ๋ฐ”๋กœ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ๋งŒ๋“œ๋Š” ์ง€๋ฆ„๊ธธ์ž…๋‹ˆ๋‹ค."

ํ‡ด๊ทผํ•˜๊ณ  ์น˜ํ‚จ ๋จน์œผ๋ฉด์„œ Map vs ๊ฐ์ฒด ํ‘œ๋ฅผ ํ•œ ๋ฒˆ ๋” ๋ดค๋‹ค. ์ด์ œ ์ž์‹  ์žˆ๊ฒŒ ์„ ํƒํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ๋‹ค์Œ์€ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ... ๋ญ”๊ฐ€ ์ข€ ์–ด๋ ต๊ฒ ์ง€๋งŒ ํ•ด๋ณด์ž.


๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ