Published on

Javascript Map 객체

Authors
  • avatar
    Name
    sulmo
    Twitter

JS에서는 해시테이블을 이용하여 최적화된 Map이라는 기본제공 객체가 존재합니다. 일반 Object를 이용하지 않고 Map이 제공되고 있는 이유를 알아봅니다.

Map 객체와 일반 JavaScript 객체(Object)의 주요 차이점

  1. 키(Key) 타입
  • Map: 모든 타입의 값을 키로 사용 가능 (객체, 함수, 원시 타입 등)
  • Object: 문자열과 Symbol만 키로 사용 가능
  1. 크기 확인
  • Map: size 속성으로 쉽게 크기 확인 가능
  • Object: Object.keys(obj).length로 계산해야 함
  1. 순회
  • Map: for...of 루프로 직접 순회 가능
  • Object: Object.keys(), Object.entries() 등을 사용해야 함
  1. 성능
  • Map: 빈번한 추가/삭제 작업에 더 좋은 성능
  • Object: 정적 데이터나 적은 수의 키-값 쌍에 적합
  1. 기본 키
  • Map: 기본적으로 제공되는 키가 없음
  • Object: toString, constructor 등의 기본 프로토타입 속성이 존재
기본적으로 제공되는 키가 없다는게 무슨말인가?

Object의 경우, 모든 객체는 기본적으로 Object.prototype으로부터 상속받은 여러 메서드와 속성들을 가지고 있습니다. 예를 들자면:

prototype_test.js
// Object 테스트
const obj = {};
console.log('Object의 기본 키들:');
console.log('toString' in obj);  // true
console.log('constructor' in obj);  // true
console.log('hasOwnProperty' in obj);  // true
console.log('valueOf' in obj);  // true

// Map 테스트
const map = new Map();
console.log('\nMap의 기본 키들:');
console.log('toString' in map);  // false
console.log('constructor' in map);  // false
console.log('hasOwnProperty' in map);  // false
console.log('valueOf' in map);  // false

// Map의 실제 메서드들
console.log('\nMap의 실제 메서드들:');
console.log(map.set);  // function
console.log(map.get);  // function
console.log(map.has);  // function
console.log(map.delete);  // function
  1. Object의 경우:
  • toString, constructor, hasOwnProperty, valueOf 등의 기본 메서드들이 프로토타입 체인을 통해 상속됨
  • 이러한 메서드들은 객체의 키로 사용될 수 있음
  • 예: obj.toString = "something"과 같이 기본 메서드를 덮어쓸 수 있음
  1. Map의 경우:
  • 기본적으로 제공되는 키가 없음
  • set, get, has, delete 등의 메서드는 Map 객체의 메서드일 뿐, 키로 사용될 수 없음
  • Map의 키는 오직 사용자가 명시적으로 추가한 것만 존재

위 특성으로 아래와 같은 이점을 얻습니다.

  1. Object를 사용할 때는 기본 메서드 이름과 충돌할 수 있음
  2. Map을 사용하면 이러한 충돌 위험이 없음
  3. Map은 순수하게 사용자가 추가한 키-값 쌍만 관리
const obj = {}
obj.toString = '이것은 문자열' // 기본 메서드를 덮어씀
console.log(obj.toString()) // 에러 발생!

const map = new Map()
map.set('toString', '이것은 문자열') // 안전하게 키로 사용 가능
console.log(map.get('toString')) // "이것은 문자열" 출력

이러한 특성 때문에 Map은 더 예측 가능하고 안전한 키-값 저장소로 사용될 수 있습니다.

Map의 성능 확인

맵은 순회 시, 성능을 발휘합니다.

  • Map은 해시 테이블을 사용하여 O(1) 시간에 접근
  • Object는 프로토타입 체인을 검색해야 하므로 추가 오버헤드 발생
  • Map은 키-값 쌍을 더 효율적으로 저장
  1. 검색 성능
  • Map: O(1) 시간 복잡도로 검색 가능
  • Object: 대부분의 경우 O(1)이지만, 많은 키가 있을 경우 해시 충돌로 인해 성능이 저하될 수 있음
  1. 메모리 사용
  • Map: 더 많은 메모리를 사용 (추가적인 메타데이터 저장)
  • Object: 상대적으로 적은 메모리 사용
  1. 삽입/삭제 성능
  • Map: 삽입/삭제 작업이 매우 빠름 (O(1))
  • Object: 삽입/삭제 시 프로토타입 체인 검색이 필요해 상대적으로 느림

실제 성능 테스트

performance_test.js
const performance = require('perf_hooks').performance;

// 테스트 데이터 생성
const testSize = 1000000;
const testData = Array.from({ length: testSize }, (_, i) => `key${i}`);

// Map 테스트
console.log('Map 성능 테스트:');
const mapStart = performance.now();
const map = new Map();
for (let i = 0; i < testSize; i++) {
    map.set(testData[i], i);
}
const mapInsertTime = performance.now() - mapStart;

const mapSearchStart = performance.now();
for (let i = 0; i < testSize; i++) {
    map.has(testData[i]);
}
const mapSearchTime = performance.now() - mapSearchStart;

// Object 테스트
console.log('Object 성능 테스트:');
const objStart = performance.now();
const obj = {};
for (let i = 0; i < testSize; i++) {
    obj[testData[i]] = i;
}
const objInsertTime = performance.now() - objStart;

const objSearchStart = performance.now();
for (let i = 0; i < testSize; i++) {
    testData[i] in obj;
}
const objSearchTime = performance.now() - objSearchStart;

// 결과 출력
console.log(`Map 삽입 시간: ${mapInsertTime.toFixed(2)}ms`);
console.log(`Map 검색 시간: ${mapSearchTime.toFixed(2)}ms`);
console.log(`Object 삽입 시간: ${objInsertTime.toFixed(2)}ms`);
console.log(`Object 검색 시간: ${objSearchTime.toFixed(2)}ms`);

테스트 결과를 분석

  1. 삽입 성능
  • Map: 500.86ms
  • Object: 728.35ms
  • Map이 약 1.5배 더 빠름
  1. 검색 성능
  • Map: 11.15ms
  • Object: 77.84ms
  • Map이 약 7배 더 빠름

사용 시 고려사항

  1. 작은 데이터셋(수천 개 이하)의 경우: Object 사용이 메모리 효율적
  2. 대규모 데이터셋이나 빈번한 삽입/삭제: Map 사용이 성능상 유리
  3. 키가 문자열만 필요한 경우: Object가 더 간단하고 직관적

현재 코드처럼 문자열 검색이 주 목적이라면, Map을 사용하는 것이 성능상 더 유리합니다.