본문으로 건너뛰기
비공식 베타 번역

이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →

Redux의 (간략한) 역사

2011: JS MVC 프레임워크

AngularJS, Ember, Backbone 같은 초기 JavaScript MVC 프레임워크에는 문제점이 있었습니다. AngularJS는 "컨트롤러"와 템플릿의 분리를 강제하려 했지만, 실제로는 템플릿에 <div onClick="$ctrl.some.deeply.nested.field = 123"> 같은 코드를 작성하는 걸 막을 수 없었습니다. 한편 Backbone은 이벤트 에미터 기반이었습니다. 모델, 컬렉션, 뷰 모두 이벤트를 발생시킬 수 있었죠. 모델이 "change:firstName" 이벤트를 발생시키면 뷰가 이를 구독했습니다. 하지만 아무 코드나 이런 이벤트를 구독해 추가 로직을 실행할 수 있었고, 이는 또다른 이벤트를 촉발할 수 있었습니다.

이로 인해 디버깅과 유지보수가 매우 어려워졌습니다. 하나의 모델에서 단일 필드를 업데이트하는 것만으로도 수십 개의 이벤트가 발생하고 앱 전반에 로직이 실행될 수 있었으며, 어떤 템플릿이든 언제든지 상태를 변경할 수 있어 상태 업데이트 시 어떤 일이 벌어질지 예측하기 불가능했습니다.

2014: Flux

2012-2013년 경, React가 처음 공개되었을 때 페이스북은 이미 몇 년간 내부적으로 사용해 왔습니다. 그들이 마주친 문제 중 하나는 UI의 여러 독립적인 부분이 "읽지 않은 알림이 몇 개인지"와 같은 동일한 데이터에 접근해야 했지만, Backbone 스타일의 코드를 사용할 때는 이 로직을 일관되게 유지하기 어려웠다는 점이었습니다.

페이스북은 결국 "Flux"라는 패턴을 고안했습니다: PostsStoreCommentsStore 같은 여러 싱글톤 스토어를 생성하는 방식이었죠. 각 스토어 인스턴스는 Dispatcher에 등록되며, 스토어 업데이트를 트리거하는 유일한 방법은 Dispatcher.dispatch({type: "somethingHappened"})를 호출하는 것이었습니다. 이 평범한 객체를 "액션"이라 불렀습니다. 핵심 아이디어는 모든 상태 업데이트 로직을 반중앙화하는 것이었습니다. 앱의 무작위 부분에서 상태를 변이시킬 수 없게 되어 모든 상태 변경이 예측 가능해졌죠.

페이스북은 2014년경 이 "Flux 아키텍처" 개념을 발표했지만, 패턴을 구현한 완전한 라이브러리를 제공하지는 않았습니다. 이로 인해 React 커뮤니티에서 해당 패턴을 변형한 수십 개 의 Flux 영감을 받은 라이브러리가 탄생했습니다.

2015: Redux의 탄생

2015년 중반, Dan Abramov는 Redux라는 또 다른 Flux 영감을 받은 라이브러리를 개발하기 시작했습니다. 아이디어는 컨퍼런스 발표를 위한 "시간 여행 디버깅"을 시연하는 것이었죠. 이 라이브러리는 Flux 패턴을 사용하되 함수형 프로그래밍 원칙을 적용해 설계되었습니다. 스토어 인스턴스 대신 불변 업데이트를 수행하는 예측 가능한 리듀서 함수를 사용할 수 있었습니다. 이를 통해 시간을 앞뒤로 이동하며 다양한 시점의 상태를 확인할 수 있게 되었습니다. 또한 코드를 더 직관적이고 테스트 가능하며 이해하기 쉽게 만들었죠.

Redux는 2015년 출시되어 다른 모든 Flux 영감을 받은 라이브러리를 빠르게 사라지게 했습니다. React 생태계의 고급 개발자들에게 초기에 채택되었으며, 2016년쯤에는 "React를 사용한다면 Redux도 반드시 사용해야 한다"는 말이 나올 정도였습니다. (솔직히 말해, 이로 인해 많은 사람들이 Redux가 필요하지 않은 곳에서도 사용하게 되었습니다!)

또한 당시 React에는 레거시 Context API만 존재했는데, 이는 사실상 동작하지 않았습니다: 제대로 업데이트된 값을 전달하지 못했죠. 따라서 Context에 이벤트 에미터를 넣고 구독하는 건 가능했지만, 일반 데이터에는 실제로 사용할 수 없었습니다. 이 때문에 많은 사람들이 업데이트된 값을 앱 전체에 일관되게 전달할 수 있는 방법으로 Redux를 채택하기 시작했습니다.

Dan은 초기에 "Redux는 코드를 가장 짧게 작성하는 방법이 아닙니다. 예측 가능하고 이해하기 쉬운 방식으로 만들기 위한 것입니다"라고 말했습니다. 이는 일관된 패턴을 갖추는 것(상태 업데이트는 리듀서에서 처리하므로, 상태 값이 무엇이 될 수 있는지, 가능한 액션은 무엇인지, 어떤 업데이트가 발생하는지 항상 리듀서 로직을 살펴보면 알 수 있음)과 관련이 있습니다. 또한 로직을 컴포넌트 트리 밖으로 이동시켜 UI가 주로 "이런 일이 발생했다"고 선언하고 컴포넌트를 단순화하는 것입니다. 여기에 더해 리듀서와 셀렉터 같은 "순수 함수"로 작성된 코드는 이해하기 더 직관적입니다: 인자가 들어가고 결과가 나오며, 그 외에 볼 것은 없습니다. 마지막으로 Redux의 설계는 Redux DevTools를 가능하게 했으며, 이 도구는 디스패치된 모든 액션의 읽기 쉬운 목록, 액션/상태에 포함된 내용, 각 액션별로 발생한 변경 사항을 보여줍니다.

초기 Redux 패턴은 특히 보일러플레이트가 많았습니다. 단일 액션 타입(const ADD_TODO = "ADD_TODO"), 액션 생성자 함수, 리듀서 케이스를 정의하기 위해 actions/todos.js, reducers/todos.js, constants/todos.js를 두는 것이 일반적이었습니다. 또한 스프레드 연산자로 불변 업데이트를 수동으로 작성해야 했는데, 이는 실수하기 쉬웠습니다. 사람들은 Redux에서 서버 상태를 가져와 캐싱하기도 했지만, 가져오기를 수행하는 썽크 작성, 가져온 데이터로 액션을 디스패치, 리듀서에서 캐시 상태 관리까지 수동으로 작성해야 하는 코드가 많았습니다.

Redux는 이런 보일러플레이트에도 불구하고 인기를 얻었지만, 이는 항상 가장 큰 우려 사항이었습니다.

2017: 생태계 경쟁

2017-2018년이 되면서 상황이 바뀌었습니다. 커뮤니티 상당수는 "클라이언트 측 상태 관리"보다 "데이터 가져오기와 캐싱"에 더 집중하기 시작했으며, 이때 Apollo, React Query, SWR, Urql 같은 데이터 가져오기 라이브러리가 부상했습니다. 동시에 새로운 React Context API가 등장하여 컴포넌트 트리 아래로 업데이트된 값을 제대로 전달할 수 있게 되었습니다.

이는 Redux가 과거처럼 "필수" 요소가 아니게 되었음을 의미했습니다. 이제 동일한 문제를 해결하는 다양한 도구들이 등장했으며(중복 정도는 다양하고 종종 코드량이 더 적음), "보일러플레이트"에 대한 빈번한 불만도 Redux 사용자들에게 큰 우려를 낳았습니다.

2019: Redux Toolkit

그래서 2019년에 우리는 동일한 Redux 로직을 더 적은 코드로 작성할 수 있는 간편한 방법으로 Redux Toolkit(RTK)을 개발해 출시했습니다. RTK는 여전히 "Redux"입니다(단일 스토어, 액션 디스패치로 리듀서에서 불변 업데이트 로직을 통해 상태 변경). 하지만 더 간단한 API와 향상된 기본 동작을 제공합니다. 여기에는 React Query와 Apollo에서 영감을 받은 내장 데이터 가져오기 및 캐싱 라이브러리인 RTK Query도 포함됩니다.

오늘날 RTK는 Redux 로직 작성의 표준 방식입니다. 모든 도구와 마찬가지로 트레이드오프가 있습니다. RTK는 Zustand보다 사용 시 코드가 약간 더 많을 수 있지만, 앱 로직을 UI에서 분리하는 유용한 패턴을 제공합니다. Redux가 모든 앱에 적합한 도구는 아니지만, 여전히 React 앱에서 가장 널리 사용되는 상태 관리 라이브러리이며, 훌륭한 문서와 일관되고 예측 가능한 구조로 앱을 구축하는 데 도움이 되는 다양한 기능을 제공합니다.

추가 정보