본문으로 건너뛰기
yceffort
PostsSeriesTagsAbout🧪 Research
EN

Tweaks

theme
accent palette
film grain
minimal mode
BACK TO INDEX
◆ ESSAY
--min
--year
KOoriginal

mailMail icongithubtwitter
yceffort
•
© 2026
•
https://yceffort.kr
BACK TO INDEX
◆ ESSAY

reduce에 spread 를 쓰면 안되는 이유

avatar
yceffort
2021-06-22 · 4분
4min
2021year
KOoriginal
javascriptweb-performancealgorithm

자바스크립트 프로젝트를 개발하다보면, 배열을 하나의 객체로 묶어야될 필요성이 생길 때가 있다. 아래 예제를 들어보자.

const items = [
  {name: 'obama', age: 10, country: 'USA'},
  {name: 'trump', age: 14, country: 'USA'},
  {name: 'moon', age: 37, country: 'KOREA'},
  {name: 'clinton', age: 64, country: 'USA'},
  {name: 'bush', age: 49, country: 'USA'},
]

이렇게 배열로 되어있는 것을 이름을 키로 하는 객체로 바꾼다고 한다면, 아마 다들 reduce를 생각할 것이다.

const result = items.reduce((prev, item) => {
  prev[item.name] = {age: item.age, country: item.country}
  return prev
}, {})

// {
//   obama: { age: 10, country: 'USA' },
//   trump: { age: 14, country: 'USA' },
//   moon: { age: 37, country: 'KOREA' },
//   clinton: { age: 64, country: 'USA' },
//   bush: { age: 49, country: 'USA' }
// }

혹은 for loop를 활용하여 똑같이 할 수 있다.

const result2 = {}
for (let item of items) {
  result2[item] = {age: item.age, country: item.country}
}

둘 중에 뭐가 더 읽기 좋은 코드냐고 묻는다면, 당연히 후자일 것이다. 내가 아무리 reduce를 좋아한다고 해도 그건 반박 불가능한 사실이다. 그러나 요즘 리액트 커뮤니티의 성장으로 인해 함수형 프로그래밍 스타일로 코드를 작성하는 일이 잦아짐에 따라서 reduce를 쓰는일이 많아지고 있다.

그러나 문제는 아래의 스타일이다.

const result3 = items.reduce(
  (prev, item) => ({
    ...prev,
    [item.name]: {age: item.age, country: item.country},
  }),
  {},
)

이제부터 이런 코드를 reduce...spread라고 부르겠다.

이 spread operator는 Object.assign과 유사하게 동작하는데, 소스 객체의 속성을 순환하면서 각 속성을 타켓의 객체에 하나씩 복사해 넣는다. 위 코드의 경우에는, 새로운 객체를 계속해서 복사하는 것이다. 위 코드의 문제는 무엇일까?

위코드의 동작을 살펴본다면, 대충 아래와 같을 것이다.

# 첫번째
result = {}

{...result, 'obama': {age: 10, country: 'USA'}}

# 두번째
result = {'obama': {age: 10, country: 'USA'}}

{...result, 'trump': {age: 14, country: 'USA'}}

# 세번째

result = {'obama': {age: 10, country: 'USA'}, 'trump': {age: 14, country: 'USA'}}

{...result, 'moon': {age: 37, country: 'KOREA'}}

##....

의사 코드이기 때문에 대충 보면 된다.

루프가 반복될때마다, 모든 루프에서 정확히 n회 실행되는 것은 아니지만, 이렇게 될 경우 O ( n 2 ) O(n^2)라 볼 수 있다.

바벨이나 타입스크립트 트랜스파일러를 사용하면 해당 코드를 어떻게 변환할까?

그전에 먼저, tc39에 나와있는 객체 전개 연산자의 스펙을 살펴보자.

let aClone = {...a}
let aClone = Object.assign({}, a)

위 구문도 거의 동일한 작업을 수행하므로, 전개 연산자와 비슷한 시간 복잡성을 가질 것이다. 즉, 새 객체를 생성한다음, 나머지 객체의 키를 새객체에 반복해서 복사하는 것이다.

  • babel transpile
  • typescript

따라서 reduce에서 전개연산자로 합성해서 리턴하는 것은 굉장히 느린 코드라고 볼 수 있다.

여담으로, 이와 관련된 키보드 배틀(?) 이 트위터에서 열린 적이 있다.

https://twitter.com/fildon_dev/status/1396252890721918979

문제의 코드

const users = [
  {id: 1, name: 'Tony Stark', active: false},
  {id: 2, name: 'Bruce Banner', active: true},
  {id: 3, name: 'Natasha Romanoff', active: false},
  {id: 4, name: 'Chris Evans', active: true},
  {id: 5, name: 'Chris Hemsworth', active: false},
  {id: 6, name: 'Clark Gregg', active: false},
]

// good?
const result1 = users.reduce((acc, curr) => {
  if (curr.active) {
    return acc
  }
  return {...acc, [curr.id]: curr.name}
})

// bad?
const result2 = users
  .filter((user) => !user.active)
  .map((user = (user) => ({[user.id]: user.name})))
  .reduce(Object.assign, {})

그래서 뭐가 더 성능이 좋고 빠를까? 저 두개가 최선의 방법일까? 만약에 나라면,,,

🤔

관련 글

  • #javascript#algorithm

    맵과 객체 중 무엇을 언제 쓰는 것이 좋을까?

    Map 도 씁시다

    2025-03-22·25분
  • #memory#debugging#web-performance

    크롬 메모리 프로파일러 사용하는 방법

    스냅샷 해석과 디버깅의 책임은 본인에게 있습니다

    2022-04-28·9분
  • #memory#javascript#debugging

    자바스크립트 메모리 누수와 해결 방법

    ```toc tight: true, from-heading: 2 to-heading: 3 ``` [4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them](https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-...

    2020-07-14·28분
  • #web-performance#javascript#browser

    실행하지 않는 JavaScript를 10MiB까지 늘려봤다

    호출하지 않는 함수가 10MiB 들어 있으면 얼마나 손해일까. 바이트가 만든 비용과 코드 형태가 만든 비용을 갈라 855회 측정했다. 미호출 선언은 MiB당 약 21ms로 CPU를 4배 늦춰도 늘지 않았고, 파일을 읽자마자 실행되는 초기화 코드는 MiB당 201ms를 메인 스레드에 얹었다. 실제 라이브러리에서는 import만 해도 모듈 평가가 실행됐다.

    2026-09-16·54분

새 글을 놓치고 싶지 않으시다면 RSS로 구독해 주세요.

RSS 구독 →

yceffort — 프론트엔드 엔지니어입니다.

← Back to the blogIssue on GitHub →