2025-09-29 01:24

  • 자바스크립트 Map은 키-값 쌍을 저장하고 키의 순서를 기억하는 순회 가능한 컬렉션입니다.

  • 기존 객체(Object)의 단점을 보완하여 모든 타입을 키로 사용할 수 있고, 성능과 편의성 면에서 장점을 가집니다.

  • set, get, has, delete 등 명확한 메서드를 통해 데이터를 조작하며, for...of 루프나 forEach로 쉽게 순회할 수 있습니다.

자바스크립트 Map 완벽 정복 핸드북 모든 것을 알려드립니다

자바스크립트 개발자라면 데이터를 저장하고 관리하기 위해 객체(Object)를 사용하는 데 익숙할 것입니다. 하지만 객체는 특정 상황에서 몇 가지 한계를 드러내며, 이를 극복하기 위해 ES6(ECMAScript 2015)에서 새로운 데이터 구조인 맵(Map) 이 등장했습니다. 맵은 단순한 키-값 저장소를 넘어, 개발자에게 더 강력하고 유연한 데이터 관리 능력을 제공합니다.

이 핸드북에서는 자바스크립트 맵이 왜 만들어졌는지부터 시작하여 그 구조와 사용법, 그리고 객체와의 차이점, 심화 활용법까지 맵에 대한 모든 것을 깊이 있게 탐구해 보겠습니다. 이 글을 끝까지 읽는다면, 여러분은 맵을 언제 어떻게 사용해야 하는지 명확하게 이해하고, 코드의 품질을 한 단계 끌어올릴 수 있을 것입니다.

1. 맵은 왜 탄생했을까 객체의 한계와 새로운 필요성

자바스크립트의 역사를 살펴보면, 초창기에는 객체를 마치 해시맵(HashMap)처럼 키-값 쌍을 저장하는 용도로 널리 사용했습니다. 이는 매우 편리했지만, 몇 가지 본질적인 문제점을 안고 있었습니다.

1.1. 키 타입의 제약

가장 큰 한계는 객체의 키가 문자열 또는 심볼(Symbol) 타입으로만 제한된다는 점이었습니다. 만약 숫자나 객체를 키로 사용하려 해도, 자바스크립트 엔진은 이를 내부적으로 문자열로 변환해버렸습니다.

JavaScript

const obj = {};
const keyObject1 = { id: 1 };
const keyObject2 = { id: 2 };
 
obj[keyObject1] = 'Value 1';
obj[keyObject2] = 'Value 2';
 
console.log(obj);
// 출력: { '[object Object]': 'Value 2' }

위 예시에서 keyObject1keyObject2는 분명 다른 객체이지만, 키로 사용될 때 모두 '[object Object]'라는 문자열로 변환됩니다. 결국, 두 번째 할당이 첫 번째 할당을 덮어쓰는 문제가 발생합니다. 이러한 현상은 개발자가 의도하지 않은 버그의 원인이 되곤 했습니다.

1.2. 순서 보장의 부재

과거 자바스크립트 명세에서는 객체의 프로퍼티 순서를 보장하지 않았습니다. 즉, 개발자가 데이터를 추가한 순서대로 순회(iterate)할 수 있다는 보장이 없었습니다. 이는 데이터의 입력 순서가 중요한 애플리케이션에서는 치명적인 단점이었습니다. (물론 최신 자바스크립트 명세에서는 일부 순서를 보장하지만, 이는 원래의 설계 목적이 아니었습니다.)

1.3. 내장 프로퍼티와의 충돌 가능성

객체는 Object.prototype에 정의된 constructor, toString과 같은 기본 프로퍼티를 상속받습니다. 만약 개발자가 실수로 이러한 이름들을 키로 사용하면, 예상치 못한 충돌이 발생할 수 있습니다.

JavaScript

const obj = {};
obj['constructor'] = 'My Value';
 
console.log(obj.constructor); // 'My Value'가 아닌 다른 함수가 출력될 수 있음

1.4. 크기 측정의 번거로움

객체에 저장된 키-값 쌍의 개수를 알아내려면 Object.keys(obj).length와 같이 매번 키 배열을 생성하고 그 길이를 계산해야 했습니다. 이는 직관적이지도 않고, 데이터가 많을 경우 성능 저하의 원인이 될 수 있습니다.

이러한 객체의 한계와 불편함을 해결하고, 더 안정적이고 효율적인 키-값 데이터 구조에 대한 필요성이 대두되면서 맵(Map) 이 탄생하게 된 것입니다. 맵은 이러한 문제들을 해결하는 데 초점을 맞춘, 현대 자바스크립트 개발의 필수적인 도구로 자리 잡았습니다.

2. 맵의 구조와 핵심 특징 파헤치기

맵은 키-값 쌍의 컬렉션이라는 점에서는 객체와 유사하지만, 내부 동작 방식과 특징에서 중요한 차이를 보입니다.

2.1. 모든 타입을 허용하는 유연한 키

맵의 가장 강력한 특징은 모든 타입의 값을 키로 사용할 수 있다는 점입니다. 문자열, 숫자, 불리언 같은 원시 타입은 물론, 객체, 배열, 함수까지도 고유한 키로 인정됩니다.

JavaScript

const myMap = new Map();
 
const keyString = 'a string';
const keyNumber = 123;
const keyObject = { id: 1 };
const keyFunc = () => 'hello';
 
myMap.set(keyString, 'Value for string');
myMap.set(keyNumber, 'Value for number');
myMap.set(keyObject, 'Value for object');
myMap.set(keyFunc, 'Value for function');
 
console.log(myMap.get(keyString));  // 'Value for string'
console.log(myMap.get(keyNumber));  // 'Value for number'
console.log(myMap.get(keyObject)); // 'Value for object'
console.log(myMap.get(keyFunc));   // 'Value for function'

이는 객체에서는 불가능했던, 데이터 간의 복잡한 관계를 매핑하고 관리하는 것을 가능하게 합니다. 예를 들어, DOM 요소를 키로 사용하여 관련 메타데이터를 저장하는 등의 고급 활용이 가능해집니다.

2.2. 흔들림 없는 순서 보장

맵은 데이터를 추가한 순서를 정확하게 기억합니다. for...of 루프나 forEach() 메서드를 사용하여 맵을 순회하면, 항상 삽입된 순서대로 키-값 쌍에 접근할 수 있습니다. 이 특징은 데이터의 순서가 중요한 로직을 구현할 때 코드의 예측 가능성과 안정성을 크게 높여줍니다.

2.3. 직관적인 크기 확인

맵은 size라는 프로퍼티를 통해 현재 저장된 요소의 개수를 직접적이고 효율적으로 알 수 있습니다.

JavaScript

const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
myMap.set('c', 3);
 
console.log(myMap.size); // 3

Object.keys().length처럼 번거로운 과정을 거칠 필요 없이, 즉시 컬렉션의 크기를 확인할 수 있어 편리합니다.

2.4. 이터러블 프로토콜 준수

맵은 이터러블(Iterable) 프로토콜을 따릅니다. 이는 for...of, 스프레드 문법(...), 구조 분해 할당 등과 함께 자연스럽게 사용할 수 있음을 의미합니다. 이는 데이터 처리를 훨씬 더 간결하고 우아하게 만들어 줍니다.

JavaScript

const myMap = new Map([
  ['a', 1],
  ['b', 2]
]);
 
// for...of
for (const [key, value] of myMap) {
  console.log(`${key}: ${value}`);
}
 
// 스프레드 문법
const entries = [...myMap];
console.log(entries); // [['a', 1], ['b', 2]]

3. 맵 기본 사용법 A to Z

맵을 실제로 어떻게 생성하고 조작하는지 핵심 메서드들을 통해 알아보겠습니다. 맵의 메서드들은 그 이름만으로도 어떤 동작을 하는지 명확하게 알 수 있도록 설계되었습니다.

3.1. 생성 및 초기화

new Map() 생성자를 사용하여 빈 맵을 만들 수 있습니다. 선택적으로, 키-값 쌍으로 이루어진 배열(또는 다른 이터러블)을 인자로 전달하여 초기화할 수도 있습니다.

JavaScript

// 빈 맵 생성
const map1 = new Map();
 
// 배열로 초기화
const map2 = new Map([
  ['name', 'Alice'],
  ['age', 30],
  [true, 'isStudent']
]);
 
console.log(map2.get('name')); // 'Alice'

3.2. 데이터 추가 및 수정: set(key, value)

set() 메서드는 맵에 새로운 키-값 쌍을 추가하거나, 이미 존재하는 키의 값을 수정합니다. set() 메서드는 맵 객체 자신을 반환하므로, 메서드 체이닝(method chaining)이 가능합니다.

JavaScript

const userMap = new Map();
 
userMap.set('name', 'John')
       .set('email', 'john@example.com')
       .set('isLoggedIn', false);
 
console.log(userMap);
// Map(3) { 'name' => 'John', 'email' => 'john@example.com', 'isLoggedIn' => false }
 
// 값 수정
userMap.set('isLoggedIn', true);
console.log(userMap.get('isLoggedIn')); // true

3.3. 데이터 조회: get(key)

get() 메서드는 주어진 키에 해당하는 값을 반환합니다. 만약 키가 존재하지 않으면 undefined를 반환합니다.

JavaScript

console.log(userMap.get('name')); // 'John'
console.log(userMap.get('password')); // undefined

3.4. 키 존재 여부 확인: has(key)

has() 메서드는 맵에 특정 키가 존재하는지 여부를 확인하여 true 또는 false를 반환합니다.

JavaScript

console.log(userMap.has('email'));   // true
console.log(userMap.has('address')); // false

3.5. 데이터 삭제: delete(key)

delete() 메서드는 특정 키와 그에 해당하는 값을 맵에서 삭제합니다. 삭제에 성공하면 true를, 해당 키가 없어 실패하면 false를 반환합니다.

JavaScript

userMap.delete('isLoggedIn');
console.log(userMap.has('isLoggedIn')); // false

3.6. 모든 데이터 삭제: clear()

clear() 메서드는 맵의 모든 키-값 쌍을 제거하여 빈 맵으로 만듭니다.

JavaScript

userMap.clear();
console.log(userMap.size); // 0

4. 맵 순회하기 다양한 방법

맵은 이터러블이기 때문에 데이터를 순회하는 다양한 방법을 제공합니다.

4.1. for...of 루프 (권장)

가장 일반적이고 가독성이 좋은 방법입니다. 구조 분해 할당을 함께 사용하면 키와 값을 쉽게 얻을 수 있습니다.

JavaScript

const foodMap = new Map([
  ['apple', '🍎'],
  ['banana', '🍌'],
  ['orange', '🍊']
]);
 
for (const [key, value] of foodMap) {
  console.log(`The key is ${key} and the value is ${value}`);
}

4.2. forEach() 메서드

배열의 forEach와 유사하게 콜백 함수를 사용하여 각 요소를 순회할 수 있습니다. 콜백 함수는 (value, key, map) 세 가지 인자를 받습니다. (배열과 순서가 다름에 유의: value가 먼저!)

JavaScript

foodMap.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});

4.3. 키, 값, 엔트리(Entries) 이터레이터

맵은 순회를 위한 세 가지 헬퍼 메서드를 제공합니다.

  • keys(): 맵의 키들로 구성된 이터레이터를 반환합니다.

  • values(): 맵의 값들로 구성된 이터레이터를 반환합니다.

  • entries(): [key, value] 쌍으로 구성된 이터레이터를 반환합니다. (for...of 루프가 내부적으로 사용하는 방식)

JavaScript

// 키 순회
for (const key of foodMap.keys()) {
  console.log(key); // apple, banana, orange
}
 
// 값 순회
for (const value of foodMap.values()) {
  console.log(value); // 🍎, 🍌, 🍊
}
 
// 엔트리 순회 (for...of와 동일)
for (const entry of foodMap.entries()) {
  console.log(entry); // ['apple', '🍎'], ['banana', '🍌'], ['orange', '🍊']
}

5. 맵 vs 객체 언제 무엇을 써야 할까?

맵과 객체는 모두 키-값 데이터를 다루지만, 명확한 사용 사례와 장단점을 가지고 있습니다. 올바른 데이터 구조를 선택하는 것은 코드의 성능과 안정성에 큰 영향을 미칩니다.

특징맵 (Map)객체 (Object)
키 타입모든 타입 (원시, 객체, 함수 등)문자열 또는 심볼
순서 보장보장됨 (삽입 순서)보장되지 않음 (최신 명세에서 일부 보장)
크기 확인size 프로퍼티 (O(1) 시간 복잡도)Object.keys().length (O(n) 시간 복잡도)
성능잦은 추가/삭제 시 일반적으로 더 빠름단순 조회 및 적은 변경 시 빠름
이터레이션이터러블 프로토콜 내장, 순회 용이for...in, Object.keys/values/entries 필요
프로토타입프로토타입 없음, 키 충돌 위험 없음Object.prototype 상속, 충돌 가능성 있음
JSON 변환직접 변환 불가, 배열로 변환 후 가능JSON.stringify로 직접 변환 가능

이런 경우 맵(Map)을 사용하세요 ✅

  1. 키가 문자열이 아니어야 할 때: DOM 요소를 키로 사용하거나, 사용자 정의 객체를 키로 매핑해야 하는 경우 맵이 유일한 선택지입니다.

  2. 데이터의 순서가 중요할 때: 입력된 순서대로 처리해야 하는 데이터(예: 작업 큐)를 다룰 때 맵의 순서 보장 특성은 매우 유용합니다.

  3. 데이터의 추가와 삭제가 빈번하게 일어날 때: 맵은 특히 요소의 추가 및 삭제 작업에서 객체보다 더 나은 성능을 보이는 경향이 있습니다.

  4. 컬렉션의 크기를 자주 확인해야 할 때: size 프로퍼티는 매우 효율적으로 크기를 알려줍니다.

  5. 순수 데이터 저장소로 사용할 때: 프로토타입과의 충돌을 원천적으로 방지하고 싶을 때 맵이 더 안전합니다.

이런 경우 객체(Object)를 사용하세요 ✅

  1. 단순하고 정적인 데이터 구조일 때: 키가 모두 문자열이고, 데이터 구조가 한 번 정해진 후 거의 바뀌지 않는 경우(예: 설정 객체, JSON에서 파싱된 데이터) 객체가 더 가볍고 편리합니다.

  2. JSON과의 상호작용이 필요할 때: 서버와 데이터를 주고받는 등 JSON 형식으로의 변환이 필수적일 때 객체가 훨씬 간단합니다.

  3. 특정 로직을 포함하는 ‘객체’가 필요할 때: 데이터와 함께 메서드(동작)를 캡슐화하는 객체 지향 프로그래밍의 개념을 활용할 때는 당연히 객체를 사용해야 합니다.

핵심 요약: 동적인 데이터를 안전하고 효율적으로 관리하고 싶다면 을, 가볍고 정적인 데이터 덩어리나 JSON 호환성이 중요하다면 객체를 사용하는 것이 좋습니다.

6. 심화 내용 위크맵(WeakMap)과의 관계

맵을 이해했다면, 그 사촌 격인 위크맵(WeakMap) 에 대해서도 알아두는 것이 좋습니다. 위크맵은 맵과 거의 유사하지만 몇 가지 중요한 차이점이 있습니다.

  1. 키는 반드시 객체여야 합니다: 위크맵은 원시 타입 값을 키로 사용할 수 없습니다.

  2. 키에 대한 참조가 ‘약합니다(weak)’: 이것이 가장 핵심적인 차이입니다. 위크맵의 키로 사용된 객체를 참조하는 다른 코드가 없다면, 해당 객체는 가비지 컬렉터(GC)에 의해 메모리에서 제거될 수 있습니다. 이때 위크맵에서도 해당 키-값 쌍이 자동으로 삭제됩니다.

이 ‘약한 참조’ 특성 때문에 위크맵은 메모리 누수를 방지하는 데 매우 유용합니다. 예를 들어, DOM 요소에 추가적인 데이터를 몰래 저장해두고 싶을 때, 해당 DOM 요소가 페이지에서 사라지면 위크맵에 저장된 데이터도 함께 사라져 메모리를 효율적으로 관리할 수 있습니다.

특징맵 (Map)위크맵 (WeakMap)
키 타입모든 타입객체만 가능
키 참조강한 참조 (Strong Reference)약한 참조 (Weak Reference)
가비지 컬렉션키 객체 참조가 남아있어 GC 대상에서 제외다른 참조가 없으면 키 객체 GC 대상 포함
이터레이션가능 (keys, values, forEach 등)불가능 (GC 시점을 예측할 수 없으므로)
size, clear사용 가능사용 불가능

사용 사례: 객체에 대한 부가적인 메타데이터를 저장하되, 해당 객체의 생명주기에 데이터를 종속시키고 싶을 때 위크맵을 사용합니다. (예: DOM 요소 캐싱, 비공개 멤버 변수 흉내 내기)

7. 결론 자바스크립트 데이터 구조의 새로운 지평

자바스크립트 맵은 단순한 객체의 대체재가 아닙니다. 이는 현대 자바스크립트 개발에서 마주치는 다양한 데이터 관리 문제들을 해결하기 위해 설계된 정교하고 강력한 도구입니다.

다양한 타입을 허용하는 유연한 키, 보장된 순서, 효율적인 크기 관리, 명확한 API를 통해 맵은 우리의 코드를 더 예측 가능하고, 안정적이며, 성능 좋게 만들어 줍니다. 객체와 맵의 차이점을 명확히 이해하고 상황에 맞는 적절한 데이터 구조를 선택하는 능력은 모든 자바스크립트 개발자가 갖추어야 할 핵심 역량 중 하나입니다.

이제 여러분은 맵에 대한 충분한 지식을 갖추었습니다. 다음 프로젝트에서는 데이터를 다룰 때 ‘객체를 쓸까, 맵을 쓸까?‘를 한 번 더 고민해 보세요. 그 작은 고민이 여러분의 코드를 더욱 견고하게 만들어 줄 것입니다.

레퍼런스(References)