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를 구분해서 사용하는 게 코드 의도를 명확히 전달한다. 팀 컨벤션에 이 내용을 추가했다.