별 건 아니지만 리덕스의 리듀서는 왜 리듀서가 되었나.

dante Yoon·2022년 4월 30일
12

별 건 아니지만

목록 보기
5/11
post-thumbnail

셍긱보다 별 거일 수도..

리덕스

리덕스는 이제 프론트엔드 개발을 하는 사람이라면 한번쯤은 들어보거나 다뤄봤을 상태 관리 라이브러리입니다.

저는 항상 새로운 상태 관리 라이브리를 배울 때는 어떤 아이디어로 만들어졌고 무슨 모델을 기반으로 구현되었는지 살펴봅니다.

이 글을 읽는 여러분도 항상 tutorial 페이지로 넘어가기 전에 core concept에 대해 꼭 읽어보시는 것을 추천합니다.

라이브러리에 대한 코어 컨셉트와 주요 아이디어를 무시한다면, 나중에 엔지니어링 관점에서 문제를 해결할 때 왜 이 라이브러리를 사용해야 하는지 설명할 수 없게 됩니다.

단순 코더가 아니라 엔지니어가 되기 위해 우리는 이 점을 유의해야 합니다.

꼰?

왜 리듀서는 리듀서로 불리게 되었나

javascript에는 Array.prototype.reduce 라는 네이티브 유틸 함수가 있습니다.

이 함수를 살펴보면 시그니처는 <T>((prev:T, current: T) => reduced: T, initialValue: T) => T 인데요,

여기서 첫 인자인 <T>(prev:T, current: T) => T 콜백 함수는 array를 돌면서 현재까지 누적된 T 값인 prev와 현재 array에서 조회중인 인덱스의 값 T 타입을 인자로 받고 새로운 누적값 T 타입을 반환합니다.

여기서의 콜백 함수를 리듀서라고 부르는데요,
이 리듀서는 이전 값과 현재 값만을 가지고 새로운 누적값을 만들어 냅니다.

const numbers = [2, 5, 8]

const addNumbers = (previousResult, currentItem) => {
  console.log({ previousResult, currentItem })
  return previousResult + currentItem
}

const initialValue = 0

const total = numbers.reduce(addNumbers, initialValue)
// {previousResult: 0, currentItem: 2}
// {previousResult: 2, currentItem: 5}
// {previousResult: 7, currentItem: 8}

console.log(total)
// 15

이는 리덕스 리듀서의 action 배열에 리듀서를 적용한 부분과 매우 유사한 모습을 띄는데요,

const actions = [
  { type: 'counter/increment' },
  { type: 'counter/increment' },
  { type: 'counter/increment' }
]

const initialState = { value: 0 }

const finalResult = actions.reduce(counterReducer, initialState)
console.log(finalResult)
// {value: 3}

이런 컨셉은 리덕스의 리듀서와 다음의 부분에서 일치합니다.

  • 이전 상태들과 현재 상태를 변경하기 위한 메타 데이터인 action 을 가지고 새로운 상태를 만들어낸다.
  • dispatch 된 액션 값들을 가지고 특정 시점에 하나의 상태 값으로 만든다.

차이점은

  • Array.reduce()는 모든 작업이 한번에 일어나지만, 리덕스는 앱의 생명주기에 걸쳐서 동작한다.

    리덕스에 흥미가 없으신 분들은 윗 글까지만 읽으시면 됩니다.

    하지만 나는 아직 할 이야기가 더 남았지. 예스, 아임 투 머치 토커

    이거.. 썸네일로 괜찮을 지도?

dispatch는 이벤트 버스 아키텍쳐의 ㅁㅁㅁ이다.

redux의 상태값을 변화시키는 유일한 방법은 store.dispatch를 사용하는 것입니다. 그리고 이렇게 상태를 변화시키는 방법을 제한했기 때문에 리덕스는 상태 변경을 미리 예측할 수 있는 장점을 가질 수 있게 되었습니다.

dispatch는 이벤트 버스 아키텍쳐의 triger event 역할을 맡습니다.

dispatch + action

diaptch 함수의 인자로 action object를 넣어 호출하게 되면 event가 trigger되고 리듀서는 해당 action을 받아 새로운 상태 값을 반환합니다.

reducer는 이벤트 버스 아키텍쳐의 event listener 역할을 맡습니다.

리덕스가 해결하려는 문제와 core concept에 대해

MVC 아키텍쳐를 기반으로 만들어진 웹에서 상태 기반 업데이트를 하는 웹 어플리케이션이 주류로 자리잡으면서 컴포넌트 각 내부에서 상태를 가지는 형태가 자연스럽게 되었습니다.

근데 컴포넌트의 계층화가 세분화되고 각 컴포넌트가 하나의 거대한 트리 구조의 형태를 띄며 컴포넌트 간 상태를 업데이트 하고 조회하는 연결고리가 매우 복잡하게 얽히게 되었습니다.

음, 여기서 컴포넌트 간의 거리가 멀다고 하나의 UI 표현을 두 개의 각기 다른 메모리를 차지하고 있는 상태 객체가 맡는다면 MVC 패턴에서 발생했던 동일한 문제가 발생하게 됩니다.

게다가 react 같은 SPA를 사용할 때 lifting state up 기법을 써야 하는데, props도 많아지고 특정 컴포넌트가 다른 컴포넌트에 강하게 결합되면서 컴포넌트 재사용성이 떨어지게 되는 경우가 많이 발생합니다.

  • MVC의 문제점이나 SPA에서
  • 각 컴포넌트에서의 UI를 표현하기 위해 상태를 조회하거나 상태를 업데이트할 때 비즈니스 로직이 복잡해지는 것을

리덕스는 전역 상태 스토어를 제공함으로 앱에서 여러 컴포넌트가 조회하는 공통 상태 값의 single source of truth를 만들어주게 되었고,
리듀서가 순수함수로 작성되었고, 상태를 업데이트하는 과정 자체가 단순 컴포넌트가 내부에서 자신만의 상태를 관리하는 아래의 그림 처럼 단순화 되었기 때문에 언제, 어떻게, 어디서 상태가 업데이트 되는지에 대해 예측하기 쉬워졌습니다.

컴포넌트

리덕스

리덕스가 Prop drilling을 줄여주기 위해서 나온 것은 올바른 설명이 아니라고 생각합니다.
애초에 prop을 사용하는 리엑트만을 위해 만들어진 라이브러리가 아닙니다.
그리고 리엑트는 해당 문제를 해결하기 위해 내부적으로 contextAPI를 제공함으로 이를 해결하고 있습니다.

profile
성장을 향한 작은 몸부림의 흔적들

4개의 댓글

comment-user-thumbnail
2022년 5월 1일

덕분에 리덕스에 아주 약간의 흥미가 생겼어요!~

1개의 답글
comment-user-thumbnail
2022년 5월 3일

좋은 글 감사합니다. 리덕스의 리듀서가 javascript의 reduce 함수에서 유래 되었다는 것은 이해가 되었는데 혹시 javascript에서 제공하는 reduce는 왜 이런 네이밍을 갖게 되었는지에 대해서도 알고 계실까요? 예전부터 궁금했는데 명확한 이유를 찾지 못했네요 ㅠㅠ

1개의 답글