본문 바로가기

자바스크립트

[JS] Map 에 대한 정리

반응형

Map

키와 값을 매핑하는 자료 구조로, ECMAScript 6 (ES6)부터 도입되었다. Map을 사용하면 데이터를 효과적으로 저장하고 검색할 수 있다.  다음과 같이 new 키워드를 사용해지만 생성할 수 있다. 만일 new 를 사용하지 않으면 typeerror 가 뜬다.

const examMap = new Map();

 

Map 의 특징

- 키와 값을 매핑하는 자료 구조로, 키와 값 모두 다양한 데이터 유형을 가질 수 있다.

- 키는 중복될 수 없다. 만일 중복된 키를 넣으려고 하면 마지막에 추가한 키의 값으로 업데이트 된다.
- 원소를 추가한 순서대로 순서를 보존한다.

- Map은 반복 가능한(iterable) 객체로, for...of 루프 등을 사용하여 순회할 수 있다.

이 때 오해하면 안 되는 점은 이터러블 객체이지 흔히 [ ] 와 같은 배열이 아니다. 그러므로 인덱스로 접근할 수 없다. 

 

Map의 메서드

- 값 추가하기 : Map에 값을 추가하려면 set() 메서드를 사용한다. set() 메서드는 키와 값을 받아서 매핑한다.

examMap.set('키1', '값1');
examMap.set('키2', '값2');

 

- 값 얻기: get() 메서드를 사용하여 특정 키에 대한 값을 얻을 수 있다.

const value = examMap.get('키1');
console.log(value); // 출력: '값1'

 

- 값 삭제하기: delete() 메서드를 사용하여 특정 키와 해당 값을 삭제할 수 있다.

examMap.delete('키1');


- 값 존재 여부 확인하기: has() 메서드를 사용하여 특정 키의 존재 여부를 확인할 수 있다.

const keyExists = examMap.has('키1');
console.log(keyExists); // 출력: false

 

- 크기 확인하기: size 프로퍼티를 사용하여 Map의 크기를 확인할 수 있다.

const mapSize = examMap.size;
console.log(mapSize); // 출력: 1 (키2와 해당 값만 남아 있음)


- 모든 키와 값 순회하기: for...of 루프를 사용하여 Map의 모든 키와 값을 순회할 수 있다.

for (const [key, value] of myMap) {
  console.log(`${key}: ${value}`);
}

 

위에서 제시된 메소드 외에도 foreach, entries, keys 등 다양한 메소드를 사용할 수 있다. 


요약)
간략하게 정리해보면, Map 은 순서를 보장하며, 이터러블 이므로 배열 메소드로 순회가 가능하다. 키는 중복이 불가능하므로, 중복된 키를 넣으려고 하면, 마지막에 추가한 키와 값으로 대체된다. 

 

Map의 장점

Map 은 일반적인 객체 생성 방식에 비해 성능상 이점이 있다. 특히, 키-값에 대한 빈번한 수정, 접근 등으로 인해 데이터의 규모가 커지는 상황에서는 그 차이는 더 벌어진다. 이는 내부적으로 Map의 경우에는 이러한 작업에 대한 최적화가 되어 있기 때문이며, 일반적인 객체 생성 방식에서는 키-값 쌍의 빈번한 추가 및 제거에 최적화되지 않는다.

 

Map 을 사용 시에는 보안적으로 일반적인 Object 프로토타입을 상속받아 생성된 객체 보다 우수하다. 왜냐 하면, Map의 경우에는 키로 사용될 수 있는 데이터 타입의 유형에 제한이 없기 때문에, 공격자로 하여금 키를 예측할 수 없게 하기 때문에 데이터 조작을 어렵게 만들기 때문이다.

 

또한, __proto__ 와 같이 프로토타입에 접근하는 것 자체를 제한하기 때문에, 공격자가 프로토타입의 상속을 조작하여 악의적인 코드 실행을 미연에 방지할 수 있다. 

 

무엇보다 Map의 경우에는 저장된 객체에 접근하기 위해서 활용 가능한 메소드가 정해져 있다. 앞서 언급한 프로토타입의 변형 등으로 인한 잠재적인 보안 위험을 예방할 수 있다는 점에서 일반적인 객체 생성 방식에 비해 보안상 이점이 크다.

 

Map의 단점

일반적인 객체 방식에 비해서 개인적으로 큰 단점은 데이터의 직렬화와 역직렬화에 대한 지원이 없다는 점이다. 객체 리터럴 등으로 생성된 객체는 JSON.parse(), JSON.stringify() 를 통해 쉽게 데이터를 직렬화하고 역직렬화 가능하지만, Map은 그러한 메소드를 지원하지 않는다.

 

단, 이러한 문제를 개선하기 위한 방식으로 랩퍼 함수를 생성하여 처리하는 방법이 있는데,

replacer 와 reviver 라는 랩퍼 함수를 생성하고, stringify()의 두번째 인자에는 replacer 함수를 , parse()의 두번째 인자에는 reviver 함수를 전달하여 기존의 stringify와 parse 함수가 첫 번째 인자로 전달받은 값을 처리하는 프로세스를 대신 처리하도록 하여 수행할 수 있다.

function replacer(key, value) {
  if(value instanceof Map) {
    return {
      dataType: 'Map',
      value: Array.from(value.entries()), // or with spread: value: [...value]
    };
  } else {
    return value;
  }
}
function reviver(key, value) {
  if(typeof value === 'object' && value !== null) {
    if (value.dataType === 'Map') {
      return new Map(value.value);
    }
  }
  return value;
}

// 사용법 :

const originalValue = new Map([['a', 1]]);
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, newValue);
// 배열, 객체, 맵의 조합을 통한 깊은 중첩

const originalValue = [
  new Map([['a', {
    b: {
      c: new Map([['d', 'text']])
    }
  }]])
];
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, newValue);

위 코드만 봐도 알겠지만, Map의 경우에는 직접 직렬화 및 역직렬화를 위한 대체 프로세스를 지정하는 함수를 인자로 넘겨 줘야지만 가능하기에 코드가 매우 더러워질 수 있다는 단점을 불가피해 보인다.

 


참고자료

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Map/Map

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

반응형