Map과 Set — Object, Array와의 차이와 활용 시점
Map과Set은 ES6에서 추가된 컬렉션 자료구조입니다. 일반 객체와 배열로도 비슷한 일을 할 수 있지만, 특정 상황에서는 Map과 Set이 훨씬 적합합니다.
Map — 키-값 저장소
Map은 어떤 타입이든 키로 사용 할 수 있는 키-값 저장소입니다.
const map = new Map();
// 다양한 타입의 키
map.set("name", "정훈");
map.set(42, "숫자 키");
map.set(true, "불린 키");
const objKey = { id: 1 };
map.set(objKey, "객체 키"); // 객체도 키로 사용 가능!
console.log(map.get("name")); // "정훈"
console.log(map.get(42)); // "숫자 키"
console.log(map.get(objKey)); // "객체 키"
console.log(map.size); // 4
Map vs Object
// 1. 키 타입 — Object는 문자열/Symbol만 가능
const obj = {};
obj[1] = "a";
obj["1"] = "b";
console.log(obj[1]); // "b" — 1이 "1"로 변환되어 덮어씀
const map = new Map();
map.set(1, "a");
map.set("1", "b");
console.log(map.get(1)); // "a" — 구분됨!
console.log(map.get("1")); // "b"
// 2. 순서 보장 — Map은 삽입 순서 보장
// Object도 ES2015+ 스펙에서는 대부분 순서 보장하지만, 정수 키는 오름차순 정렬됨
// 3. size — Map은 O(1), Object는 Object.keys().length
console.log(map.size); // 간단!
// 4. 이터러블 — Map은 직접 순회 가능
for (const [key, value] of map) {
console.log(key, value);
}
Map의 주요 메서드
const map = new Map([
["a", 1],
["b", 2],
["c", 3],
]);
map.has("a"); // true — 존재 확인
map.delete("b"); // true — 삭제
map.clear(); // 전체 삭제
// 순회 메서드
map.keys(); // MapIterator {"a", "c"}
map.values(); // MapIterator {1, 3}
map.entries(); // MapIterator {["a", 1], ["c", 3]}
map.forEach((value, key) => console.log(key, value));
Map을 쓰면 좋은 경우
// 1. 빈번한 추가/삭제 — Object보다 성능 우수
const cache = new Map();
cache.set(url, response);
cache.delete(oldUrl);
// 2. DOM 요소를 키로 사용
const elementData = new Map();
elementData.set(document.body, { scrollY: 0 });
// 3. 객체 → Map → 객체 변환
const obj = { a: 1, b: 2 };
const map = new Map(Object.entries(obj));
const backToObj = Object.fromEntries(map);
Set — 중복 없는 값 컬렉션
Set은 고유한 값 만 저장하는 컬렉션입니다.
const set = new Set();
set.add(1);
set.add(2);
set.add(2); // 무시됨 — 이미 존재
set.add(3);
console.log(set.size); // 3
console.log(set.has(2)); // true
set.delete(2);
console.log(set.has(2)); // false
배열 중복 제거 — 가장 흔한 활용
const arr = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3, 4]
// 문자열 중복 제거
const chars = [...new Set("banana")].join("");
console.log(chars); // "ban"
이 패턴은 코딩 테스트에서 정말 자주 쓰입니다.
Set의 참조 비교
const set = new Set();
set.add({ name: "a" });
set.add({ name: "a" }); // 서로 다른 객체이므로 둘 다 추가됨
console.log(set.size); // 2 — 참조가 다르면 다른 값으로 취급
집합 연산 (ES2025)
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);
// ES2025 Set 메서드
a.union(b); // Set {1, 2, 3, 4, 5, 6} — 합집합
a.intersection(b); // Set {3, 4} — 교집합
a.difference(b); // Set {1, 2} — 차집합
a.symmetricDifference(b); // Set {1, 2, 5, 6} — 대칭차집합
a.isSubsetOf(b); // false — 부분집합 여부
a.isSupersetOf(b); // false — 상위집합 여부
a.isDisjointFrom(b); // false — 교집합 없는지 여부
// ES2025 이전 방식 (폴리필)
const union = new Set([...a, ...b]);
const intersection = new Set([...a].filter((x) => b.has(x)));
const difference = new Set([...a].filter((x) => !b.has(x)));
WeakMap과 WeakSet
키(WeakMap) 또는 값(WeakSet)에 객체만 저장할 수 있고, 가비지 컬렉션을 방해하지 않습니다.
const weakMap = new WeakMap();
let obj = { data: "important" };
weakMap.set(obj, "메타데이터");
console.log(weakMap.get(obj)); // "메타데이터"
obj = null; // 원래 객체에 대한 참조가 사라지면
// weakMap에서도 자동으로 제거됨 (GC에 의해)
WeakMap의 활용
// 1. 프라이빗 데이터 저장
const privateData = new WeakMap();
class User {
constructor(name, password) {
this.name = name;
privateData.set(this, { password }); // 외부에서 접근 불가
}
checkPassword(input) {
return privateData.get(this).password === input;
}
}
// 2. DOM 요소에 데이터 연결 (메모리 누수 방지)
const elementMeta = new WeakMap();
function trackElement(el) {
elementMeta.set(el, { clicks: 0 });
}
// el이 DOM에서 제거되면 WeakMap에서도 자동 정리
언제 뭘 쓸까?
| 상황 | 추천 자료구조 |
|---|---|
| 일반 키-값 저장 (문자열 키) | Object |
| 비문자열 키, 빈번한 추가/삭제 | Map |
| 중복 제거, 존재 여부 확인 | Set |
| 메모리 누수 방지 (캐시, DOM) | WeakMap / WeakSet |
| JSON 직렬화 필요 | Object (Map은 직렬화 안 됨) |
** 기억하기 **: Object는 "구조화된 데이터", Map은 "딕셔너리", Set은 "중복 없는 목록"으로 기억하면 됩니다. 특히
[...new Set(arr)]로 배열 중복 제거하는 패턴은 코딩 테스트 필수입니다.
댓글 로딩 중...