Redux(리덕스)를 쓰는 이유와 사용 시 주의할 점에 대해 살펴봅니다.

장점 1: 상태의 중앙화 link

Redux는 스토어(Store)라는 이름의 전역 자바스크립트 변수를 통해 상태를 한 곳에서 관리하는데, 이를 중앙화라 합니다.

상태: 웹 사이트에서 현재를 위해 필요한 정보를 뜻합니다. 현재 로그인한 사용자 정보라든가, 현재 사용자가 보고 있는 이 어떤 것인지에 대한 정보가 이에 해당합니다. 상태는 그저 값에 불과하기 때문에 숫자, 문자열, 객체 등 어떠한 타입으로도 표현할 수 있습니다. 상태의 위치 또한 제한이 없습니다. 변수, HTML 엘리먼트의 data-* 애트리뷰트, (리액트(React)를 쓴다면) 리액트의 state 등 어느 곳에서나 존재할 수 있습니다.

상태의 중앙화로 인해 다음과 같은 이점을 얻을 수 있습니다:

  • 더 이상 웹 사이트의 상태를 어디에 둘지 고민하지 않아도 됩니다. 예를 들어, 로그인 사용자의 레벨을 상단 메뉴에도 출력하고 본문에도 출력해야 한다고 합시다. 이때 상태 정보를 어디에 두어야 할까요. 상단 메뉴 엘리먼트와 본문 엘리먼트에 각각 data-* 애트리뷰트로 레벨을 저장할까요? 아니면 전역 변수? 리액트를 쓴다면 문제는 더 복잡해지는데, 부모 컴포넌트로부터 props를 통해 단계 별로 물려받도록 구성해야 합니다.
  • 웹 사이트에 다시 방문했을 때를 대비해 쉽게 상태를 저장하거나 불러올 수 있습니다. 상태 정보가 여기저기 흩어져 있다면 각 상태마다 저장 기능을 구현해야 하는 불편함이 있습니다.

장점 2: 읽기 전용 상태 link

Redux는 상태를 읽기 전용으로 취급합니다. 상태를 변경하기 위해서는 상태 일부를 바꾸는 게 아니라 상태 전체를 갈아치워야 합니다.

이로 인해 다음과 같은 이점을 얻을 수 있습니다:

  • 상태가 읽기 전용이므로, 이전 상태로 돌아가기 위해서는 그저 이전 상태를 현재 상태에 덮어쓰기만 하면 됩니다. 이런 식으로 실행 취소 기능을 구현할 수 있습니다. 실제로 Redux Undo라는 라이브러리는 이런 식으로 실행 취소 기능을 제공합니다.

반면 다음과 같은 단점도 있습니다:

  • Redux는 상태를 읽기 전용으로 취급할 뿐, 실제 읽기 전용으로 만들어주지는 않습니다. 때문에 상태를 실수로 직접 변경하지 않도록 항상 주의해야 합니다. 이를 예방하기 위해 Immutable.js같은 라이브러리도 존재합니다.

장점 3: 부수 효과 없는 리듀서 link

Redux는 상태를 변경하는 도중 부수 효과(side effect)가 일어나지 않도록 코딩하기를 요구하며, 이와 함께 상태를 변경하려는 시도 자체를 복제, 저장, 전송할 수 있도록 자바스크립트 객체 형태로 구성하기를 강제합니다. 이때 부수 효과 없이 상태를 변경하는 함수를 리듀서(Reducer), 상태 변경을 어떻게 할 것인지에 대한 정보를 담은 자바스크립트 객체를 액션(Action)이라 합니다.

부수 효과: 함수가 실행될 때 함수의 매개변수가 아닌 다른 값에 따라 반환값이 바뀔 수 있는 것을 의미합니다. 바꿔 말하면, 부수 효과가 없는 함수는 매개 변수가 바뀌지 않는 한 언제나 동일한 결과를 반환해야 합니다. 함수 내부에서 XMLHttpRequest, fetch()를 사용할 경우 가져오는 데이터가 바뀌거나, 그 사이에 서버가 죽어 최종적으로 함수의 반환값이 바뀔 수 있으므로 부수 효과가 있다고 여깁니다. Math.random(), Date.now()를 사용하는 것 역시 함수를 언제 호출하냐에 따라 다른 결과를 반환하기 때문에 부수 효과가 있습니다1.

리듀서와 액션의 성질로 인해 다음과 같은 이점을 얻게 됩니다:

  • 액션만 가지고 있어도 상태가 어떻게 변화할지 완벽히 예상할 수 있습니다. 리듀서는 액션 말고 아무 것도 알 수 없으니, 액션이 무엇인지 알면 리듀서가 만들어내는 상태의 변화도 알 수 있기 때문입니다. 상태를 변경하는 도중 네트워크에 의해 영향을 받거나 예기치 않은 결과가 나오거나 하는 일이 없습니다.
  • 액션 일부를 제외하고 다시 실행하더라도 어떤 일이 벌어질지 예상하기 쉽습니다. 리듀서는 액션에 따라서만 움직이므로 마치 수학 공식처럼 작동하기 때문입니다. Redux DevTools Extension(리덕스 개발자 도구)의 skip 기능이 이러한 Redux의 성질을 효과적으로 이용합니다. 반면 리듀서 내부에서 Math.random() 따위를 호출해 부수 효과를 일으킬 경우, 리듀서에 동일한 액션을 전달해도 언제나 결과가 같음을 보장할 수 없습니다.
  • 전체 상태 정보를 넘기지 않고도 액션만 있다면 동일한 상태를 만들어낼 수 있습니다. 네트워크 상에서 서로 떨어진 기기를 동일한 상태로 맞출 수 있습니다.

그렇다고 다음과 같은 단점을 완전히 무시할 수는 없습니다:

  • 부수 효과 없이 웹 사이트를 개발할 수는 없습니다. 필연적으로 어딘가에서는 네트워크로부터 무언가를 fetch()하거나 Math.random()으로 랜덤 값을 만들어내야 합니다. 리듀서에서는 부수 효과가 존재할 수 없으니, 대신 바깥에서 fetch()Math.random()을 호출한 뒤 그 값을 액션에 담아둔 다음 리듀서를 호출하는 번거로운 과정을 거쳐야 합니다.
  • 어떤 코드에 부수 효과가 있는지 없는지 확인하는 것은 대단히 어려운 일입니다. 자바스크립트 자체가 부수 효과에 대해 민감하게 대응하도록 설게된 언어가 아니기 때문입니다. 당장 Math만 봐도 Math.random()같이 부수 효과를 일으키는 함수가 있는 반면, Math.sin()같이 부수 효과가 없는 것도 있습니다. 정말 부수 효과에 대해 민감하게 처리하고 싶다면 하스켈과 같은 함수형 프로그래밍 언어를 사용하는 것이 좋습니다.

또다른 장점: 리액트와의 유사성 link

리액트에는 상태(state)라는 개념이 존재합니다. 리액트의 상태는 Redux와 마찬가지로 읽기 전용이며, 일정한 방법(setState())을 통해서만 상태를 변경할 수 있도록 합니다. 이렇듯 서로 유사한 성질로 인해 Redux는 리액트의 상태 관리 도구로 애용되고 있습니다.

또한 Redux의 핵심 개발자 중 일부는 실제 리액트의 개발에 참여하고 있기도 합니다.

주의점: Redux는 개발자의 행위를 제약 link

리덕스는 일반적인 라이브러리나 프레임워크가 아닙니다. 리덕스만의 설계 철학과 함께, 철학을 쉽게 따를 수 있도록 각종 도구를 지원해주는 개발 도구입니다. 설계 철학을 따르는 건 그냥 라이브러리를 사용하는 것과는 완전히 다른 문제입니다. 개발자가 그 설계 철학에서 요구하는 대로 코딩의 방식을 바꾸어야 합니다. 실제로는 상태가 읽기 전용이 아니더라도 읽기 전용인 것처럽 취급해야 하며, 상태를 변경할 때(리듀서를 사용할 때) 부수 효과가 없도록 잘 만들어야만 합니다.

리덕스를 사용함으로 인해 강력한 장점을 얻는 것은 사실입니다. 그러나 리덕스의 철학에서 강제하는 방식으로 인하여 오히려 코드가 복잡해지거나 코딩하기 어려워질 수도 있습니다. 만약 앞서 언급한 Redux의 장점 중 일부만이 필요하다면 Redux 대신 다른 것을 찾아보시기 바랍니다. 다른 것 다 필요 없고 상태 관리를 중앙화하는 것만 있어도 된다면 Context를 사용하세요2 3.

  1. https://redux.js.org/recipes/structuring-reducers/prerequisite-concepts#note-on-immutability-side-effects-and-mutation

    Other side effects like generating unique IDs or timestamps in a reducer also make the code unpredictable and harder to debug and test.

  2. https://frontarm.com/james-k-nelson/when-context-replaces-redux/

    Redux provides a whole toolkit for managing state:

    • It comes with a time traveling debugger
    • It provides a middleware API, giving you access to tools like redux-sagas
    • Its React bindings prevent many unnecessary renders

    As you can see, context is not a replacement for Redux. Context won’t give you time traveling debugging, configurable middleware, or anything more than a way to get data from one place to another. If you want a tool to help you manage your state, then Redux is a great choice.

  3. https://blog.isquaredsoftware.com/2018/03/redux-not-dead-yet/#clearing-the-confusion-1

    Context also doesn’t give you anything like the Redux DevTools, the ability to trace your state updates, middleware to add centralized application logic, and other powerful capabilities that Redux enables.