๐๏ธ 12. Map, Set, WeakMap โ '๊ทธ๋ฅ ๊ฐ์ฒด'๋ฅผ ์ฐ๋ฉด ์ ๋๋ ๊ฒฐ์ ์ ์๊ฐ๋ค
๐ ๊ฐ์
์ผ๋ฐ ๊ฐ์ฒด ๋์ Map์, ๋ฐฐ์ด ๋์ Set์ ์จ์ผ ํ ์ํฉ, WeakMap์ ๋ฉ๋ชจ๋ฆฌ ์ ๋ต, ์ค๋ฌด ์บ์ฑยท์ค๋ณต ์ ๊ฑฐ ํจํด์ ์์ ์ ๋ณตํฉ๋๋ค.
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ผ๋ฐ ๊ฐ์ฒด(
{}) ๋์Map์, ์ผ๋ฐ ๋ฐฐ์ด ๋์Set์ ์ฐ๋ ํ๋จ ๊ธฐ์ค์ ์๋ค.WeakMap์ด ์ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง์ ์ต์ ์ธ์ง ์ดํดํ๋ค.- ์บ์ฑ, ์ค๋ณต ์ ๊ฑฐ, ๊ทธ๋ํ ํ์ ๋ฑ ์ค๋ฌด ํจํด์์ ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ ํํ๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค 1. ์ ์์์ผ ํ๋๊ฐ?
- ๐บ๏ธ 2. Map โ ์ง์ ํ ํค-๊ฐ ์๋ฃ๊ตฌ์กฐ
- ๐ต 3. Set โ ์ค๋ณต ์๋ ์ปฌ๋ ์
- ๐ป 4. WeakMap & WeakSet โ ์ฝํ ์ฐธ์กฐ์ ํ
- ๐ 5. ์๋ฃ๊ตฌ์กฐ ์ ํ ๊ฐ์ด๋
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 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).length | map.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) ์กด์ฌ ์ฌ๋ถ ํ์ธ | Set | has() 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 ๊ฐ์ฒด ํ๋ฅผ ํ ๋ฒ ๋ ๋ดค๋ค. ์ด์ ์์ ์๊ฒ ์ ํํ ์ ์์ ๊ฒ ๊ฐ๋ค. ๋ค์์ ์ ๋๋ ์ดํฐ... ๋ญ๊ฐ ์ข ์ด๋ ต๊ฒ ์ง๋ง ํด๋ณด์.