본문 바로가기

언어(JS,TS)/JavaScript

JS [ Study: Map vs WeakMap ]

Map과 WeakMap의 차이점이 궁금해져서 찾아보았습니다.

0. 공통점

  • 둘 다 객체(Object) 처럼 key-value 쌍으로 데이터를 저장합니다.
  • 둘 다 임의의 값을 key로 사용할 수 있음 (단, WeakMap은 예외 존재 → 아래 설명).
  • 둘 다 get, set, delete, has 메서드를 제공합니다.

1. Map의 특징

Map은 ES6에서 추가된 자료구조로, 기존 객체(Object)의 한계를 보완하기 위해 만들어졌습니다.

객체는 키로 문자열이나 심볼만 사용할 수 있지만, Map은 모든 값을 키로 사용할 수 있습니다.

즉, 객체, 함수, 숫자, 문자열 등 어떤 타입이든 키로 활용할 수 있습니다.

const map = new Map();
const objKey = { id: 1 };

map.set(objKey, 'User');
map.set('role', 'Admin');

console.log(map.get(objKey)); // 'User'
console.log(map.get('role')); // 'Admin'

주요 특징

  • 키로 모든 자료형을 사용할 수 있습니다.
  • 삽입 순서를 유지하며, 순회가 가능합니다.
  • map.size로 크기를 확인할 수 있습니다.
  • 내부적으로 강한 참조(strong reference) 를 유지합니다.

내부 동작

Map은 해시 테이블 기반으로 동작하며, 평균 접근 복잡도는 O(1)입니다.

엔진은 키에 대한 강한 참조를 유지하기 때문에, 키로 사용된 객체가 외부에서 제거되어도 Map 내부에는 남아 있습니다.

let obj = { id: 1 };
const map = new Map();
map.set(obj, 'data');

obj = null;
console.log(map.size); // 1

이 경우 객체는 외부에서 더 이상 참조되지 않지만, Map이 참조를 유지하고 있으므로 가비지 컬렉션(GC)의 대상이 되지 않습니다.

따라서 대규모 데이터나 동적으로 변하는 객체를 다룰 때는 명시적으로 삭제(delete) 해야 메모리 누수를 방지할 수 있습니다.


2. WeakMap의 개념과 특징

WeakMap은 이름처럼 “약한 참조(weak reference)”를 사용하는 자료구조입니다.

즉, 키로 사용된 객체가 외부에서 더 이상 참조되지 않으면, GC가 해당 객체를 자동으로 해제할 수 있습니다.

이는 메모리 안전성을 높여 주며, 임시 데이터 저장이나 캐시 구조에서 자주 사용됩니다.

let obj = { id: 1 };
const weakMap = new WeakMap();

weakMap.set(obj, 'User');
obj = null; // 외부 참조 제거
// 이후 GC가 발생하면 자동으로 해제됩니다.

주요 특징

  • 키는 반드시 객체(Object) 여야 합니다.
  • 순회(forEach, for...of)가 불가능합니다.
  • size 속성이 없습니다.
  • 약한 참조를 사용하기 때문에 GC가 자유롭게 객체를 해제할 수 있습니다.

내부 동작

WeakMap은 객체를 WeakRef 형태로 저장합니다.

GC는 객체가 외부에서 더 이상 참조되지 않는다고 판단하면, WeakMap 내부의 데이터도 함께 제거합니다.

이 과정은 비동기적으로 진행되며, 개발자가 명시적으로 감지할 수 없습니다.

이 때문에 WeakMap은 순회나 크기 확인 기능을 지원하지 않습니다.

GC 타이밍에 따라 데이터가 갑자기 사라질 수 있기 때문입니다.


3. Map과 WeakMap 비교

구분 Map WeakMap
키 타입 모든 값 (객체, 문자열, 숫자 등) 객체만 가능
참조 방식 강한 참조 약한 참조
GC 관여 자동 해제 안 됨 외부 참조 없으면 자동 해제
순회 가능 여부 가능 (for...of, forEach) 불가능
size 속성 있음 없음
사용 목적 일반 데이터 저장 객체 기반 임시 데이터, 캐시, private 데이터

Map은 명시적인 데이터 관리가 필요한 경우에 적합하며,

WeakMap은 객체의 생명주기와 데이터의 수명을 일치시킬 때 적합합니다.


4. 사용 시점과 예시

Map을 사용하는 경우

  1. 명확하게 관리되는 데이터가 필요할 때
  2. 원시값을 키로 사용할 때
  3. 순회, 크기 확인, 로깅 등이 필요한 경우
const routeMap = new Map([
  ['/home', 'HomePage'],
  ['/login', 'LoginPage'],
]);

for (const [path, page] of routeMap) {
  console.log(`${path} -> ${page}`);
}

이 경우 Map은 일반적인 키-값 매핑 구조로 동작하며,

순회가 가능하고 데이터 크기를 쉽게 확인할 수 있습니다.

WeakMap을 사용하는 경우

  1. 객체의 생명주기와 함께 데이터가 사라져야 할 때
  2. 비공개(private) 데이터를 저장할 때
  3. 메모리 누수를 방지해야 할 때
const cache = new WeakMap();

function getUserData(user) {
  if (!cache.has(user)) {
    cache.set(user, fetchFromDB(user.id));
  }
  return cache.get(user);
}

위 예제에서 user 객체가 메모리에서 사라지면,

WeakMap 내부의 캐시 데이터도 자동으로 제거됩니다.

이는 서버 캐시나 프론트엔드 DOM 관리에 매우 유용한 패턴입니다.


5. 성능 및 메모리 관점

  • Map은 조회, 삽입, 삭제 모두 평균 O(1) 성능을 가집니다.데이터가 쌓이면 누수가 발생할 수 있습니다.
  • 하지만 메모리 해제는 수동으로 처리해야 합니다.
  • WeakMap은 성능은 비슷하지만, GC 의존적입니다.
  • 개발자가 직접 관리할 필요는 없지만, 데이터 개수를 정확히 알 수 없고 순회가 불가능합니다.

정리하자면 Map데이터 중심의 구조이고,

WeakMap객체 생명주기 중심의 구조입니다.


6. 실무에서의 선택 기준

실무에서는 다음과 같이 구분하는 것이 일반적입니다.

  • React, Vue 등 프론트엔드 프레임워크 내부에서는객체가 사라지면 데이터도 함께 정리되므로 메모리 누수를 방지할 수 있습니다.
  • DOM 노드나 컴포넌트 상태를 관리할 때 WeakMap을 사용합니다.
  • Node.js 백엔드 환경에서는캐시나 미들웨어 컨텍스트처럼 요청마다 객체가 달라지는 경우에만 WeakMap을 사용합니다.
  • 설정값, 사용자 세션, 라우팅 테이블 등 명확히 관리되는 데이터에는 Map을 사용합니다.
// Express 요청별 캐시 예시
const requestCache = new WeakMap();

function middleware(req, res, next) {
  if (!requestCache.has(req)) {
    requestCache.set(req, new Map());
  }
  next();
}

요청이 종료되면 req 객체가 GC 대상이 되고,

함께 저장된 데이터도 자연스럽게 정리됩니다.


7. 결론

MapWeakMap의 가장 큰 차이는 참조 강도와 메모리 관리 방식입니다.

Map은 명시적 관리가 필요한 데이터 구조이며,

WeakMap은 객체의 생명주기에 따라 자동으로 정리되는 구조입니다.

즉,

  • 데이터의 수명을 직접 관리해야 한다면 → Map
  • 객체의 생명주기와 함께 자동 정리를 원한다면 → WeakMap

이 원칙만 기억해도 대부분의 상황에서 올바른 선택을 할 수 있습니다.


핵심 요약

항목 Map WeakMap
모든 값 객체만
참조 강한 참조 약한 참조
GC 해제 수동 자동
순회/크기 가능 불가
주 용도 일반 데이터 저장 캐시, private 데이터
메모리 관리 직접 관리 자동 관리

Map은 구조적이고 예측 가능한 데이터 저장소이며,

WeakMap은 객체 중심의 메모리 친화적 저장소입니다.

두 자료구조의 핵심은 단순한 문법 차이가 아니라,

“데이터 생명주기를 어디서 제어할 것인가”에 있습니다.