Object.groupBy() — 배열 그룹화

ES2024 Object.groupBy()와 Map.groupBy()로 배열을 키 기준으로 분류하는 방법, reduce를 사용하던 기존 방식과의 비교, 실전 패턴을 정리합니다.

· 4 min read · PALDYN Team

지난 글에서 structuredClone()을 살펴봤습니다. 이번에는 ES2024에 추가된 **Object.groupBy()**와 **Map.groupBy()**를 다룹니다. 배열을 특정 기준으로 분류하는 작업을 위해 매번 reduce()를 직접 구현하던 번거로움을 해결하는 내장 메서드입니다.

배열 그룹화란?

배열 요소를 어떤 기준(카테고리, 상태, 범위 등)에 따라 분류해 각 그룹별 배열로 묶는 작업입니다.

// 기존 방식 — reduce로 직접 구현
const grouped = items.reduce((acc, item) => {
  const key = item.status;
  (acc[key] ??= []).push(item);
  return acc;
}, {});

매번 이 패턴을 반복하는 대신, Object.groupBy()가 같은 작업을 선언적으로 처리합니다.

Object.groupBy()

Object.groupBy 그룹화 개념

Object.groupBy(iterable, callbackFn) 형식으로 사용합니다. 콜백이 반환하는 문자열 키를 기준으로 요소를 분류하고, null 프로토타입을 가진 객체를 반환합니다.

const products = [
  { name: '노트북', category: '전자제품', price: 1_200_000 },
  { name: '키보드', category: '전자제품', price: 80_000   },
  { name: '책상',   category: '가구',    price: 350_000   },
  { name: '의자',   category: '가구',    price: 200_000   },
  { name: '마우스', category: '전자제품', price: 40_000   },
];

const byCategory = Object.groupBy(products, p => p.category);

byCategory['전자제품'];
// [노트북, 키보드, 마우스]
byCategory['가구'];
// [책상, 의자]

Map.groupBy()

Object.groupBy vs Map.groupBy

키가 문자열이 아닌 임의 타입(객체, 숫자 등)이어야 할 때는 Map.groupBy()를 사용합니다.

const EXPENSIVE = { label: '고가', min: 500_000 };
const MIDRANGE  = { label: '중가', min: 100_000 };
const BUDGET    = { label: '저가', min: 0        };

const byPriceRange = Map.groupBy(products, p => {
  if (p.price >= 500_000) return EXPENSIVE;
  if (p.price >= 100_000) return MIDRANGE;
  return BUDGET;
});

byPriceRange.get(EXPENSIVE); // [노트북]
byPriceRange.get(MIDRANGE);  // [책상]
byPriceRange.get(BUDGET);    // [키보드, 의자, 마우스]

null 프로토타입 반환의 의미

Object.groupBy()가 반환하는 객체는 Object.create(null)로 만들어진 null 프로토타입 객체입니다.

const g = Object.groupBy(['a', 'b', 'a'], x => x);
Object.getPrototypeOf(g); // null

// 주의: hasOwnProperty 같은 메서드 없음
g.hasOwnProperty; // undefined
// Object.hasOwn()을 사용해야 함
Object.hasOwn(g, 'a'); // true

다양한 그룹화 기준

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 홀수/짝수
const byParity = Object.groupBy(numbers,
  n => n % 2 === 0 ? 'even' : 'odd'
);
// { even: [2,4,6,8,10], odd: [1,3,5,7,9] }

// 범위별
const byRange = Object.groupBy(numbers, n => {
  if (n <= 3)  return 'low';
  if (n <= 7)  return 'mid';
  return 'high';
});
// { low: [1,2,3], mid: [4,5,6,7], high: [8,9,10] }

이터러블 지원

두 메서드는 배열뿐만 아니라 Set, Map, 제너레이터 등 모든 이터러블을 첫 번째 인자로 받습니다.

const set = new Set(['apple', 'banana', 'avocado', 'blueberry']);

const byFirstLetter = Object.groupBy(set, s => s[0]);
// { a: ['apple', 'avocado'], b: ['banana', 'blueberry'] }

지원 환경

Chrome 117+, Firefox 119+, Safari 17.4+, Node.js 21+에서 지원됩니다. 이전 환경에서는 core-js의 폴리필 또는 Array.prototype.reduce()로 대체할 수 있습니다.


지난 글: structuredClone()

다음 글: 배열 불변 메서드 (toSorted, toReversed, with)


읽어주셔서 감사합니다. 😊