JavaScript 내장 객체 - Map, WeakMap

Take!·2025년 8월 15일

JavaScript

목록 보기
11/12

Map, WeakMap

Zustand와 Jotai를 공부하여 내부 구성 코드를 보던 중, weakMap을 사용한 것을 보게 되었다. 개념은 알고 있었지만 실무에서는 잘 사용하지 않았던 내장 객체라 좀 더 눈길이 가게 되어 자세히 알아보았다...

JavaScript Map

JavaScript Map 객체는 기존 객체와는 다르게 key 값으로 모든 형태의 JavaScript 값이 올 수 있다는 점이 가장 큰 차이점이다.

특징

  • 다양한 키 타입: 문자열, 숫자, 불리언, 심볼뿐만 아니라 객체나 함수 같은 복잡한 타입도 key로 사용 가능하다.
  • 순서 보장: Map에 추가된 순서대로 key-value가 저장되고, 순회(iteration)시에도 그 순서가 보장된다.
  • 크기 확인: size 프로퍼티를 통해 Map에 저장된 요소의 개수를 쉽게 확인할 수 있다.
  • 편리한 메서드: set(), get(), has(), delete(), clear() 등 데이터를 조작하기 위한 명확하고 편리한 메서드들을 제공한다.
  • 이터러블(iterable): for...of 루프나 forEach() 메서드를 사용하여 쉽게 순회할 수 있다.

사용 예시

<script>
// Map 생성
const map = new Map();

// 키로 사용할 객체
const user = { name: 'Alice' };

// 데이터 추가 (set)
map.set('name', 'Bob');      // 문자열 키
map.set(1, 'one');           // 숫자 키
map.set(true, 'isTrue');     // 불리언 키
map.set(user, { role: 'admin' }); // 객체 키

// 데이터 조회 (get)
console.log(map.get('name')); // "Bob"
console.log(map.get(user));   // { role: 'admin' }

// 크기 확인 (size)
console.log(map.size); // 4

// 키 존재 여부 확인 (has)
console.log(map.has(1)); // true

// 데이터 삭제 (delete)
map.delete(1);
console.log(map.size); // 3

// 순회 (forEach)
map.forEach((value, key) => {
  console.log(`키: ${key}, 값: ${value}`);
});
</script>

new Map()을 써야할 때?

  • 다양한 타입의 데이터를 key로 사용해야 할 때
  • 저장된 데이터의 순서가 중요할 때
  • 컬렉션의 크기를 알아야 하거나, 전체 데이터를 순회해야 할 때

JavaScript WeakMap

WeakMap은 Map과 매우 유사하지만, key로 오직 객체만을 사용할 수 있으며, 키에 대한 약한 참조(weak reference)를 한다는 결정적인 차이가 있다.

특징

  • 객체 전용 키: WeakMap의 키는 반드시 객체여야 한다. 원시값(primitive values)을 키로 사용하면 에러가 발생한다.
  • 약한 참조: '약한 참조'는 WeakMap의 핵심 개념이다. WeakMap의 키로 사용된 객체를 참조하는 다른 코드가 없다면, 해당 객체는 가비지 컬렉션의 대상이 된다. 가비지 컬렉터가 이 객체를 메모리에서 제거하면, WeakMap에서도 해당하는 키-값이 자동으로 삭제된다.
  • 메모리 누수 방지: 약한 참조의 특성 덕분에, WeakMap은 객체와 관련된 부가적인 데이터를 저장할 때, 메모리 누수를 방지하는 데 매우 유용하다.
  • 순회 불가: 가비지 컬렉션에 의해 언제든 요소가 사라질 수 있기 때문에, WeakMap은 현재 포함된 요소의 목록을 알 수 없다. 따라서 forEach, keys, values, entries 같은 순회 메서드나 size, clear 메서드를 제공하지 않는다.

사용 예시

<script>
let user = { name: 'John' };
const weakMap = new WeakMap();

// 데이터 추가 (set)
weakMap.set(user, 'some metadata');

// 데이터 조회 (get)
console.log(weakMap.get(user)); // "some metadata"

// 키 존재 여부 확인 (has)
console.log(weakMap.has(user)); // true

// user 객체를 참조하는 곳이 더 이상 없도록 null을 할당
user = null;

// 이 시점 이후, 가비지 컬렉터가 동작하면
// 'user' 객체는 메모리에서 제거되고,
// weakMap에서도 해당 키-값 쌍이 자동으로 사라집니다.
// (정확한 시점은 예측할 수 없습니다.)
</script>

WeakMap()을 써야할 때?

  • 객체에 대한 부가적인 정보를 저장하고 싶지만, 해당 객체가 더 이상 필요 없어졌을 때 이 부가 정보도 함께 메모리에서 깨끗하게 제거하고 싶을 때.
  • 메모리 누수에 민감한 애플리케이션 개발 시.

Jotai가 WeakMap을 사용하는 이유

1. 아톰과 실제 상태 값의 분리

  • Jotai에서 만드는 atom 객체 자체는 상태 값이 아니라, 상태를 가리키는 key와 같다. 실제 상태 값, 구독자 목록 등은 Jotai 내부의 별도 저장소에서 보관된다. --> WeakMap이 이 저장소 역할을 한다!

  • key: atom 객체

  • value: 해당 atom의 실제 상태 값(value). 이 atom에 의존하는 다른 atom 목록(dependents)

2. 자동 메모리 관리(가비지 컬렉션)

  • Map 대신 WeakMap을 사용하는 결정적인 이유이다. 만약 어떤 atom이 더 이상 코드 어디에서도 참조되지 않아 쓸모가 없어지면, 자바스크립트 가비지 컬렉터는 이 atom 객체를 메모리에서 제거한다.
  • WeakMap은 키를 약하게 참조하기 때문에, 키가 사라지면 WeakMap에 저장된 해당 데이터(상태 값 등)도 자동으로 함께 삭제된다. 덕분에 개발자가 신경 쓰지 않아도 메모리 누수(memory leak)가 발생하지 않는다.

Map vs WeakMap 핵심 요소 비교

profile
확장성 있는 설계와 유지보수가 용이한 클린 코드 지향하는 개발자입니다.

0개의 댓글