리덕스 아키텍처 Best practice
리덕스를 사용하고 있는 어플리케이션에서 모든 상태관리를 리덕스를 통해 해야 할까? 아니면 적절하게 어떤건 리덕스에, 어떤건 컴포넌트에 저장해놓고 사용하면 될까? 적절하게의 기준은 뭘까? 리덕스 툴킷 공식 홈페이지에서 제안한 리덕스 아키텍처 best practice에 대해서 알아보고자 한다.
리덕스란?
리액트에서 state는 두 가지 성격으로 나눌 수 있는데, 전역 state와 로컬 state로 나뉜다고 볼 수 있다. 리덕스는 전역 state(상태) 관리를 도와주는 툴로 가장 대중적으로 사용되는 라이브러리다. 그리고 리덕스 툴킷은 기존에 리덕스를 작성하기 위해 요구되었던 불편함들을 해소하고 리덕스를 작성하는 표준 방법을 제공하기 위해 만들어진 라이브러리다. 리덕스보다 사용이 간편하고 보일러플레이트도 더 적다는 장점이 있다. 하지만 state를 관리하는 기본 원리와 설계는 동일하다.
전역 state와 로컬 state의 차이
리액트에서 로컬 state는 컴포넌트가 들고 있는 state이다. 컴포넌트 자기 자신 또는 자기가 품고 있는 children에 한해서만 공유되는 state를 말한다. 전역 state는 여러 컴포넌트가 필요로 하는 state로, 유저 정보가 대표적이다. 유저 정보는 로그인 한 후 계속해서 유지되면서, 웹사이트의 여러 페이지를 방문하는 동안 유실되지 않아야 한다. 어떤 컴포넌트에서는 유저의 이름을 표시해주어야 하고, 어떤 컴포넌트에서는 유저의 개인정보를 모두 표시해줘야 할 수도 있다. 이렇게 여러 컴포넌트가 동일하게 필요로 하는 state는 전역에 저장해놓고 접근하기 편하게 만들어두는 것이 좋다. 그리고 이런 작업을 도와주는 것이 리덕스이다.
로컬 state를 사용할 때의 흔한 문제점
어플리케이션의 다른 부분에 위치한 여러 컴포넌트가 동일한 state(ex: 유저정보)를 공유하고 사용해야 할 때, 특히 그러한 state 요소가 다른 부분에 위치할 때 문제가 발생한다. 아래 이미지를 보면 Home컴포넌트가 User data와 Layout data를 가지고 있다. 그런데 Profile컴포넌트도 User data가 필요한 상황이다. 하지만 두 컴포넌트는 평행하게 놓여있어 state를 물려줄 수 있는 구조가 아니다.

이것을 리덕스를 사용하여 개선하면 아래와 같은 flow가 된다.

언제 전역으로 state를 관리하면 좋은지 Check list
만약 state를 전역으로 관리할지, 컴포넌트 레벨에서 관리할지 애매하다면 아래 체크리스트를 살펴보자. 만약 yes가 많다면 리덕스 스토어에 상태를 저장하는 것이 좋다.
- 어플리케이션의 다른 컴포넌트에서도 이 state를 사용하는지?
- 이 state를 기반으로 많은 파생 컴포넌트가 만들어지는지?
- 예를 들어 projectInfo state를 사용하는 페이지가 3개 이상인지?
- 예를 들어 projectInfo state를 사용하는 페이지가 많은 children 컴포넌트를 품고 있는지?
- 동일한 state가 여러 컴포넌트를 구동하는데 사용되는지? (ex : 유저 정보)
- 이 state를 캐시해두고 사용하고 싶은지?
- 예를 들어, 데이터를 재요청하지 않고 state에 있는 상태를 그대로 사용하고 싶은지?
- state를 업데이트하는 로직이 복잡한가?
- 앱의 중간-대규모 코드베이스가 있으며 많은 사람들이 공동으로 작업해야 하는가?
- 이 경우 리덕스를 사용하여 state를 관리하면 협업이 편해진다. store에 어떤 데이터가 관리되고 있는지 한 눈에 볼 수 있기 때문이다.
Form 데이터는 컴포넌트 레벨에서 관리해야
하지만 form데이터의 경우 보통은 로컬 state로 관리하는 것이 좋다. 사용자가 데이터를 편집하는 동안 폼 컴포넌트 내에서 데이터를 유지하고, 사용자가 작업을 마치면 (form을 submit하면) 그 때 리덕스 액션을 디스패치하여 전역 state를 업데이트하는 것이 좋다. 그리하여 form이 submit되고 난 후 사용자에게 보여줘야 할 새로운 데이터를 업데이트해주는 것이다.
* 즉 리덕스는 앱의 전역 상태를 관리하기 위해 사용되며, 대부분의 경우 폼 데이터와 같은 지역적인 상태는 컴포넌트 내에서 관리하고, 필요할 때 리덕스 스토어로 업데이트 한다. 이렇게 하면 상태 관리가 더 단순해지고 예측 가능해진다.
Dashboard UI향상에 도움이 되는 redux
대시보드처럼 다양한 수치 데이터를 그래프에 뿌려줘야 하는 경우에 리덕스를 사용하면 좋다. 특히 데이터가 자주 fetch되어야 한다면 말이다. 아래를 보면 대시보드페이지가 5초마다 데이터를 fetch한 후 setState함수를 통해 데이터를 업데이트해주고, 이 데이터를 자식 컴포넌트인 그래프 1~4컴포넌트에 물려주고 있다. 자식 그래프 컴포넌트들은 부모에게 받은 데이터를 그래프로 그리고 있다.
여기서 문제점은 setState가 일어날 때 부모 컴포넌트에서 UI리렌더링이 일어난다는 것이다. 자식 컴포넌트는 부모 컴포넌트가 리랜더링 될 때 함께 리랜더링이 된다. 원치않는 깜빡임이 일어날 수 있는 부분이다.
이럴 때 부모 컴포넌트가 data를 fetch받아 업데이트 되어야 할 데이터만 dispatch하여 store를 업데이트 해주는 역할만 하고
자식컴포넌트는 store에서 필요한 데이터를 각자 받아와서 뿌려주면 부모-자식컴포넌트 간 의존성이 떨어지고, 부모 컴포넌트에서 UI렌더링이 일어나지 않기 때문에 자식 컴포넌트에서도 불필요한 UI렌더링이 일어나지 않게 된다.
마치며
요즘엔 react-query와 같이 데이터를 관리하기에 간편하고 직관적인 라이브러리들이 많이 나왔다. 개인이 쓰기엔 DX도 좋고 사용이 쉽다. 하지만 프로젝트 규모가 크고, 공동작업하는 팀원이 많을 경우엔 이런 데이터 fetching라이브러리 만으론는 부족하다고 생각한다. 리덕스나와 같은 전역 데이터 저장소를 사용할 경우 어플리케이션에서 넓은 범위로 사용되는 중요한 데이터가 한 곳에 저장되기 때문에, 이 어플리케이션에 어떤 데이터가 있고, 어디에서 사용되고 있고, 어떤 함수를 통해 데이터가 업데이트 되고 있는지를 보기가 편하다. 때문에 여러 개발자가 중복되는 데이터를 다른 이름으로 저장해놓고 사용할 경우가 줄어들고 마찬가지로 중복되는 데이터 업데이트 로직을 작성할 경우도 줄어든다. 다양한 데이터 관리 라이브러리와 리덕스를 섞어서 사용하는 것도 좋은 방법인 것 같다.
참고
Redux Essentials, Part 2: Redux Toolkit App Structure | Redux
The official Redux Essentials tutorial: learn the structure of a typical React + Redux Toolkit app
redux.js.org
'프론트엔드' 카테고리의 다른 글
HTML Canvas로 이미지 편집기 만들기 (feat. Konva.js) (0) | 2024.08.26 |
---|---|
[HTML] 당신이 몰랐을 수도 있는 시맨틱 태그들 구조 편 (1) | 2023.11.17 |
[Javascript] Carousel 구현 (0) | 2023.02.13 |
[React] useRef, forwardRef 사용법 (0) | 2023.02.12 |
[React] React Hook이란? (0) | 2023.02.12 |