개발 기술/사소하지만 놓치기 쉬운 개발 지식

키 타입을 보장할 수 있는 Map 객체(주의! array.map 아님)

by GicoMomg 2023. 10. 15.

1. 키 타입이 왜 String이 되지?

  • JS에서 Object.keys()로 객체의 키값을 배열로 반환할 수 있다.
const object = {
  a: '1',
  b: 2,
  c: 3,
};

console.log(Object.keys(object)); // ['a', 'b', 'c']

💡 그런데 만약 키 타입이 Number인 객체의 키값을 Object.keys()로 추출하면 어떻게 될까?
Number 타입의 배열이 되나?

  • 결과는 String 타입의 배열이 된다! 왜일까?
  • 그 이유는 Object.keys()가 String 타입의 배열을 리턴하는 함수이기 때문이다.(MDN 문서 참고)
const object = { 1: '일', 2: '이', 3: '삼', 4: '사' };

console.log(Object.keys(object)); // ["1", "2", "3", "4"]

만약, Number 타입의 객체 키값을 추출할 때 타입을 보장하고 싶다면?

  • 데이터를 Map 으로 선언한 후, keys()를 사용해 Number 타입의 키를 추출할 수 있다!
const map = new Map([[1, '일'], [2, '이'], [3, '삼'], [4, '사']]);

console.log(Array.from(map.keys()));  // [1, 2, 3, 4]

  • 그럼 이 Map 데이터는 어떤 특성을 가지고 있길래 Object와 달리 키 타입도 보장해주는 걸까?
  • 이번 시간에는 Map에 대해 간단히 알아보고, Map, Object 의 차이를 알아보았다!



2. Map 객체는 뭐죠?

1) Map 객체에 대해 알아보자

  • Map 객체는 키:값 쌍을 저장하는 데이터 형태이다.
  • new Map()을 사용해 선언할 수 있으며, 형태는 Object와 유사하다.
const map = new Map();  // Map {}

(1) 메서드 훑어보기

  • 제공하는 메서드는 여러가지가 있는데, 코드로 예시를 살펴보았다.
메서드 설명
[1] map.set(키, 값) - 키에 해당하는 값 추가
- 중복 키가 있을 때 값을 덮어씀                                                   
[2] map.set(키, 값) - 키에 해당하는 값 제거
[3] map.clear() - 모든 요소 제거
[4] map.forEach(값, 키, map) - 데이터를 순회할 수 있음
const map = new Map([['key1', 'value1'], ['key2', 'value2']]);

// [1] 값 추가
map.set('key1', 'value111');
console.log(map);  // Map(2) {key1: "value111", key3: "value3"}

// [2] 값 제거
map.delete('key1'); 
console.log(map);  // Map(2) {key2: "value2", key3: "value3"}

// [3] 모든 값 제거
map.clear();  
console.log(map);  // Map {}

메서드 설명
[5] map.keys() - 요소의 키를 반환                                                   
[6] map.values() - 요소의 값을 반환
[7] map.get(키) - key에 해당하는 값을 반환
[8] map.has(키) - key가 존재하면 true
- 존재하지 않으면 false를 반환
[9] map.size - 요소의 개수를 반환
const map = new Map([['key1', 'value1'], ['key2', 'value2']]);

// [5] 요소의 키에 접근
console.log(Array.from(map.keys()))   // ["key1", "key2"]

// [6] 요소의 값에 접근
console.log(Array.from(map.values())) // ["value1", "value2"]

// [7] 특정 값에 접근
console.log(map.get('key1'));         // value1

// [8] 키가 있는지 확인
console.log(map.has('key2'));         // true

// [9] 요소의 개수를 반환
console.log(map.size);                // 2

📌 Map의 메서드를 보니, Object처럼 키 값 형태로 데이터가 선언되고
키로 데이터에 접근할 수 있는데 Object와 어떤 차이가 있는 걸까? 차이점에 대해 알아보자!


2) Map과 Object의 차이

(1) Map은 키 타입이 자유롭다

  • Map은 키에 모든 데이터 타입이 가능하다!
  • 그래서 키 타입이 Number도 가능하다.
const map = new Map();

map.set(11, '숫자 11');
map.set(22, '숫자 22');

console.log(map);            // Map {11: "숫자 11", 22: "숫자 22"} 

console.log(map.get(11));    // 숫자 11
console.log(map.get("11"));  // undefined

  • 그에 반해 Object는 키 타입이 문자열이나 Symbol만 가능하다.
  • 만약 데이터의 키 타입을 Number로 지정해도 문자열로 형변환된다.
const object = {11: '숫자 11', 22: '숫자 22'};

console.log(object);       // {"11": "숫자 11", "22": "숫자 22"}

  • 다행히도 Object는 Number 타입의 키를 넘기나 String 타입의 키를 넘기나 값에 접근할 수 있다.
console.log(object[11]);   // 숫자 11
console.log(object["11"]); // 숫자 11

(2) Map은 추가한 순서로 데이터가 구성된다

  • Map은 데이터를 추가한 순서대로 데이터가 정렬된다.
  • 그래서 순서가 보장되어야 하는 경우에 유용하다.
const map = new Map();

map.set(11, '숫자 11');
map.set(22, '숫자 22');
map.set(0, '숫자 0');
map.set(999, '숫자 999');;

console.log(map);  

// Map {11: "숫자 11", 22: "숫자 22", 0: "숫자 0", 999: "숫자 999"} 

  • 그러나 Object는 데이터를 추가한 순서대로 정렬되지 않는다.
const object = {11: '숫자 11', 22: '숫자 22'};

object[0] = '숫자 0';
object[999] = '숫자 999';

console.log(object);  

// {0: "숫자 0", 11: "숫자 11", 22: "숫자 22", 999: "숫자 999"}

(3) Map은 그 자체로 iterable 하다

  • Map은 그 자체로 순회가능(iterable)한 데이터이므로, for ~ of, forEach로 순회할 수 있다.
const map = new Map();

map.set(11, '숫자 11');
map.set(22, '숫자 22');

for (const value of map) {  
  console.log(value);
}
// [11, "숫자 11"]
// [22, "숫자 22"]

map.forEach((value) => {
  console.log(value);
})
// 숫자 11
// 숫자 22

  • Object는 그 자체로 순회가능한 데이터가 아니므로 반복문 사용시 not iterable 에러가 발생한다.
const object = {11: '숫자 11', 22: '숫자 22'};

// TypeError: object is not iterable
for (const value of object) { 
   console.log(value);
 }

  • 그래서 Object는 별도의 가공(ex. Object.keys)를 거쳐야 반복문을 사용할 수 있다.
const object = {11: '숫자 11', 22: '숫자 22'};

for (const key of Object.keys(object)) { 
  console.log(object[key]);
}
// 숫자 11
// 숫자 22

(4) Map은 값 제거, 추가에 대한 성능이 ‘조금 더’ 좋다

  • MDN 공식문서에 따르면, 키 값 쌍의 빈번한 추가를 하거나 제거를 할 때 MapObject보다 성능이 좀 더 좋다고 한다.
  • 만약 값의 추가, 제거가 빈번하다면 Map을 한 번 고려해봐도 좋을 듯 하다.



3. 마치며

  • 이번 시간에는 객체의 키 타입을 보장해주는 Map 객체에 대해 보았다.
  • MapObject와 달리, 키를 어떤 타입으로도 선언할 수 있으며, 추가한 순서로 데이터가 구성되고, 값의 추가와 제거에 대한 성능이 좋다는 특징이 있었다.
  • 또한 Map은 그 자체로 iterable하므로 반복문을 사용하여 데이터를 순회할 수 있다.
  • 암묵적으로 Object로 데이터를 구성하기 보다는, 상황에 따라 Map을 한 번 사용해보면 어떨까?





+) 참고자료



반응형

댓글