리액트 애플리케이션을 개발하면서 가장 중요한 부분 중 하나는 상태 관리 전략을 선택하는 것이다. 이 중에는 리액트가 제공하는 Context API 부터, Redux, Recoil, MobX 등 다양한 라이브러리가 이러한 필요를 충족시키기 위해 존재한다. 각각의 도구는 고유의 장단점이 있고 사용하는 방법이 있기에, 프로젝트의 요구 사항에 따라 올바른 도구를 선택하는 것이 중요하다. 리액트의 Context API 와 Redux로 대표되는 상태 관리 도구를 비교하여, 어떤 상황에서 어떤 도구를 사용하는 것이 가장 효과적인지 알아보자.
Context API
- 기본 개념
리액트 Context API 는 컴포넌트 트리 전체에 걸쳐 데이터를 전역적으로 공유할 수 있는 메커니즘을 제공한다. 이를 통해 특정 데이터를 여러 컴포넌트에 걸쳐 쉽게 접근할 수 있게 한다. Context API 는 본래 하위 컴포넌트로 계속해서 props를 전달하는 'Prop drilling' 의 문제를 해결하기 위해 고안되었으나, 일반적으로 상태관리 도구로 인식되어 있고, 그렇게 많이 사용되고 있다. 'createContext' 를 사용하여 Context 와 Provider 를 정의하며 'useContext' 를 이용하여 쉽게 Provider 내부의 값을 가져오고 변경할 수 있다.
- 데이터 변경시 동작
- 상태 업데이트: 상위 Context Provider 에서 값 업데이트가 발생한다.
- Context 전파: Provider 내부 value 속성이 업데이트 되면, 이 변경 사항은 모든 하위 컴포넌트로 전파된다. Context 내부의 모든 컴포넌트는 새로운 value 를 받아 리렌더링되는 트리거가된다.
- 컴포넌트 리렌더링: 변경된 Context 값을 구독하는 모든 컴포넌트는 새로운 값으로 리렌더링된다. 이 과정은 Provider 하위 모든 컴포넌트가 영향을 받는다.
- 장점
- 간결함과 접근성: 별도 라이브러리 없이 리액트만으로 상태 관리가 가능한다.
- 컴포넌트 간의 결합 감소: Provider 를 통해 하위 컴포넌트에게 상태를 전달할 수 있기에, 컴포넌트 간 직접적인 데이터 전달이 필요 없다.
- 단점
- 성능 문제: 상태가 변경될 때마다 Context를 사용하는 모든 컴포넌트가 리렌더링 되기에 대규모 애플리케이션에서 성능 저하를 일으킬 수 있다.
Redux
- 기본 개념
Redux는 애플리케이션의 상태를 중앙에서 관리하는 자바스크립트 라이브러리이다. Store 에 데이터가 저장되며, Action, Reducer를 통해 상태를 변경한다. 그리고 'connect' 또는 'useSelector' 훅을 사용하여 상태를 구독한다. Redux 는 예측 가능한 상태 관리를 가능하게 하며, 복잡한 UI 상태가 많은 대형 애플리케이션에 적합하다.
- 데이터 변경시 동작
- 액션 발행: 애플리케이션에서 특정 이벤트가 발생하면, 관련된 데이터를 포함하는 액션을 생성하고, 이를 dispatch 함수를 통해 발송한다.
- Reducer 처리: dispatch 된 액션은 스토어에 설정된 리듀서로 전달된다. 순수 함수인 리듀서는 액션을 받아 이전 상태와 함께 처리하여 새 상태 계산하여 반환한다.
- 상태 업데이트: 리듀서에서 반환된 새로운 상태는 애플리케이션의 단일 진실 원천 (single source of trurh) 역할을 하는 리덕스 store 에 저장된다.
- 구독자 알림: 스토어의 상태가 변경되면, 이전에 subscribe 함수를 통해 등록된 리스너들에게 변경 사항이 알려진다.
- 컴포넌트 리렌더링: 상태의 의존하는 컴포넌는 새로운 상태를 반영하여 리렌더링 된다. connect 또는 useSelector 훅을 통해 최적화된 리렌더링을 제공하며, 변경된 데이터를 구독하는 컴포넌트만 업데이트된다.
- 장점과 사용 예시
- 효율적인 상태 구독: 필요한 상태 부분만 구독하여 성능 최적화가 가능하다.
- 개발 도구와 미들웨어 지원: Redux DevTools 등의 개발 도구를 사용하여 상태 변화를 추적하고 디버깅할 수 있다.
- 단점과 복잡성
- 학습 곡선: Redux의 개념과 패턴을 익히는 데 시간이 필요할 수 있다.
- 보일러플레이트 코드: 상태 업데이트 로직을 작성하는 데 많은 코드가 필요할 수 있다.
요즘에는 리덕스의 보일러플레이트 코드를 크게 줄일 수 있도록 Redux Toolkit(RTK) 를 사용하고 있는 추세이다. RTK 는 Redux의 공식적인 권장 도구로, Redux 개발을 더 간편하고 효율적으로 만들어주는 여러 기능들을 제공한다.
Context API 와 성능 한계
리액트 Context API 는 매우 간단하고 직관적인 방법으로 컴포넌트 트리 전체에 데이터를 공급할 수 있다. 그러나 Context API는 특정 컨텍스트를 사용하는 모든 컴포넌트가 해당 컨텍스트의 데이터에 변화가 있을 때마다 리렌더링된다는 단점이 있다. 이는 Context 값을 내부에 존재하는 모든 컴포넌트는 Context 내의 어떤 값이 변경되었는지에 관계 없이 리렌더링 되기 때문이다. 이는 값의 변경이 컴포넌트가 담당하는 화면에 영향을 주지 않을 때에도 리렌더링이 발생할 수 있음을 의미한다.
Redux 와 Publish-Subscirbe (Pub-Sub) 패턴
반면, Redux 는 발행-구독 패턴을 사용하여 상태 관리의 성능을 향상시킨다. 컴포넌트는 저장소의 특정 부분만을 구독할 수 있고, 상태의 특정 부분이 변경되면, 그 부분에 대해 구독하고 있는 컴포넌트만 업데이트 되고 이는 성능 측면에서 다음과 같은 이점을 제공한다.
- 타겟 업데이트: Redux 는 저장소의 변경된 부분만을 감지하고, 이를 구독하는 컴포넌트만을 업데이트한다. 이는 불필요한 컴포넌트 리렌더링을 방지하여 리소스를 절약하고 성능을 향상시킨다.
- 선택적 리렌더링: useSelector 훅을 사용하는 경우, Redux 는 이전 상태와 새로운 값을 비교하여 실제로 값이 변경되었을 때만 컴포넌트가 업데이트되도록 한다. 이러한 메모이제이션 덕에 성능이 크게 향상 될 수 있다.
이처럼 Redux 는 발행-구독 패턴을 통해 상태 관리에서 더욱 세밀한 업데이트 제어가 가능하며, 이는 대규모 애플리케이션에서 불필요한 리렌더링을 줄이고 성능을 최적화하는데 크게 기여할 수 있다. Context API 는 사용이 간편하고 접근성이 높지만, 세밀한 업데이트 제어가 부족하기에 성능 문제를 일으킬 수 있기에 웹페이지 정도에서 또는 그리 복잡하지 않은 데이터를 다루면서 한정적으로 사용할때 적합할 수 있고, 복잡하고 상태가 자주 변경되는 대규모 웹 애플리케이션에는 Redux나 비슷한 구조를 가진 다른 상태 관리 도구를 사용하는 것이 더 적합할 수 있다.
이러한 상태관리가 필요한 이유는 모든 상태와 그 관리가 클라이언트에서 일어나기 때문이다. 그렇기 때문에 요즘 인기있는 Next.js 와 같은 Meta Framework 의 경우에는 서버 컴포넌트를 이용하여 데이터베이스에서 직접 데이터를 뽑아와서 아예 상태를 관리할 필요가 없도록 만들기도 하고, 또는 이러한 상태 관리 도구보다 URL 을 적극 활용하여 상태를 쿼리 파라미터에 담아두는 방법도 있는 것 같다. 그렇기에 위와 같이 Next.js 또는 URL 을 사용한다면 클라이언트 측의 상태 관리 중요도는 조금 줄어들면서, 그 관심사도 디스플레이할 데이터, 폼과 관련된 데이터, 또는 유저 인터렉션과 관련된 상태 정도로 좁혀지는 방향인 것 같다.
그렇다면 좋은 코드라는 관점에서도 서버사이드에서는 내려줄 데이터, 클라이언트 사이드에서는 서버에 전송할 데이터와 인터렉션 관련된 상태로 나눠지게 되면서, 둘 간의 관심사 분리가 일어나게 되고 모든 것을 클라이언트 사이드에서 관리하는 상태 관리 측면의 비효율도 줄어들게 된다면 Next.js 같은 서버 컴포넌트를 사용하지 않을 이유가 없을 것 같다..
Reference
State Managers Are Making Your Code Worse In React - https://www.youtube.com/watch?v=VenLRGHx3D4
Blogged Answers: Why React Context is Not a "State Management" Tool (and Why It Doesn't Replace Redux) - https://blog.isquaredsoftware.com/2021/01/context-redux-differences/
Passing Data Deeply with Context - https://react.dev/learn/passing-data-deeply-with-context
The Battle of React State Management: Redux Versus Context API - https://www.linkedin.com/pulse/battle-react-state-management-redux-versus-context-api-jane-brewer-suczf/
'React' 카테고리의 다른 글
React - useQuery 를 사용할 때 에러가 발생한다면..? (0) | 2024.07.11 |
---|---|
Suspense in React | 리액트 서스펜스 (0) | 2024.06.22 |
ErrorBoundary in React || 리액트 에러 바운더리 (0) | 2024.06.17 |
Why React? | 리액트 왜 사용하세요? (0) | 2024.05.14 |