본문으로 건너뛰기
김신건의 로그

[Javascript] Map / Set / WeakMap / WeakSet

· 수정 · 📖 약 1분 · 373자/단어 #javascript #map #set #collection
JS Map, JS Set, JS WeakMap, JS WeakSet, JS Map / Set

정의

ES6 의 4 가지 컬렉션. 객체 / 배열보다 적합한 경우.

컬렉션강참조
Map모든 값모든 값
Set(값만)(값만)
WeakMap객체 only모든 값✗ (약참조)
WeakSet객체 only(값만)

Map

const m = new Map();
m.set('a', 1);
m.set(42, 'number key');
m.set({}, 'object key');     // 객체도 키
m.set(true, 'bool key');

m.get('a');                   // 1
m.has('a');                   // true
m.delete('a');
m.size;                        // 3
m.clear();

// 초기화
const m2 = new Map([
    ['a', 1],
    ['b', 2],
]);

Map 의 순회

for (const [k, v] of m) { ... }
for (const k of m.keys()) { ... }
for (const v of m.values()) { ... }
m.forEach((v, k) => { ... });

// 배열로
[...m.entries()]
[...m.keys()]
[...m.values()]

삽입 순서 보존.

Map vs Object

항목ObjectMap
키 타입문자열, Symbol모든 값
순서 보장부분적 (string vs int)✓ (삽입 순서)
sizeObject.keys(obj).length.size
순회for…in (prototype 주의)for…of (자체만)
기본 키prototype 키 (toString)없음
성능 (잦은 add/remove)느림빠름
JSON
// Object 함정
const o = {};
o.toString;     // function (prototype 에서)

// Map 안전
const m = new Map();
m.get('toString');   // undefined

Set

const s = new Set([1, 2, 3, 2, 1]);   // {1, 2, 3}
s.add(4);
s.has(2);                              // true
s.delete(1);
s.size;
s.clear();

[...s];                                // 배열로

자주 쓰는 패턴

// 배열 중복 제거
[...new Set([1, 2, 2, 3])]            // [1, 2, 3]

// 합집합
new Set([...a, ...b])

// 교집합
new Set([...a].filter(x => b.has(x)))

// 차집합
new Set([...a].filter(x => !b.has(x)))

ES2025 에서 Set.prototype.union, intersection 등 메서드 추가.

WeakMap

const wm = new WeakMap();
let obj = {};
wm.set(obj, 'metadata');

obj = null;     // obj 가 가비지 수집 → wm 의 entry 도 자동 제거

키가 객체만, 약참조. 객체가 GC 되면 entry 도 자동 제거.

용도

  • 객체 메타데이터 : 객체 자체에 property 추가하지 않고 부가 정보 보관
  • private state 흉내 : 외부에서 접근 못 함
const privateData = new WeakMap();

class User {
    constructor(name) {
        privateData.set(this, { password: 'secret' });
        this.name = name;
    }
    checkPassword(p) {
        return privateData.get(this).password === p;
    }
}

#private 필드 (ES2022) 가 표준이지만 WeakMap 으로도 가능.

WeakSet

const ws = new WeakSet();
let obj = { id: 1 };
ws.add(obj);
ws.has(obj);     // true
obj = null;       // ws 에서 자동 제거

객체의 집합 (예: “처리된 객체”, “방문한 객체”).

WeakMap / WeakSet 제약

  • 순회 메서드 없음 (forEach, entries 등)
  • size 없음
  • clear() 없음
  • 키는 객체만 (primitive 불가)

이유: 약참조 컬렉션은 어느 시점에 어떤 entry 가 살아있는지 알 수 없음.

자주 만나는 패턴

Map 으로 그룹화

const grouped = new Map();
for (const item of items) {
    if (!grouped.has(item.category)) grouped.set(item.category, []);
    grouped.get(item.category).push(item);
}

Set 으로 중복 체크

const seen = new Set();
for (const item of items) {
    if (seen.has(item.id)) continue;
    seen.add(item.id);
    process(item);
}

WeakMap 으로 캐시

const cache = new WeakMap();

function expensiveCompute(obj) {
    if (cache.has(obj)) return cache.get(obj);
    const result = doExpensive(obj);
    cache.set(obj, result);
    return result;
}
// obj 가 GC 되면 캐시도 자동 정리

함정

1. Map 의 키 비교

const m = new Map();
m.set({}, 1);
m.get({});      // undefined (다른 객체 참조)

const key = {};
m.set(key, 1);
m.get(key);     // 1

객체 키는 참조 동등성.

2. NaN 키

const m = new Map();
m.set(NaN, 'x');
m.get(NaN);     // 'x'  (SameValueZero, NaN === NaN 처럼 취급)

3. JSON 으로 직렬화 안 됨

JSON.stringify(new Map([['a', 1]]))    // '{}'
JSON.stringify(new Set([1, 2]))         // '{}'

// 변환 필요
JSON.stringify([...map])
JSON.stringify({ ...Object.fromEntries(map) })

참고

이 글의 용어 (3개)
[Javascript] Arrayjavascript
정의 JavaScript 의 는 순서 있는 객체. 정수 키 + . 풍부한 메서드 (map/filter/reduce/...) 가 함수형 스타일을 지원. 생성 핵심 메서드 (반복) …
[Javascript] Objectjavascript
정의 JavaScript 의 는 key-value 쌍의 컬렉션. 거의 모든 non-primitive 값이 객체. 함수, 배열, 정규식도 객체. 생성 접근 수정 / 삭제 Objec…
[Spring] ConcurrentReferenceHashMapspring
정의 는 Spring Framework 의 핵심 유틸리티. 의 동시성 과 / 의 GC 친화성 을 결합한 Map. JDK 표준에 없는 조합. 메모리 압박 시 GC 가 entry 를…

이 개념을 다룬 위키 페이지 (1)

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기