본문 바로가기
IT 개발/자바스크립트

[JAVASCRIPT] 자바스크립트 reduce() 함수 완벽 가이드

by 이것 저것 모든것 2025. 2. 27.
728x90

자바스크립트 reduce() 함수 완벽 가이드

목차

  1. 개요
  2. 기본 문법
  3. 동작 원리
  4. 기본 활용 예제
  5. 고급 활용 예제
  6. 실전 활용 패턴
  7. 성능과 주의사항
  8. 실용적인 레시피

개요

reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다. 배열을 기반으로 계산된 단일 값을 도출할 때 매우 유용한 함수형 프로그래밍 도구입니다.

기본 문법

array.reduce(callback(accumulator, currentValue[, currentIndex[, array]])[, initialValue])

매개변수 설명

  • callback: 리듀서 함수
    • accumulator: 누산기. 콜백의 반환값을 누적
    • currentValue: 처리할 현재 요소
    • currentIndex: 처리할 현재 요소의 인덱스 (선택적)
    • array: reduce를 호출한 배열 (선택적)
  • initialValue: 초기값 (선택적이지만 권장됨)

기본 예제

const numbers = [1, 2, 3, 4, 5];

// 배열의 모든 숫자 합산
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 15

// 초기값 없이 사용 (권장하지 않음)
const sumWithoutInitial = numbers.reduce((acc, curr) => acc + curr);
console.log(sumWithoutInitial); // 15

동작 원리

초기값이 있는 경우

const numbers = [1, 2, 3];
const result = numbers.reduce((acc, curr) => {
    console.log(`누산값: ${acc}, 현재값: ${curr}`);
    return acc + curr;
}, 0);

// 실행 로그:
// 누산값: 0, 현재값: 1
// 누산값: 1, 현재값: 2
// 누산값: 3, 현재값: 3
// 최종 결과: 6

초기값이 없는 경우

const numbers = [1, 2, 3];
const result = numbers.reduce((acc, curr) => {
    console.log(`누산값: ${acc}, 현재값: ${curr}`);
    return acc + curr;
});

// 실행 로그:
// 누산값: 1, 현재값: 2
// 누산값: 3, 현재값: 3
// 최종 결과: 6

기본 활용 예제

배열의 최대/최소값 찾기

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

const max = numbers.reduce((acc, curr) => Math.max(acc, curr));
console.log(max); // 9

const min = numbers.reduce((acc, curr) => Math.min(acc, curr));
console.log(min); // 1

객체 배열 합산

const orders = [
    { product: '노트북', price: 1200000, quantity: 1 },
    { product: '마우스', price: 50000, quantity: 2 },
    { product: '키보드', price: 150000, quantity: 1 }
];

const totalAmount = orders.reduce((acc, curr) => 
    acc + (curr.price * curr.quantity), 0
);
console.log(totalAmount); // 1450000

배열을 객체로 변환

const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];

const fruitCount = fruits.reduce((acc, curr) => {
    acc[curr] = (acc[curr] || 0) + 1;
    return acc;
}, {});

console.log(fruitCount);
// { apple: 3, banana: 2, orange: 1 }

고급 활용 예제

중첩 배열 평탄화

const nestedArray = [[1, 2], [3, 4], [5, 6]];

const flattened = nestedArray.reduce((acc, curr) => 
    acc.concat(curr), []
);
console.log(flattened); // [1, 2, 3, 4, 5, 6]

Promise 체이닝

const asyncTasks = [
    () => Promise.resolve(1),
    (a) => Promise.resolve(a + 2),
    (a) => Promise.resolve(a + 3)
];

async function executeSequentially() {
    const result = await asyncTasks.reduce(async (promise, task) => {
        const accumulator = await promise;
        return task(accumulator);
    }, Promise.resolve(0));

    return result;
}

executeSequentially().then(console.log); // 6

복잡한 데이터 구조 변환

const users = [
    { id: 1, name: '김철수', skills: ['JavaScript', 'React'] },
    { id: 2, name: '이영희', skills: ['Python', 'Django'] },
    { id: 3, name: '박지성', skills: ['JavaScript', 'Node.js'] }
];

const skillMap = users.reduce((acc, user) => {
    user.skills.forEach(skill => {
        if (!acc[skill]) {
            acc[skill] = [];
        }
        acc[skill].push(user.name);
    });
    return acc;
}, {});

console.log(skillMap);
// {
//   JavaScript: ['김철수', '박지성'],
//   React: ['김철수'],
//   Python: ['이영희'],
//   Django: ['이영희'],
//   'Node.js': ['박지성']
// }
728x90

실전 활용 패턴

파이프라인 구현

const pipe = (...functions) => 
    functions.reduce((acc, fn) => (...args) => fn(acc(...args)));

// 사용 예시
const add = x => x + 2;
const multiply = x => x * 3;
const divide = x => x / 4;

const compute = pipe(add, multiply, divide);
console.log(compute(5)); // ((5 + 2) * 3) / 4 = 5.25

상태 관리

const initialState = { count: 0, lastUpdated: null };

const actions = [
    { type: 'INCREMENT', payload: 1 },
    { type: 'DECREMENT', payload: 1 },
    { type: 'INCREMENT', payload: 2 }
];

const reducer = (state, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return {
                ...state,
                count: state.count + action.payload,
                lastUpdated: new Date()
            };
        case 'DECREMENT':
            return {
                ...state,
                count: state.count - action.payload,
                lastUpdated: new Date()
            };
        default:
            return state;
    }
};

const finalState = actions.reduce(reducer, initialState);

성능과 주의사항

성능 최적화

// 비효율적인 방법
const numbers = Array.from({ length: 1000000 }, (_, i) => i);
const sum = numbers
    .filter(n => n % 2 === 0)
    .map(n => n * 2)
    .reduce((acc, curr) => acc + curr, 0);

// 효율적인 방법
const optimizedSum = numbers.reduce((acc, curr) => {
    if (curr % 2 === 0) {
        return acc + (curr * 2);
    }
    return acc;
}, 0);

주의사항

// 빈 배열에서의 사용
const emptyArray = [];

// 오류 발생
try {
    emptyArray.reduce((acc, curr) => acc + curr);
} catch (e) {
    console.error('초기값 없이 빈 배열에서 reduce 사용 시 오류 발생');
}

// 안전한 사용
const safeResult = emptyArray.reduce((acc, curr) => acc + curr, 0);

실용적인 레시피

중복 제거

const array = [1, 2, 2, 3, 3, 4, 5, 5];

const unique = array.reduce((acc, curr) => {
    if (!acc.includes(curr)) {
        acc.push(curr);
    }
    return acc;
}, []);

console.log(unique); // [1, 2, 3, 4, 5]

그룹화

const people = [
    { age: 25, city: '서울' },
    { age: 30, city: '부산' },
    { age: 25, city: '서울' },
    { age: 35, city: '부산' }
];

const groupByCity = people.reduce((acc, person) => {
    const { city } = person;
    acc[city] = acc[city] || [];
    acc[city].push(person);
    return acc;
}, {});

console.log(groupByCity);
// {
//   '서울': [{ age: 25, city: '서울' }, { age: 25, city: '서울' }],
//   '부산': [{ age: 30, city: '부산' }, { age: 35, city: '부산' }]
// }

데이터 정규화

const normalizeData = (data, key) => {
    return data.reduce((acc, item) => {
        acc.byId[item[key]] = item;
        acc.allIds.push(item[key]);
        return acc;
    }, {
        byId: {},
        allIds: []
    });
};

const users = [
    { id: 1, name: '김철수' },
    { id: 2, name: '이영희' }
];

const normalized = normalizeData(users, 'id');
console.log(normalized);
// {
//   byId: {
//     1: { id: 1, name: '김철수' },
//     2: { id: 2, name: '이영희' }
//   },
//   allIds: [1, 2]
// }

결론

reduce() 함수는 단순한 배열의 합산을 넘어서 복잡한 데이터 변환, 상태 관리, 비동기 작업 처리 등 다양한 용도로 활용할 수 있는 강력한 도구입니다. 초기값을 적절히 사용하고, 리듀서 함수를 명확하게 작성하면 코드의 가독성과 유지보수성을 크게 향상시킬 수 있습니다.

728x90