JavaScript 개발을 하다 보면 배열 데이터를 다루는 경우가 빈번하게 발생합니다. 이때, 배열의 요소들을 순회하며 특정 작업을 수행하고, 최종적으로 하나의 결과값을 도출해야 하는 경우가 많습니다. JavaScript는 이러한 작업을 효율적으로 처리하기 위해 reduce()
메서드를 제공합니다.
reduce()
메서드는 배열을 순회하며 누적 값을 계산하고, 최종적으로 하나의 결과값을 반환하는 강력한(?) 도구입니다. 단순한 합계 계산부터 복잡한 데이터 변환까지 다양한 상황에서 유용하게 활용될 수 있습니다.
1. 🧰 reduce
메서드 기본 syntax 및 기본 동작 원리
reduce()
메서드는 배열의 각 요소에 대해 지정된 콜백 함수를 실행하고, 이전 결과와 현재 요소를 기반으로 누적 값을 계산합니다. 기본 syntax는 다음과 같습니다.
array.reduce(callback(accumulator, currentValue, currentIndex, array), initialValue);
각 부분에 대한 설명은 다음과 같습니다.
array
:reduce()
메서드를 호출하는 배열입니다.callback
: 배열의 각 요소에 대해 실행될 함수입니다. 이 함수는 네 개의 인자를 받습니다.accumulator
: 누적 값을 저장하는 변수입니다. 콜백 함수의 반환값은 다음 반복에서accumulator
로 전달됩니다.currentValue
: 현재 처리 중인 배열 요소입니다.currentIndex
: 현재 처리 중인 배열 요소의 인덱스입니다. (0부터 시작)array
:reduce
메서드가 호출된 배열입니다.
initialValue
(optional): 초기값입니다. 초기값을 설정하지 않으면 배열의 첫 번째 요소가 초기값으로 사용되고, 두 번째 요소부터 반복이 시작됩니다. 초기값을 설정하면 첫 번째 요소부터 반복이 시작됩니다.
⚙️ 동작 원리
reduce()
메서드는 콜백 함수를 배열의 각 요소에 대해 순차적으로 실행합니다.- 콜백 함수는 각 요소에 대해 누적 값을 계산하고 반환합니다.
- 첫 번째 반복에서
initialValue
가 제공되면, 콜백 함수는initialValue
와 배열의 첫 번째 요소를 사용하여 누적 값을 계산합니다.initialValue
가 제공되지 않으면, 콜백 함수는 배열의 첫 번째 요소와 두 번째 요소를 사용하여 누적 값을 계산합니다. - 두 번째 반복부터는 이전 콜백 함수의 반환값(누적 값)과 다음 배열 요소를 사용하여 누적 값을 계산합니다.
- 모든 배열 요소에 대한 반복이 완료되면,
reduce()
메서드는 마지막 콜백 함수의 반환값(최종 누적 값)을 반환합니다.
2. 💡 reduce
활용 예시: 다양한 연산 및 알고리즘 구현
2.1. ➕ 배열 요소의 합 계산
reduce
메서드를 사용하여 배열 요소의 합을 간단하게 계산할 수 있습니다.
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 출력: 15
- 위 코드에서
reduce
메서드는 콜백 함수(acc, cur) => acc + cur
와 초기값0
을 인자로 받습니다. - 콜백 함수는
acc
(누적값) 와cur
(현재 값) 을 더한 값을 반환하고, 이 값은 다음 반복에서acc
에 저장됩니다. - 초기값
0
은 첫 번째 반복에서acc
에 할당됩니다.
2.2. 📊 배열 요소의 평균 계산
reduce
메서드를 사용하여 배열 요소의 평균을 계산할 수 있습니다.
const numbers = [1, 2, 3, 4, 5];
const average = numbers.reduce((acc, cur, index, arr) => {
acc += cur;
if (index === arr.length - 1) {
return acc / arr.length;
} else {
return acc;
}
}, 0);
console.log(average); // 출력: 3
- 위 코드에서는
reduce
메서드를 사용하여 배열 요소의 합을 구하고, 마지막 반복에서 배열의 길이로 나누어 평균을 계산합니다.
2.3. 🌟 배열에서 최대값 찾기
reduce
메서드를 사용하여 배열에서 최대값을 찾을 수 있습니다.
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
const max = numbers.reduce((acc, cur) => acc > cur ? acc : cur, numbers[0]);
console.log(max); // 출력: 9
- 위 코드에서는
reduce
메서드를 사용하여 누적값acc
와 현재 값cur
을 비교하여 더 큰 값을 누적값으로 유지합니다.
2.4. 🆎 문자열 내 각 문자의 출현 빈도 계산
reduce
메서드를 사용하여 문자열 내 각 문자의 출현 빈도를 계산할 수 있습니다.
const str = "hello world";
const charCount = str.split('').reduce((acc, cur) => {
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
console.log(charCount); // 출력: { h: 1, e: 1, l: 3, o: 2, ' ': 1, w: 1, r: 1, d: 1 }
- 위 코드에서는
reduce
메서드를 사용하여 빈 객체를 초기값으로 설정하고, 문자열을 순회하며 각 문자를 key로, 해당 문자의 출현 빈도를 value로 갖는 객체를 생성합니다.
2.5. 🔢 팩토리얼 계산
팩토리얼이란?
팩토리얼은 1부터 특정 양의 정수까지 모든 정수를 곱한 결과를 의미합니다. 예를 들어, 5! (5 팩토리얼)은 1 * 2 * 3 * 4 * 5 = 120입니다. 팩토리얼은 확률, 조합, 순열 등 다양한 수학적 계산에 사용됩니다.
reduce
메서드를 사용하여 팩토리얼을 계산할 수 있습니다.
const factorial = (n) => {
return Array(n).fill(0).map((_, i) => i + 1).reduce((acc, cur) => acc * cur, 1);
}
console.log(factorial(5)); // 출력: 120
- 위 코드에서는
reduce
메서드를 사용하여 1부터 n까지의 숫자를 곱하여 팩토리얼을 계산합니다.
2.6. 🌀 피보나치 수열 계산
피보나치 수열이란?
피보나치 수열은 앞의 두 수를 더해서 다음 수를 만들어내는 수열입니다. 0과 1로 시작하며, 이후의 각 숫자는 바로 앞의 두 숫자의 합입니다. 예를 들어, 피보나치 수열의 처음 10개 항은 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 입니다. 피보나치 수열은 자연界의 다양한 패턴에서 발견되며, 컴퓨터 과학, 금융, 그리고 예술 분야에서도 널리 활용됩니다.
reduce
메서드를 사용하여 피보나치 수열을 계산할 수 있습니다.
const fibonacci = (n) => {
return Array(n).fill(0).reduce((acc, _, i) => {
if (i < 2) return [...acc, i];
return [...acc, acc[i - 1] + acc[i - 2]];
}, []);
};
console.log(fibonacci(10)); // 출력: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
- 위 코드에서는
reduce
메서드를 사용하여 피보나치 수열의 각 항을 계산하여 배열에 추가합니다.
3. 🚀 reduce
활용 예시: 복잡한 로직 구현
reduce
메서드는 복잡한 로직을 간결하게 구현하는 데에도 유용하게 활용될 수 있습니다. 예를 들어, 특정 조건에 따라 객체 배열을 그룹화하거나, 배열을 기반으로 트리 구조를 생성하는 등의 작업을 reduce
메서드를 사용하여 효율적으로 처리할 수 있습니다.
3.1. 📊 객체 배열 그룹화
const products = [
{ name: '사과', category: '과일', price: 1000 },
{ name: '바나나', category: '과일', price: 500 },
{ name: '당근', category: '채소', price: 700 },
{ name: '상추', category: '채소', price: 300 },
];
const groupedProducts = products.reduce((acc, cur) => {
const category = cur.category;
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(cur);
return acc;
}, {});
console.log(groupedProducts);
// 출력:
// {
// 과일: [
// { name: '사과', category: '과일', price: 1000 },
// { name: '바나나', category: '과일', price: 500 }
// ],
// 채소: [
// { name: '당근', category: '채소', price: 700 },
// { name: '상추', category: '채소', price: 300 }
// ]
// }
- 위 코드에서는
reduce
메서드를 사용하여 빈 객체를 초기값으로 설정하고, 카테고리별로 상품들을 그룹화합니다.
3.2. 🌳 배열을 사용한 트리 구조 생성
const treeData = [
{ id: 1, name: 'root', parentId: null },
{ id: 2, name: 'node1', parentId: 1 },
{ id: 3, name: 'node2', parentId: 1 },
{ id: 4, name: 'node3', parentId: 2 },
{ id: 5, name: 'node4', parentId: 3 },
];
const buildTree = (data) => {
return data.reduce((acc, cur) => {
const { id, parentId } = cur;
const node = { ...cur, children: [] };
if (parentId === null) {
acc.push(node);
} else {
const parentNode = acc.find((n) => n.id === parentId);
if (parentNode) {
parentNode.children.push(node);
}
}
return acc;
}, []);
};
const tree = buildTree(treeData);
console.log(tree);
// 출력:
// [
// {
// id: 1,
// name: 'root',
// parentId: null,
// children: [
// {
// id: 2,
// name: 'node1',
// parentId: 1,
// children: [
// { id: 4, name: 'node3', parentId: 2, children: [] }
// ]
// },
// {
// id: 3,
// name: 'node2',
// parentId: 1,
// children: [
// { id: 5, name: 'node4', parentId: 3, children: [] }
// ]
// }
// ]
// }
// ]
- 위 코드에서는
reduce
메서드를 사용하여 배열 데이터를 트리 구조로 변환합니다.
4. ⚖️ reduce
메서드 장점과 단점 비교
4.1. 👍 장점
- 코드 간결성 및 가독성 향상: 반복적인 루프 구문을 사용하지 않고도 배열 데이터를 처리하는 로직을 간결하게 표현할 수 있어 코드의 가독성을 향상시킵니다.
- 다양한 연산 처리:
reduce
메서드 하나로 합계, 평균, 최대/최소값 계산, 데이터 변환 등 다양한 연산을 수행할 수 있습니다. - 함수형 프로그래밍:
reduce
메서드는 함수형 프로그래밍 패러다임을 따르며, 코드의 재사용성을 높이고 부수 효과를 줄이는 데 도움이 됩니다.
4.2. 👎 단점
- 복잡한 로직 처리 시 가독성 저하: 복잡한 로직을
reduce
메서드 하나에 담으려고 하면 코드의 가독성이 떨어질 수 있습니다. - 디버깅의 어려움:
reduce
메서드 내부에서 발생하는 오류를 디버깅하는 것이 까다로울 수 있습니다. - 성능 문제: 배열의 크기가 매우 큰 경우,
reduce
메서드보다for
루프를 사용하는 것이 성능 측면에서 유리할 수 있습니다.
5. ⚡ reduce
메서드 성능 고려 및 최적화
배열의 크기가 매우 큰 경우, reduce
메서드의 성능을 고려해야 합니다. reduce
메서드는 내부적으로 새로운 배열을 생성하고 반환하기 때문에, 메모리 사용량이 증가하고 성능 저하가 발생할 수 있습니다.
성능 최적화를 위해 다음과 같은 방법을 고려할 수 있습니다.
for
루프 사용: 배열의 크기가 매우 큰 경우,reduce
메서드보다for
루프를 사용하는 것이 성능 측면에서 유리할 수 있습니다.- 적절한 초기값 설정: 초기값을 설정하지 않으면 배열의 첫 번째 요소가 초기값으로 사용되므로, 불필요한 연산이 발생할 수 있습니다.
- 콜백 함수 내부 로직 최적화: 콜백 함수 내부의 로직을 최대한 간결하게 유지하여 성능 저하를 최소화해야 합니다.
6. 📚 추가 학습 자료
- MDN 문서: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
- Fun Fun Function - Reduce: https://www.youtube.com/watch?v=Wl98eZpkp-c
reduce
메서드는 JavaScript에서 제공하는 강력한 도구 중 하나입니다. 위 내용을 참고하여 reduce
메서드를 효과적으로 활용하고, JavaScript 코드의 가독성과 효율성을 향상시키세요!
'개발 분야 (Development Area) > 프론트엔드 (Frontend)' 카테고리의 다른 글
[React] React Query와 Storybook: "No QueryClient set" 오류 해결 방법 (0) | 2025.02.24 |
---|---|
React useEffect 완벽 가이드: 햄버거 메뉴 구현에서 시작된 여정 (SSR 환경에서의 활용과 최적화)🚀 (2) | 2025.01.11 |
[React] React의 `createRoot`와 `hydrateRoot`이해하기 (0) | 2024.08.25 |
🛑 React에서 key={index} 사용의 위험성: 쉽게 이해하는 key 값과 React Reconciliation (0) | 2024.07.30 |