개발

자바스크립트 reduce 사용 시 주의점

동고킴 2022. 9. 21. 07:00
반응형

reduce와 스프레드 연산자(Spread syntax)를 함께 사용하는 걸 피해라

 

자바스크립트의 reduce는 매우 강력한 내장 함수이다. 배열의 덧셈, 곱셈 등의 연산에 많이 사용되지만 자유도가 높아서 map, filter 등의 함수도 모두 reduce로 구현할 수 있다. 하지만 reduce 사용 시 주의해야 할 점이 있다. 그건 바로 reduce와 스프레드 연산자를 함께 쓰지 않는 것이다. 다음 코드를 예로 들어보자.

 

const arr = [
  { id: "ironman", name: "Tony Stark" },
  { id: "hulk", name: "Bruce Banner" },
  { id: "blackwidow", name: "Natasha Romanoff" },
];

const superheroes1 = arr.reduce(
  (acc, item) => ({
    ...acc,
    [item.id]: [item.name],
  }),
  {}
);
console.log("superheroes1", superheroes1);

const superheroes2 = {};
arr.forEach((o) => (superheroes2[o.id] = o.name));
console.log("superheroes2", superheroes2);

 

위의 코드는 arr 배열을 id를 키값으로 한 오브젝트로 변환하는 예이다. superheroes1, superheroes2의 결과는 같다. 그럼 성능은 어떨까? 아래 결과를 보면 superheroes2가 superheroes1보다 훨씬 빠르게 나온다.

 

 

왜 이런 결과가 나오는 걸까?

이유는 스프레드 연산자에 있다. TC39 Proposal에서 스프레드 연산자를 보면 내부적으로 Object.asign()처럼 동작한다. 스프레드 연산자는 Object.asign()의 Syntex sugar라고 할 수 있다.

// Shallow Clone (excluding prototype)
let aClone = { ...a };

// Desugars into:
let aClone = Object.assign({}, a);

 

그럼 Object.assign()는 어떻게 동작할까? MDNTC39를 보면 객체를 순환하면서 복사하고 있다. 즉, 시간복잡도가 O(N)이다.

 

 

다시 reduce 코드로 돌아가보자

 

const superheroes1 = arr.reduce(
  (acc, item) => ({
    ...acc,
    [item.id]: [item.name],
  }),
  {}
);

 

reduce는 배열에 대해 N번 실행된다. (시간복잡도 O(N))

그리고 reduce의 callback을 단계별로 단순화해보면

// 첫번째 순회
acc -> {} 
{ ...acc, ironman: 'Tony Stark' } // 복사 필요 없음

// 두번째 순회
acc -> { ironman: 'Tony Stark' } 
{ ...acc, hulk: 'Bruce Banner' } // 한번 복사
// would be copied to the new object

// 세번째 순회
acc -> { ironman: 'Tony Stark', hulk: 'Bruce Banner' } 
{ ...acc, blackwidow: 'Natasha Romanoff' } // 두번 복사

// ... 복사 반복

 

조건에 따라서 다르겠지만 callback에서 O(N)이 발생한다. reduce의  O(N)만큼 O(N)이 수행되는 것이기 때문에 O(N^2)만큼 수행된다고 볼 수 있다. 그래서 두 번째 예제 for문 O(N) 보다 느린 것이다. 빅오 표기법에서 알 수 있듯이 데이터가 커질수록 시간복잡도가 급격하게 커진다.

 

 

당장 데이터가 체감을 할 수 없을 만큼 작다고 하더라도 확장성을 고려한다면 reduce와 스프레드 연산자를 함께 사용하는 패턴을 피해야 할 것이다.

 

참고 : Why using object spread with reduce probably a bad idea

 

반응형