Array.prototype.reduce()를 제대로 이해하고 사용하기
문제 상황
동료가 작성한 코드 리뷰 중 모든 배열 처리를 reduce로 구현한 코드를 발견했다. map이나 filter로 충분한 경우에도 reduce를 사용해서 가독성이 떨어졌다.
// 기존 코드
const activeUsers = users.reduce((acc, user) => {
if (user.active) {
acc.push(user);
}
return acc;
}, []);
이건 그냥 filter를 쓰면 되는 상황이었다.
reduce의 적절한 사용
reduce는 배열을 단일 값으로 축약할 때 사용하는 게 맞다. 객체로 그룹화하거나, 합계를 구하거나, 중복을 제거할 때 유용하다.
// 1. 카테고리별 그룹화
const grouped = products.reduce((acc, product) => {
const category = product.category;
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(product);
return acc;
}, {});
// 2. 합계 계산
const total = orders.reduce((sum, order) => sum + order.price, 0);
// 3. 배열을 객체로 변환
const userMap = users.reduce((map, user) => {
map[user.id] = user;
return map;
}, {});
성능 고려사항
reduce는 강력하지만 체이닝이 필요한 경우 오히려 비효율적일 수 있다. filter + map을 한 번의 reduce로 합치면 루프를 줄일 수 있지만, 코드 복잡도가 올라간다.
// 가독성 우선
const result = users
.filter(u => u.active)
.map(u => u.name);
// 성능 우선 (데이터가 많을 때)
const result = users.reduce((acc, u) => {
if (u.active) acc.push(u.name);
return acc;
}, []);
대부분의 경우 가독성이 더 중요하다. 성능 이슈가 실제로 측정되기 전까진 명확한 코드가 낫다.
결론
reduce는 만능이 아니다. 용도에 맞게 map, filter, reduce를 구분해서 사용하는 게 코드 의도를 명확히 전달한다. 팀 컨벤션에 이 내용을 추가했다.