본문 바로가기

FrontEnd

리덕스 이후의 삶(Life after Redux) : 리덕스 없이 리덕스 장점 누리기

반응형
Redux는 유용한 도구이지만 React의 새로운 API는 다음 애플리케이션에서 해당 라이브러리를 대체할 수 있습니다.

원문 : https://medium.com/itnext/life-after-redux-21f33b7f189e

 

Life after Redux

Redux has been a useful tool but React’s new APIs are cause for pause as to whether or not you should use it in your next application.

itnext.io

이 기사에서는 React의 새로운 useReducer 훅을를 간단한 이벤트 버스와 결합하여
애플리케이션 아키텍처에 필요한 확장성을 제공하는 동시에
Redux가 대규모 애플리케이션에서 종종 촉진하는 일부 문제를 방지할 수 있는 방법을 탐구합니다.
Photo by Arif Wahid on Unsplash

리액트의 새로운 기능과 리덕스

인기 있는 상태 컨테이너 Redux는
분리된 컴포넌트 간에 데이터 공유, 데이터 일관성 보장, 코드 구성을 위한 템플릿 제공 등
프런트엔드 애플리케이션에 여러 이점을 제공합니다.
그러나 최근 React의 핵심 API에 추가된 내용은
Redux가 앱에 적합한지 여부에 대해 다시 고려할 가치가 있음을 시사할 수 있습니다.
최근 API 변경으로 인해 생각의 전환이 나타나기 시작했으며,
결과적으로 Redux 기능의 2/3 이상을 현재 React 코어 자체에서 찾을 수 있습니다.
이와 함께 누락된 부분이 큰 추가 종속성 내에서와 달리 '별도의' 엔터티로 유지 관리될 때 더 많은 유연성을 제공한다고 주장할 수 있습니다.
일부 사람들은 상태 관리를 위해 새로운 HooksContext APIs를 사용하는 아이디어(examined)를 검토했지만
Redux를 복합적인 부분으로 분해한 다음 React가 제공하지 않는 부분을 대체하여 실제로 더 많은 것을 만들 수 있다는 것입니다.
순수한 Redux보다 유연한 아키텍처를 갖을 수 있고, 암시적인 함정이 없습니다.

리덕스와 절충점(tradeoffs)

Redux는 이벤트처럼 동작하는 Action 객체를 사용하여 제어되는 함수적적 불변 상태 관리 컨테이너입니다.
이를 통해 애플리케이션은 이러한 이벤트를 사용하여 상태 변경 수신기에 전파되는 변경 불가능한 전역 데이터 개체에 대한 업데이트를 나타낼 수 있습니다.
Redux의 가장 효과적인 기능 중 하나는
프로그래머가 Reducer 함수(Array.prototype.reduce를 생각해 보세요)를
사용하여 주어진 Action 객체를 저장소의 현재 상태와 병합하는 방식입니다.

그럼에도 불구하고 Redux를 사용하면 이점과 절충점이 모두 있습니다.
React 애플리케이션 내에서 Redux를 사용하여 장단점을 살펴보겠습니다.

 

Redux를 사용하면 많은 이점이 있습니다.

  • 개발자는 react-redux와 같은 도우미 라이브러리를 사용하여 애플리케이션 전체에서 서로 다른 컴포넌트 간에 상태 데이터를 쉽게 공유할 수 있습니다.
  • 제어 흐름(control-flow), 미들웨어 패턴 및 사용 가능한 미들웨어 확장 에코시스템을 얻을 수 있습니다.
  • 단일 마스터 저장소를 사용하면 특정 MVC 패턴에서 Redux 및 Flux 이전에 문제였던 상태가 일관성을 유지하는지 확인할 수 있습니다.
  • 중앙 집중화를 통해 개발자는 시간 여행 디버깅(이라고도 하는 Redux Devtools(Redux Devtools)에서 지원하는 기술의 이점을 얻을 수 있습니다.,
  • 상태 리듀서 패턴은 원자 상태 변경을 관리하고 구성하는 효과적이고 간단한 방법이며, Redux는 JavaScript 애플리케이션 내에서 상태를 관리하기 위한 공통 언어와 규칙을 제공합니다.

모든 것이 훌륭하지만 Redux에는 여전히 몇 가지 문제가 있습니다.

  • Redux는 코드베이스 전체에 의존성을 내리는 전역 종속성이 되는 경향이 있습니다.
    • 이는 개발자가 실제로 필요한 것보다 훨씬 더 많은 상태를 전역적으로 저장하도록 유도할 수 있습니다.
  • Redux는 상태 관리 컨테이너로 간주되지만 컨텍스트에 따라 안티 패턴으로 간주될 수 있는 관행인 이벤트 버스로 자주 사용됩니다.
  • Redux는 기본적으로 비동기성을 잘 다루지 않으며 비동기 이벤트를 지원하기 위해 미들웨어가 필요합니다.
  • Redux는 기본적으로 부작용을 처리하지도 않습니다. 라우터와 같은 다른 암시적 상태 관리 컨테이너와의 통합은 항상 문제가 있었습니다.
  • 새로운 React API 덕분에 전역 애플리케이션 데이터를 관리하기 위해 Redux가 필요하지 않습니다.
이러한 절충점 중 일부는 Redux를 이용한 컴포넌트를 분리하고 나머지는 분리하지 않음으로써 부분적으로 해결할 수 있습니다.
동시에 Redux와 같은 표준 라이브러리가 제공하는 이점 중 일부를 잃을 수 있습니다.
우리의 옵션을 살펴보기 위해 이제 Redux와 현재 우리에게 제공하는 것이 무엇인지 더 잘 이해하기 위해
데이터 변경 트랜잭션의 흐름을 조사해 보겠습니다.

데이터 트랜잭션 알아보기

React가 현재 제공하는 것을 가장 잘 사용하는 방법을 탐구하기 전에
Redux가 개념적으로 동작하는 방식을 더 깊이 살펴보고
우리가 마음대로 사용할 수 있는 새로운 내장 React 도구와 의미 있는 구별을 할 수 있습니다.

(state, action) => state Reducer는 데이터 트랜잭션의 변환 단계를 제어하기 때문에 Redux의 핵심입니다.
이것은 프론트엔드 비즈니스 계층의 대부분을 정의하는 설정의 중요한 부분이지만
이 주변에는 트랜잭션 시퀀스의 체인을 구성하는 하위 시스템 또는 빌딩 블록이 있습니다.
Redux 기반 React 앱을 빌드할 때 다음과 같이 보이는 빌딩 블록에 대해 생각하고 싶을 수 있습니다.
  1. 컴포넌트는 액션 타입의 데이터를 Redux 내의 Pub/Sub 또는 이벤트 버스 구현에 전달합니다.
  2. Pub/Sub(이벤트 시스템)는 Action을 조작하는 일련의 미들웨어를 통해 데이터를 전송하여 돌연변이를 진행하거나 연기합니다. 저는 이것을 트랜잭션 전처리라고 부르고 싶습니다.
  3. Action은 새로운 상태 트리를 생성하기 위해 현재 State를 주어진 Action과 불변하게 병합하는 순수한 Reducer 함수를 실행하는 트랜잭션 엔진으로 보내집니다.
  4. 새 상태 트리는 메모리 내 객체로 존재하지만 localStorage 또는 데이터베이스와 같은 다른 곳에 캐시될 수도 있는 데이터 스토어에 저장됩니다.
  5. State Dependency Injector(상태 종속성 인젝터)는 데이터 저장소에서 변경 사항을 수신하고 이를 필요로 하는 컴포넌트에 새 상태를 제공합니다.
    • react-redux
  6. 수신자(recipient) 컴포넌트는 마침내 새 상태를 수신하고 업데이트된 데이터를 표시하는 다시 렌더링합니다.

react-redux가 상태 의존성 주입기이고
redux가 여러 조각을 제공하는 다음과 같은 방식으로 이 흐름과 각 조각을 제공하는 기술 조합에 대해 생각할 수 있습니다.
아래 다이어그램을 참조하십시오.

Building blocks of a Redux Transaction


리액트가 제공하는 상태 관리 기능

React의 새로운(new) API(APIs)는 많은 Redux 구성 요소와 모든 react-redux를 대체할 수 있는 기능을 제공합니다.
useReducer API 내에서 유사한 트랜잭션 엔진을 찾을 수 있습니다.
useReducer 훅은 또한 변경할 수 없는 데이터 저장소를 제공합니다.
그런 다음 새로운 useContext API를 활용하여 상태 종속성 인젝터 역할을 할 수 있습니다.
카운트에 클라이언트 컴포넌트를 포함하면 전체 트랜잭션 흐름의 거의 2/3가
이제 React와 번들로 제공되는 기능 내에서 관리된다고 말할 수 있습니다.
이는 상당한 양입니다!
 
Substituting React for Redux
위에서 Pub/Sub(또는 이벤트 시스템) 및 트랜잭션 전처리기가 누락되었음을 알 수 있습니다.

useReducer가 자식에게 공유할 수 있는 디스패치 함수를 노출한다는 점에서 이미 이벤트 시스템을 제공한다고 주장할 수 있습니다.
문제는 React의 Fiber 재작성 및 결과적으로 일관된(eventually-consistent) 상태 확인의 세부 사항으로 인해
순차 트랜잭션 전처리 계층을 useReducer에 적용하는 쉬운 방법(no easy way)이 없다는 것입니다.
 
이것은 디스패치를 ​​이벤트 시스템의 클라이언트로 사용하고
트랜잭션을 리액트로 유입(influx)하는 것이 훨씬 낫다는 것을 의미합니다.
또한 이벤트 디스패처와 상태를 분리하는 이점 또한 얻을 수 있습니다.
이는 앱의 컨텍스트와 데이터가 섞이지 않도록 해줍니다.
 
createContext의 성능에 대한 몇 가지 우려(아래 주석 참조)가 있습니다.
createContext로 이동하는 react-redux의 기술적 문제에서 이에 대한 증거를 볼 수 있습니다.
이벤트 버스를 상태 관리에서 분리하여 얻을 수 있는 것 중 하나는 성능 요구 사항 측면에서 상태의 각 부분을 다르게 처리하는 기능입니다. 따라서 대부분의 상황에서는 React의 createContext를 사용하는 것이 좋지만
더 빠른 업데이트 성능이 필요할 때 사용할 수 있는 한 가지 기술은
이벤트 버스에서 전달된 이벤트를 직접 수신하고 필요한 경우 로컬 캐시를 사용하여 내부적으로 이를 관리하는 것입니다.
 
reinspect가 있으면 Redux Devtools를 리덕스 없이도 사용할 수 있습니다.

이벤트 시스템 추가하기

그렇다면 이벤트 시스템으로 무엇을 사용해야 할까요? 당신이 좋아하는 아무거나 가능합니다.
트랜잭션 전처리 엔진을 무료로 얻을 수 있기 때문에 RxJS와 같은 것을 고려할 수 있습니다.
redux-observable에서 앱을 마이그레이션하는 경우 올바른 방법일 수 있습니다.
주 : 액션을 rxjs에서 날리라는 말임
Using RxJS as an Event Bus and Transaction Preprocessing system
또는 Node의 EventEmitter 모듈의 동형 포트를 사용하여 이벤트를 전달할 수 있습니다.
이것은 동작하지만 관련 이벤트에만 응답하기 위해 앱의 섹션을 분리하려는 경우 유용할 수 있는 와일드 카드 또는 이름 간격 이벤트 수신을 지원하지 않습니다.
와일드 카드 이벤트를 허용하는 EventEmitter2로 해답을 찾았습니다.
모든 사람을 위한 것은 아니지만 내 방법은
이벤트 버스에서 실제로 필요한 기능의 작은 하위 집합만 제공하는
우수한 TypeScript 지원을 포함하는 간단한 이벤트 버스 API(simple Event Bus API that contains good TypeScript support)로 래핑하는 것이었습니다.
나는 ts-bus라는 나만의 라이브러리를 만들었습니다.
Using an event bus only
주 : 액션을 eventEmitter에서 날리라는 말임
이런 방법 중 하나는 Redux만 사용하는 것보다 더 많은 유연성을 제공합니다.

별도 이벤트 시스템의 이점

이벤트 시스템을 상태 관리에서 분리하면 많은 이점이 있습니다.

이벤트에 따라 항상 상태를 변경할 필요는 없습니다.

이벤트에 대한 응답으로 상태가 변경되는 것이 일반적이지만 항상 이벤트와 상태 변경을 연결할 필요는 없습니다.
때때로 필요한 것은 비동기 백그라운드 프로세스를 시작하고 완료되면 보고하도록 하는 것입니다.
필요하지 않은 외부 상태를 가져오면 복잡성과 오버헤드가 추가됩니다.

작고 간단한 컴포넌트를 의존성에 노출시키지 않습니다.

이벤트 시스템은 다른 컴포넌트와 통신해야 하는 모든 컴포넌트가 이벤트 디스패처의 인스턴스에 액세스해야 하므로
대형 앱에 대한 주요 종속성 위험이 됩니다.
별도의 이벤트 시스템을 사용하여 모든 앱 컴포넌트에 자동으로 연결된 상태 관리를 방지했습니다.
 

원하는대로 비동기를 관리할 수 있습니다

이벤트는 더 이상 상태 변경에 연결되지 않으므로 비동기가 더 쉬워집니다.
비동기식 이벤트를 관리하기 위해 비동기 함수를 사용하는 것이 쉽습니다. (thunk? no)
비동기성을 관리하는 방법과 사용 가능한 상태 종속성을 제어합니다.
취소 가능한 사가 워크플로를 구현하는 것은 매우 어렵지만 이는 원래 해결하기 쉬운 문제가 아니며 실제로 필요한 경우도 거의 없습니다.

라우터와 같은 별도의 저장소에서 상태를 추적하는 것이 간단합니다

때때로 상태를 관리해야 하지만 상태 컨테이너에서 추적할 수 없는 경우가 있습니다.
라우터를 통해 브라우저 URL 내에서 상태가 유지되는 탐색에서 가장 자주 발생합니다.
별도의 이벤트 시스템이 있다는 것은 라우터에 대한 추상화 지점을 쉽게 제공할 수 있음을 의미합니다.
이 기술의 추가 이점은 애플리케이션 전체에서 라우터 코드를 공유할 필요가 없다는 것입니다.

이벤트 드리븐 아키텍처의 유연성을 갖게 됩니다.

간단한 이벤트 이미터를 사용하여 원하는 대로 이벤트를 처리할 수 있습니다.
서비스 워커에서 핸들러를 실행하거나 Observable 스트림을 설정하거나 계산을 서버로 오프로딩하는 방법입니다.
서버에 이벤트 보내기, 서버에서 이벤트 받기, 두 번째 micro-Vue 프론트엔드 애플리케이션 동기화, RxJS로 부작용 관리 등
이벤트 버스를 공유할 수 있는 한 당신의 애플리케이션 아키텍처가 이를 지원할 수 있습니다.
이것은 원하는 대로 앱을 연결할 수 있는 이벤트 기반 아키텍처의 힘입니다.
예시로 다양한 프론트엔드 프레임워크 간에 통신하기 위해 이벤트 버스를 개념적으로 공유하는 방법을 볼 수 있습니다.
이것은 프런트엔드 스택에서 작업하는 여러 팀이 있는 대규모 조직이나
팀이 시스템을 하나씩 천천히 다시 작성하는 경우에 좋은 접근 방식일 수 있습니다.
이벤트 버스를 통한 마이크로 프론트엔드 통신의 예. 각 앱은 코드베이스 간의 종속성을 피하기 위해 자체 상태를 관리합니다.
또는 socket.io와 같은 브리징 시스템으로 이벤트 버스를 설정하여 브라우저 간에 다중 메시징 채널을 만들고
대규모 이벤트 소스 응용 프로그램에 공급하기 위해 Kafka와 같은 산업 이벤트 스트리밍 플랫폼을 이용할 수 있습니다.
이는 실시간 데이터가 필요한 금융 거래소와 같은 경우에 좋은 접근 방식일 수 있습니다.
클라이언트와 서버 간의 이벤트 동기화의 가능한 예입니다.

 

마지막으로 우리는 기본적인 이벤트 버스 아키텍처에 대해 이야기하고 있고
Redux에는 이벤트 버스가 포함되어 있기 때문에 이러한 시스템은 확실히 Redux만 사용하여 설정할 수 있습니다.
주의할 점은 Redux를 사용하면 개발자가 부주의하게 분리되어 있어야 하는 시스템 간에 상태를 공유할 수 있습니다.
즉 버스와 데이터가 함께 동작하게 됩니다.
이는 득보다 실이 더 많을 수 있음을 의미합니다.

결론

Redux는 가치 있고 다재다능한 라이브러리이며 단순한 상태 관리 컨테이너 그 이상입니다.
Redux를 사용하기로 선택하면 이벤트 버스, 데이터 저장소, 표준 DI 메커니즘 등을 선택하게 되며
이러한 모든 개별 애플리케이션 기능을 대한 단일 패키지에 의존하는 것이 모든 상황에 필요한 것이 아닐 수도 있습니다.
 
Redux를 제거하기 위해 큰 앱을 다시 작성하는 것은 확실히 의미가 없지만,
Redux는 글로벌 종속성이 되는 경향이 있기 때문에 신규 또는 소규모 앱은
적절한 이벤트 버스를 선택하여 요구 사항을 충족할 수 있는지 여부에 대해 신중하게 생각해야 합니다.
내장된 React 상태 관리를 사용하는 것이 더 깨끗하고 유연한 아키텍처로 이어질 것입니다.
... 새로운 컨텍스트가 로케일/테마와 같이 빈도가 낮은 업데이트에 사용할 준비가 되어 있다는 것입니다.
이전 컨텍스트와 비슷하게 사용해도 좋습니다. 즉, 정적 값의 경우 구독을 통해 업데이트를 전파합니다.
하지만, 모든 Flux와 유사한 상태 전파를 대체할 준비는 되지 않았습니다.

Sebastian Markbage - 리액트 코어 개발팀 엔지니어

역자 의견

미디엄 박수 1400개 짜리 게시물이라 번역해봤는데
결과적으로 자기 라이브러리 광고이며, 리덕스 대신 더 복잡한 계층 하나를 중간에 두는 결과를 초래합니다.
그리고 리덕스 툴킷과 (리스너) 미들웨어로 전부 해결할 수 있는 문제입니다.
어지간하면 걍 리덕스 툴킷 씁시다... rxjs쓸꺼 아니면...

반응형