Redux - History & Why
리덕스 공식문서를 읽다가 redux의 Core Concepts
와 RTK
, react-redux
의 설명이 혼재되어 있어 처음 docs를 보는 이가 이해하기 어렵다. 한국인이 더 편하게 Redux
를 이해하고 사용할 수 있도록 정리해보려고 한다.
Notice: Docs를 기반으로 작성한 문서입니다. 작성자의 견해가 첨가되오니 수정이 필요한 부분은 Email로 문의주시면 감사하겠습니다.
리덕스의 정의
A JS library for predictable and maintainable global state management
리덕스는 예측 가능하고 유지보수가 쉬운 전역 상태 관리 라이브러리이다.
특징
- 예측 가능한 : 상태가 변하는 방식이 엄격하게 정의되어 있어, 어떤 일이 일어나는지 예측할 수 있다.
- 중앙 집중화 : 애플리케이션의 상태와 로직을 중앙에서 관리하면 실행 취소/재실행 , 상태 지속성 등 강력한 기능을 활용할 수 있다.
- 디버깅 가능 : Redux Dev Tools를 사용하면 애플리케이션 상태가 언제, 어디서, 왜, 어떻게 변경되었는지 쉽게 추적할 수 있다.
- 유연한 : Redux는 모든 UI 계층에서 작동하며 , 사용자의 요구 사항에 맞는 다양한 라이브러리와 프레임워크와 함께 사용할 수 있다.
리액트 컴포넌트 트리 간에 props drilling 복잡도 개선을 위한다면 Context API를 먼저 이용해보는 것을 추천한다. 리덕스는 조금 더 근원의 상태를 예측하고 디버깅하는데 적합하다.
리덕스의 역사
2011 : MVC 패턴의 한계
AngularJS, Ember, Backbone과 같은 초기 JavaScript MVC 프레임워크에는 문제가 있었습니다. AngularJS는 "컨트롤러"를 템플릿에서 분리하려고 했지만, <div onClick="$ctrl.some.deeply.nested.field = 123">
템플릿에서 작성하는 것을 막는 것은 없었습니다. 한편, Backbone은 이벤트 이미터를 기반으로 했습니다. 모델, 컬렉션, 뷰는 모두 이벤트를 내보낼 수 있었습니다. 모델은 이벤트를 내보낼 수 있고 "change:firstName", 뷰는 이벤트를 구독할 수 있습니다. 하지만 모든 코드는 해당 이벤트를 구독하고 더 많은 로직을 실행할 수 있으며, 이는 더 많은 이벤트를 트리거할 수 있습니다.
이로 인해 이러한 프레임워크는 디버깅하고 유지하기가 매우 어려웠습니다. 한 모델의 한 필드를 업데이트하면 앱 주변에서 실행되는 수십 개의 이벤트와 로직이 트리거되거나, 모든 템플릿이 언제든지 상태를 변경할 수 있어 상태 업데이트를 수행했을 때 어떤 일이 일어날지 이해하는 것이 불가능했습니다.
MVC 패턴
: Model, View, Controller의 약자입니다. Model에 데이터를 저장하고, Controller를 이용하여 Model의 데이터를 관리(CRUD)합니다. Model의 데이터가 변경되면 View로 전달되어 사용자에게 보여집니다. 또한 중요한 점은 사용자가 View를 통해 데이터를 입력하면 View 역시 Model을 업데이트할 수 있다는 점입니다. 즉 데이터가 양방향으로 흐를 수 있다는 것입니다.
2014 : Flux 패턴의 등장
React가 처음 공개된 2012~2013년 무렵, Facebook은 몇 년 동안 내부적으로 React를 사용했습니다. 그들이 마주친 문제 중 하나는 "읽지 않은 알림이 몇 개나 있는가"와 같이 동일한 데이터에 액세스해야 하는 여러 개의 독립적인 UI 부분이 있었지만, Backbone 스타일 코드를 사용할 때 그 논리를 일관되게 유지하기 어렵다는 것을 알게 되었습니다.
Facebook은 궁극적으로 "Flux"라는 패턴을 생각해냈습니다. a PostsStore
와 와 같은 여러 개의 싱글톤 Store를 만듭니다 CommentsStore
. 각 Store 인스턴스는 에 등록되고 Dispatcher
, Store에서 업데이트를 트리거하는 유일한Dispatcher.dispatch({type: "somethingHappened"})
방법은 를 호출하는 것입니다 . 그 평범한 객체를 "작업"이라고 합니다. 아이디어는 모든 상태 업데이트 로직이 반중앙화된다는 것입니다. 앱의 임의의 부분이 상태를 변경하도록 할 수 없고, 모든 상태 업데이트가 예측 가능하다는 것입니다.
Facebook은 2014년경에 이 "Flux Architecture" 컨셉을 발표 (opens in a new tab)했지만, 그 패턴을 구현한 전체 라이브러리를 제공하지 않았습니다. 그로 인해 React 커뮤니티는 패턴의 변형을 사용하여 수십 개의 Flux에서 영감을 받은 라이브러리를 구축하게 되었습니다.
Flux 패턴
: React에서 데이터를 관리하기 위한 패턴입니다. Flux 패턴은 단방향 데이터 흐름을 가지고 있습니다. 이는 데이터가 한 방향으로만 흐르게 하여 데이터의 흐름을 예측 가능하게 만들어줍니다. Flux 패턴은 Dispatcher, Store, View, Action으로 구성되어 있습니다.
2015 : Redux의 등장
2015년 중반, Dan Abramov는 Redux라는 또 다른 Flux (opens in a new tab)에서 영감을 받은 라이브러리를 만들기 시작했습니다. 컨퍼런스 강연에서 '시간 여행 디버깅'을 시연하기 위해서였습니다. 이 라이브러리는 Flux 패턴을 사용하도록 설계되었지만 몇 가지 함수형 프로그래밍 원칙이 적용되었습니다. 스토어 인스턴스 대신 불변 업데이트를 수행하는 예측 가능한 감속기 함수를 사용할 수 있었습니다. 이렇게 하면 시간을 앞뒤로 넘나들며 다양한 시점에서 상태가 어떻게 보이는지 확인할 수 있습니다. 또한 코드를 더 간단하고 테스트 가능하며 이해하기 쉽게 만들 수 있습니다.
Redux는 2015년에 출시되었고, 다른 모든 Flux에서 영감을 받은 라이브러리를 빠르게 제쳤습니다. React 에코시스템의 고급 개발자들로부터 일찍 채택되었고, 2016년에 이르러 많은 사람들이 “React를 사용한다면 Redux도 사용해야 한다”고 말하기 시작했습니다. (솔직히 말해서, 이로 인해 많은 사람들이 Redux를 사용할 필요가 없는 곳에서 사용하게 되었습니다!).
또한 당시 React에는 기본적으로 업데이트된 값을 제대로 전달할 수 없는 레거시 Context API만 있었습니다. 업데이트된 값을 제대로 전달할 수 없었 습니다. 따라서 이벤트 이미터를 Context에 넣고 구독할 수는 있었지만, 일반 데이터에는 실제로 사용할 수 없었습니다. 그래서 많은 사람들이 전체 애플리케이션에 업데이트된 값을 일관되게 전달할 수 있는 방법인 Redux를 채택하기 시작했습니다.
Dan은 일찍이 "Redux는 코드를 작성하는 가장 짧은 방법이 아니라 예측 가능하고 이해하기 쉽게 만드는 것"이라고 말했습니다. 그 중 일부는 일관된 패턴을 갖는 것입니다(상태 업데이트는 리듀서에서 수행되므로 항상 리듀서 로직을 보고 상태 값이 무엇 인지 , 가능한 동작이 무엇인지, 어떤 업데이트를 일으키는지 확인합니다). 또한 로직을 컴포넌트 트리에서 이동하여 UI 가 대부분 "이런 일이 발생했습니다"라고만 표시하고 컴포넌트가 더 단순해지도록 하는 것입니다. 이와 함께 Reducer 및 Selector와 같은 "순수 함수"로 작성된 코드는 인수가 들어가고 결과가 나오며 다른 것은 볼 필요가 없는 등 이해하기 더 간단해집니다. 마지막으로 Redux의 디자인은 Redux Dev Tools를 활성화하여 전달된 모든 동작, 동작/상태에 포함된 내용, 각 동작에 대해 발생한 변경 사항을 읽을 수 있는 목록으로 표시합니다.
초기 Redux 패턴은 보일러플레이트가 심했습니다. 하나의 액션 유형(const ADD_TODO = “ADD_TODO”
), 액션 생성자 함수, 리듀서 케이스를 정의하기 위해 action/todos.js
, reducers/todos.js
, constants/todos.js
를 사용하는 것이 일반적이었죠. 또한 스프레드 연산자를 사용하여 변경 불가능한 업데이트를 직접 작성해야 했기 때문에 엉망이 되기 쉬웠습니다. Redux에서 서버 상태를 가져오고 캐시하는 것은 가능했지만, 가져오는 작업을 하기 위해 thunk를 작성하고 가져온 데이터로 액션을 디스패치하고 감속기에서 캐시 상태를 관리하려면 수동으로 작성하는 코드가 많이 필요했습니다.
Redux는 이러한 보일러플레이트에도 불구하고 인기를 얻었지만, 항상 가장 큰 우려의 대상이었습니다.
2017 : 경쟁자의 등장
2017-18년에는 상황이 바뀌었습니다. 많은 커뮤니티가 "클라이언트 측 상태 관리"보다는 "데이터 페칭 및 캐싱"에 더 집중했고, 그때 Apollo, React Query, SWR, Urql과 같은 데이터 페칭 라이브러리가 등장했습니다. 동시에, 컴포넌트 트리 아래로 업데이트된 값을 제대로 전달하는 새로운 React Context API도 나왔습니다.
이는 Redux가 예전만큼 "필수적"이지 않다는 것을 의미했습니다. 이제는 다양한 양의 중복(그리고 종종 더 적은 코드)으로 동일한 문제 중 많은 부분을 해결하는 다른 도구가 있었습니다. "보일러플레이트"에 대한 잦은 불만은 Redux를 사용하는 사람들에게 많은 우려를 불러일으켰습니다.
2019 : Redux Toolkit의 등장
그래서 2019년에 우리는 더 적은 코드로 동일한 Redux 로직을 작성하는 더 간단한 방법으로 Redux Toolkit을 빌드하여 출시했습니다. RTK는 여전히 Redux
(단일 스토어, 불변 업데이트 로직을 통해 리듀서에서 수행되는 상태 업데이트를 트리거하는 액션을 디스패치)이지만 더 간단한 API와 더 나은 기본 제공 동작을 제공합니다. 여기에는 React Query와 Apollo에서 영감을 받은 기본 제공 데이터 페칭 및 캐싱 라이브러리인 RTK Query도 포함됩니다.
오늘날 RTK는 Redux 로직을 작성하는 표준적인 방법 (opens in a new tab)입니다 . 모든 도구와 마찬가지로, RTK는 장단점이 있습니다. RTK는 Zustand보다 사용할 코드가 조금 더 많을 수 있지만, 앱 로직을 UI에서 분리하는 데 유용한 패턴도 제공합니다. Redux는 모든 앱에 적합한 도구는 아니지만, 여전히 React 앱에서 가장 널리 사용되는 상태 관리 라이브러리이며, 훌륭한 설명서가 있으며, 일관되고 예측 가능한 구조로 앱을 빌드하는 데 도움이 되는 많은 기능을 제공합니다.
왜 리덕스를 사용하는가?
Redux는 MVC패턴의 양방향 데이터 바인딩 문제를 해결하는 Flux 패턴을 기반으로 만들어진 전역 상태 관리를 위한 독립형 JS 라이브러리이다.
Redux가 제공하는 패턴과 도구를 사용하면 애플리케이션의 상태가 언제, 어디서, 왜, 어떻게 업데이트되는지, 그리고 해당 변경이 발생할 때 애플리케이션 로직이 어떻게 동작하는지 더 쉽게 이해할 수 있다. 더 간단히 전역상태 관리가 필요하며, 안정적인 데이터 흐름을 통해 상태를 예측 가능하게 관리하고 싶다면 Redux가 도움이 될 것이다.
언제 리덕스를 사용해야 하는가?
그렇다면 우리 프로젝트에 리덕스를 도입하는 목적은 무엇일까? 이하의 문제를 겪고 있다면 리덕스는 좋은 해결책이 될 수 있다.
- 프로젝트 내에 전역상태 관리를 위한 라이브러리가 필요하다.
- 순수함수를 활용해 상태 업데이트를 예측 가능하게 하고 싶다.
- 단방향의 데이터 흐름을 통해 안정적인 상태관리를 하고 싶다.
- React Dev tools를 활용해 상태 업데이트를 추적하고 디버깅 하기 쉽게 하고 싶다.
모든 앱에 Redux가 필요한 것은 아니다. 어떤 종류의 앱을 만들고 있는지 생각해보고, 작업 중인 문제를 해결하는 데 가장 적합한 도구가 무엇인지 결정해보자.
참고 자료
- Redux 공식문서 (opens in a new tab)
- Redux 공식문서 - A (Brief) History of Redux (opens in a new tab)
- Redux 공식문서 - Redux Essentials, Part 1: Redux Overview and Concepts (opens in a new tab)
- [10분 테코톡] 우디의 Flux Architecture (opens in a new tab)
다음 글 읽으러 가기 →