[React] 상태관리 라이브러리 종류와 사용

정호·2024년 7월 1일
0

TIL

목록 보기
17/17


리액트에서 가장 중요한 라이브러리가 바로 상태 관리 라이브러리이다.

단방향 바인딩의 특징과 문제점

리액트(React)는 주로 단방향 데이터 바인딩을 사용합니다. 이는 부모 컴포넌트에서 자식 컴포넌트로 stateprops 형태로 전달할 수 있지만, 반대로 자식 컴포넌트에서 부모 컴포넌트로 props를 직접 전달할 수는 없다. 자식 컴포넌트가 부모 컴포넌트의 state를 변경하려면, 부모 컴포넌트의 setState 함수를 props로 전달받아 사용해야한다.

이 방식은 컴포넌트 간 데이터 흐름을 명확하게 유지하는 장점이 있지만, 복잡한 애플리케이션에서는 props를 여러 단계에 걸쳐 전달하는 문제가 발생할 수 있다. 이를 Props Drilling이라고 하며, 프로젝트의 규모가 커질수록 props의 깊이가 증가하여 불필요한 리렌더링을 초래할 수 있다.

모든 state를 props로 관리하는 것이 비효율적인가?

컴포넌트의 재사용성과 의존성 분리 측면에서 props를 사용하여 state를 관리하는 것은 유용합니다. 그러나 프로젝트 전반에서 사용되는 데이터는 전역 상태로 관리하고, 이를 보완하는 부가적인 state만 props로 처리하는 것이 더 효율적이다.
--> 코드의 유지보수성을 높이고 불필요한 리렌더링을 줄이는 데 도움이 됩니다.

상태 관리 라이브러리의 선택

대표적인 라이브러리로는 Redux, Tanstack-query, Recoil 등이 있습니다. 각 라이브러리의 장단점을 살펴보고, 프로젝트의 요구 사항에 맞게 적절한 라이브러리를 선택하는 것이 좋습니다.


1. Redux

Redux는 데이터가 단방향으로 흐르는 구조
이 구조는 애플리케이션의 상태 관리와 데이터 흐름을 예측 가능하고 명확하게 만든다. Redux의 주요 구성 요소는 action, reducer, selector, store로 이루어집니다. 이러한 구성 요소들은 유지보수에 유리한 장점을 제공하지만, 상태의 개수가 적더라도 많은 보일러플레이트 코드가 필요하다는 단점이 있다.

Redux의 단방향 데이터 흐름

  • Action: 상태 변화를 요청하는 객체로, type 속성과 필요한 추가 데이터를 포함합니다.
  • Reducer: action을 처리하여 새로운 상태를 반환하는 순수 함수입니다.
  • Store: 애플리케이션의 상태를 저장하는 중앙 집중식 저장소입니다. 모든 상태 변경은 store를 통해 이루어집니다.
  • Selector: store에서 필요한 상태를 추출하는 함수입니다.

중앙 집중식 상태 관리

Redux는 모든 상태를 중앙 집중식으로 관리
하나의 store에 모든 상태를 저장하고, 애플리케이션의 외부 요소로 간주됩니다. 따라서 리액트 내부에서 직접 접근할 수 없으며, 상태 변경은 반드시 action 객체를 통해 이루어집니다.

비동기 데이터 처리

Redux 자체는 비동기 데이터를 처리하는 기능을 제공하지 않음
비동기 작업을 처리하려면 redux-thunkredux-saga와 같은 별도의 미들웨어를 추가로 사용해야한다. 이러한 미들웨어는 비동기 로직을 관리하고, 이를 Redux의 단방향 데이터 흐름에 통합할 수 있게 해준다.

Redux의 장단점

장점

예측 가능성: 상태 변경이 단방향으로 일어나고, 모든 상태 변화가 store에 집중되기 때문에 예측 가능한 결과를 얻을 수 있다.
디버깅 용이성: Redux는 하나의 store와 객체 트리를 사용하여 상태를 관리하기 때문에 디버깅이 용이합니다. 상태의 변화는 action 객체를 통해서만 이루어지며, 이로 인해 상태 변화를 쉽게 추적할 수 있다.
확장성: 보일러플레이트 코드가 많다는 단점에도 불구하고, Redux의 구조는 애플리케이션 확장과 유지보수에 유리하다.

단점

보일러플레이트 코드: 상태의 개수가 적더라도 action, reducer, store 등을 설정하는 많은 양의 보일러플레이트 코드가 필요하다.
비동기 데이터 처리: 비동기 작업을 처리하기 위해 추가적인 라이브러리나 미들웨어가 필요하다.

Redux는 엄격한 데이터 관리 방식을 채택하여 보일러플레이트 코드가 많아지는 단점이 있지만, 이는 확장성과 디버깅의 용이성 측면에서 큰 강점으로 작용합한다. 모든 상태 변경이 예측 가능하고 추적 가능하기 때문에 복잡한 애플리케이션에서도 안정적으로 상태를 관리할 수 있다.


2. Mobx

MobX는 사용이 간편하고 코드 양이 적다는 장점을 가지고 있지만, Redux에 비해 사용률이 낮다.

MobX의 특징

불변성에 대한 자유로움: MobX는 Redux와 달리 불변성(immutability)에 엄격하지 않습니다. 상태를 직접 변경할 수 있으며, 상태 변경 시 자동으로 반응형 업데이트가 일어난다. 이는 개발자가 불변성에 신경 쓰지 않아도 되므로 구현이 용이하다.

다수의 Store: MobX는 여러 개의 store를 사용할 수 있습니다. 이는 상태를 논리적으로 분리하여 관리할 수 있는 유연성을 제공한다. 하지만 상태 변경 시 여러 store에 영향을 미칠 수 있어 복잡한 프로젝트에서는 관리가 어려울 수 있다.

액션 없이 상태 변경: MobX는 Redux와 달리 action 객체 없이도 상태를 업데이트할 수 있다. 이는 구현을 간단하게 하지만, 테스트와 유지보수 측면에서는 문제를 일으킬 수 있다.

사용률이 낮은 이유

  1. 테스트와 유지보수
    MobX의 자유로운 상태 변경 방식은 초기 개발 단계에서는 편리하지만, 프로젝트가 커지고 복잡해질수록 상태 관리가 어려워진다.

  2. 확장성
    대규모 프로젝트에서는 Redux의 구조화된 접근 방식이 더 적합합니다. MobX는 작은 규모의 프로젝트에서는 효율적이지만, 대규모 프로젝트에서는 유지보수와 확장성이 문제가 될 수 있다.

  3. 커뮤니티와 지원
    MobX는 상대적으로 덜 사용되므로 지원과 자료가 제한적일 수 있다.


3. contextAPI

컴포넌트 트리 전체에서 데이터를 전역적으로 공유할 수 있도록 한다. 리액트가 제차적으로 가지고 있다.
정적인 데이터 위주로 처리하거나 업데이트가 자주 발생하지 않을 때 사용하기 적합하다.

Context API의 사용 사례

정적인 데이터 처리: Context API는 주로 테마, 사용자 인증 정보, 언어 설정 등과 같이 변경되지 않거나 드물게 변경되는 정적인 데이터를 처리하는 데 적합하다.
전역 상태 관리: 여러 컴포넌트에서 공통적으로 사용하는 상태를 전역적으로 관리할 수 있다.

Context API의 한계

복잡한 업데이트 처리의 비효율성: Context API는 복잡한 업데이트를 처리할 때 비효율적일 수 있다.
Context의 Provider로 감싸진 컴포넌트들이 상태의 변화를 감지할 때, 실제로 업데이트가 필요하지 않은 컴포넌트들까지도 리렌더링됩니다. 이는 성능 저하로 이어질 수 있다.


Recoil: Context API의 보완
이러한 한계를 보완하기 위해 나온 것이 Recoil 라이브러리입니다. Recoil은 리액트의 상태 관리를 더 효율적으로 처리할 수 있도록 도와줍니다.

Atom과 Selector: Recoil은 상태를 atom이라는 단위로 관리하며, selector를 통해 파생 상태를 생성하고 관리합니다. 이는 상태의 변경이 필요한 최소한의 컴포넌트만 리렌더링되도록 하여 성능을 최적화합니다.
더 나은 상태 추적: Recoil은 복잡한 상태 변화와 의존성을 효율적으로 관리할 수 있도록 도와줍니다. 상태가 변경될 때마다 의존성을 추적하여 필요한 부분만 업데이트합니다.

3. Recoil

Atom과 Selector라는 두 가지 주요 개념을 통해 상태를 관리한다.

Atom

Atom은 상태의 단위로, 고유한 키값을 통해 구분

특정 Atom을 구독하고 있는 컴포넌트들만 상태 변경 시 선택적으로 리렌더링된다.

Selector

Selector는 Atom의 상태 변화를 다루는 순수 함수

비동기 처리 및 데이터 캐싱 기능을 제공하여 비동기 데이터를 다루기에 용이하다.

Recoil의 주요 특징

  1. 보일러플레이트 감소

Recoil은 단순한 get/set 인터페이스를 통해 상태를 공유할 수 있어 보일러플레이트 코드가 없다.

  1. 동시성 모드 지원

Recoil은 React의 동시성 모드와 양립할 가능성이 있어, 더 나은 성능과 사용자 경험을 제공한다.

  1. 코드 스플리팅

상태를 분산적으로 둘 수 있어 코드 스플리팅이 가능하며, 로딩 시간을 개선할 수 있다.


라이브러리 병합 사용 사례

다른 라이브러리와 병합하여 사용할 수 있다.

💡Rest API와 함께 사용하는 경우

Tanstack-Query: API로부터 데이터를 불러오고 저장, 자동화하는 데 사용
Recoil: 상태 관리 용도로 사용하여 미니 리덕스처럼 동작

💡GraphQL API와 함께 사용하는 경우

Apollo-client: GraphQL API와의 통신을 담당
Recoil: 상태 관리 용도로 사용

코드 스플리팅

코드에서 당장 필요한 부분만 로딩하고, 나중에 필요할 때 추가로 로드함으로써 초기 로딩 시간을 개선하는 방법

사용자 경험을 개선하고, 애플리케이션의 성능을 최적화할 수 있다.

profile
열심히 기록할 예정🙃

0개의 댓글