filter로 반복 조회하면 호출할 때마다 배열 전체를 순회해 비용이 O(n * k)로 늘어납니다.groupBy 유틸리티를 만들 수 있고, 대량 데이터라면 객체를 직접 변경(mutable)하는 방식이 성능상 유리합니다.프론트엔드에서는 서버로 받은 리스트 데이터를 카테고리나 날짜 같은 기준으로 분류해 화면에 보여줘야 할 때가 많습니다. 이때 흔히 쓰는 방식이 Array.prototype.filter()입니다.
하지만 filter는 호출될 때마다 배열 전체를 순회합니다. 여러 카테고리를 처리하려고 filter를 반복해서 쓰면, 배열이 커질수록 비용이 O(n * k)(n: 배열 크기, k: 카테고리 수)로 선형 증가합니다. 특정 카테고리 데이터를 찾으려고 매번 전체 배열을 뒤지는 셈이라 비효율적입니다.
가장 좋은 방법은 데이터를 미리 그룹화해두는 것입니다. 배열을 단 한 번만 순회해 객체(Object)나 맵(Map) 형태로 재구성해두면, 이후에는 키로 데이터에 직접 접근할 수 있어 접근 속도가 O(1)로 향상됩니다.
filter(category === 'X') 실행 -> 전체 순회.reduce 실행 -> 결과 객체 생성 -> result['X']로 즉시 접근.핵심은 배열을 순회하며 각 아이템의 특정 속성을 키로 사용하는 '해시 맵' 형태의 객체를 만드는 것입니다.
TypeScript 제네릭을 활용해 재사용성이 높고 타입 안정성이 보장되는 groupBy 유틸리티 함수를 구현해 보겠습니다.
const DUMMY = [ { category: "food", name: "짜장면" }, { category: "electronic", name: "TV" }, { category: "food", name: "떡볶이" }, { category: "clothes", name: "tshirt" }, ]; /** * 배열을 특정 키를 기준으로 그룹화하는 유틸리티 함수 */ const groupBy = <T extends Record<string, any>>( array: T[], keyForGrouping: keyof T ): Record<string, T[]> => { return array.reduce<Record<string, T[]>>((group, item) => { const valueForGrouping = item[keyForGrouping]; // 그룹이 존재하지 않으면 빈 배열로 초기화 후 아이템 추가 return { ...group, [valueForGrouping]: group[valueForGrouping] ? [...group[valueForGrouping], item] : [item] }; }, {}); }; // 사용 예시 const groupedData = groupBy(DUMMY, 'category'); console.log(groupedData.food); // [{ category: "food", name: "짜장면" }, ...]
위 구현은 reduce 내부에서 ...group(스프레드 연산자)을 사용해 매 순회마다 새 객체를 만듭니다. 대량의 데이터를 처리하면 가비지 컬렉션 부담이 커질 수 있습니다. 실무에서는 다음처럼 객체를 직접 변경(mutable)하는 방식이 성능상 유리합니다.
const groupByOptimized = <T extends Record<string, any>>( array: T[], keyForGrouping: keyof T ): Record<string, T[]> => { return array.reduce<Record<string, T[]>>((group, item) => { const key = String(item[keyForGrouping]); if (!group[key]) { group[key] = []; } group[key].push(item); return group; }, {}); };
성과는 다음과 같습니다.
다만 다음 트레이드오프가 있습니다.
TypeScript의 타입 시스템과 결합한 groupBy 함수는 데이터 중심 웹 애플리케이션에서 유용한 도구입니다. 최근 JavaScript 표준(ES2024)에는 Object.groupBy()와 Map.groupBy()가 추가되었으니, 최신 환경이라면 이를 활용하는 것도 좋은 방법입니다.
참고 자료: