React 초기 상태 관리

- 2014년 당시 웹 개발 생태계에서는 MVC 패턴이 대세, Model(JS) - View(HTML) 간 서로 변경할 수 있는 양방향 데이터 바인딩 방식

-> 웹 애플리케이션이 비대해지고, 데이터의 양도 많아짐에 따라 상태 관리가 어려워짐

-> 문제를 인식한 페이스북 팀에서 단방향 데이터 바인딩 제안 : FLUX 패턴

 

FLUX 패턴

- 단방향 데이터 바인딩을 사용하는 아키텍처 디자인 패턴

- 데이터 흐름 Action -> Dispatcher -> Model -> View (View에서 다시 Action -> .. )

1) Action : { type, payload } - type: 어떠한 작업 / payload: 작업할 데이터

2) Dispatcher : Action -> Store 로 디스패치 수행

3) Store : 실제 상태 값, Action type 에 따라 상태를 변경할 수 있는 메서드 포함

4) View : Component, Store 에서 만들어진 데이터를 화면에서 렌더링, 사용자의 행위에 Action 호출

type StoreState = {
	count: number
}

type Action = {
	type: "add";
    payload: number;
}

function reducer(prevState: StoreState, action: Action) {
	const { type: ActionType } = action;
    
    if (ActionType === "add") {
    	return { coun: prevState.count + action.payload };
    }
    
    throw new Error(`Unhandled Action [${ActionType}]`);
}

export default function App() {
	const [state, dispatcher] = useReducer(reducer, { count: 0 }); 
    
    function handleClick() {
    	dispatcher({ type: "add", payload: 1 });
    }
    
    return (
    	<div>
        	<h1>{state.count}</h1>
            <button onclick={handleClick}> + </button>
        </div>
    );
}

 

단방향 데이터 바인딩

- 사용자의 입력에 따라 데이터를 갱신하고, 화면을 업데이트 해야하는지 코드로 작성 -> 코드 양 증가

- 데이터의 흐름이 단방향으로 제한되기 때문에 흐름 추적 용이, 마찬가지로 코드 가독성 향상

- 리액트 : 대표적인 단방향 데이터 바인딩 라이브러리

 

Redux

- https://mykuromi.tistory.com/13

- FLUX 구조를 구현하기 위해 만들어진 라이브러리

- Elm 아키텍처 도입 : 웹페이지를 선언적으로 작성하기 위한 언어, 데이터의 흐름을 Model(상태), View(HTML), Update(Type, Logic)로 분류

- 등장과 동시에 리액트 생테게에 큰 영향 -> Global state 를 하위 컴포넌트에 전파해 props drilling 문제 해결

- dispatcher, selector, action type 선언 등 복잡한 구조, 보일러 플레이트가 많아지는 문제 -> Redux toolkit 으로 간소화

 

Context API

- https://react.dev/reference/react/useContext

- React 버전 16.3 에서 출시 (이전에는 getChildContext 로 구현)

- props drilling 대신 Context provider 사용

 

Hook, React Query, SWR
- React 버전 16.8 에서 함수형 컴포넌트 Hook API 추가(Custom hooks ..) -> state 재사용 용이 ⭐️

- 이전에는 없던 상태관리 방식 등장 : React Query, SWR -> API 호출에 따른 상태 관리, HTTP 요청에 특화

- React Query : https://tanstack.com/query/latest/docs/framework/react/overview

- SWR : https://swr.vercel.app/ko

 

다양한 상태관리 라이브러리 등장

- Redux 라이브러리와는 다르게, 훅을 활용해 상태를 작고 효율적으로 관리 (React ver 16.8 이상 조건)

- 전역 상태 관리 패러다임에서 벗어나 개발자가 지역적으로 상태 관리

- Recoil : https://recoiljs.org

- Jotai : https://jotai.org

- Zustand : https://zustand-demo.pmnd.rs

- Valtio : https://valtio.pmnd.rs

 


 

🖤 상태 관리 범위 : useState / useReducer < Context < 상태 관리 라이브러리

 

컴포넌트 내부 상태 관리

- 가장 기본적인 상태 관리 방법

- useState : https://react.dev/reference/react/useState

- useReducer : https://react.dev/reference/react/useReducer

- useState 나 useReducer 를 기반으로 한 사용자 정의 훅을 통해 컴포넌트 상태 관리

- useState 가 선언될 때마다 state 초기화 되므로, 이는 곧 해당 컴포넌트 내에서만 유효한 지역 상태(local state)

 

useState 를 사용해서 컴포넌트 외부에서 상태 관리하기

1) Parent 컴포넌트로 state 를 끌어올리기 :  여러 컴포넌트가 동일한 상태를 사용할 수 있지만 코드가 복잡해짐  ex) parent 에서 handleClick 같은 함수 drilling

2) 컴포넌트 외부 별개의 파일에서 state 를 관리하는 경우, 리렌더링이 되지 않는 문제 발생 (컴포넌트의 state를 업데이트 치는게 아니므로)

-> useState 로 컴포넌트 외부 상태까지 관리하기에는 코드 가독성이 저해되거나, 리렌더링 문제가 발생

 

전역 상태 관리 조건

* 상태 관리 라이브러리에서 공통적으로 구현된 부분

- 컴포넌트 외부에 위치, 여러 컴포넌트가 함께 사용

- 이 상태를 참조하는 컴포넌트는 상태 변화를 감지하고 리렌더링 되어야 함 -> callback / subscribe 

- 상태가 원시값이 아니라 객체인 경우에는 객체에서 감지하는 값이 변화되어야 리렌더링

-> state, initialState, get, set, callback, subscribe, unsubscribe(cleanup) 필요

- cf) useSubscribtion : 리액트 외부에서 관리하는 값 추적, 리렌더링 훅 - https://resthooks.io/docs/api/useSubscription

 

Context 로 상태 관리

- useState 만 사용하는 것 보다 좀 더 넓게 상태 관리

- Context provider 를 지역적으로, 또는 여러 번 사용해서 컴포넌트 레이어별로 상태 관리 가능

 

상태 관리 라이브러리를 사용한 상태 관리

특징 Recoil Jotai Zustand MobX Redux
API https://recoiljs.org/ko/ https://jotai.org https://zustand-demo.pmnd.rs https://mobx.js.org/README.html https://redux-toolkit.js.org
https://redux.js.org
examples https://app.sideguide.dev/recoil/tutorial/ https://tutorial.jotai.org/examples/textLenght https://github.com/pmndrs/zustand https://mobx.js.org/README.html#a-quick-example https://redux-toolkit.js.org/tutorials/quick-start
설계 철학 간단한 상태 관리와 효율적인 상태 업데이트 간단하고 작으며 원자 단위 상태 관리 불변성을 유지하지 않는 단순 상태 관리 반응형 상태 관리와 옵저버 패턴 전역 상태 관리와 상태의예측 가능성
상태 관리방식 Atom Selector 사용 Atom 사용 Immutable 아닌 mutable 상태 관리 Observable 상태 단일 스토어와 Reducer통한 상태 관리
데이터 구조 트리 구조 트리 구조 플랫 구조 (얕은 복사) 트리 구조 트리 구조
성능 높은 성능과 효율적인 리렌더링 높은 성능과 적은 메모리 사용 매우 높은 성능과 빠른 상태 업데이트 높은 성능과 효율적인 상태 업데이트 중간 정도의 성능, 대규모애플리케이션에 적합
리렌더링제어 Atom 단위의 리렌더링제어 Atom 단위의 리렌더링 제어 선택적 리렌더링 제어 Observable 통한 효율적인 리렌더링 필요할 때만 리렌더링
미들웨어지원 제한적 제한적 지원 - immer, persist 없음 다양한 미들웨어 지원 - Thunk / Saga
커뮤니티와 생태계 작음 작음 작음 작음 매우 
개발자 도구 기본 제공 기본 제공 없음 MobX DevTools 제공 Redux DevTools 제공
동시성 관리 기본 제공 기본 제공 없음 기본 제공 기본 제공
설정 복잡도 간단 매우 간단 매우 간단 중간 정도 복잡할  있음
TypeScript 지원 기본 제공 기본 제공 기본 제공 기본 제공 기본 제공
학습 곡선 낮음 낮음 낮음 중간 정도 중간에서 높음
애플리케이션 규모 중소규모 중소규모 중소규모 중소규모에서 대규모까지 대규모

- 사용자 추이 : https://json-5.com/awesome-react-state-management

 

Recoil

- 페이스북에서 만든 상태 관리 라이브러리

- 2020년에 출시했지만 아직 정식 출시 X - npm 0.7.7 (2024.08)

- RecoilRoot : 애플리케이션 최상단에 감싸서 스토어 생성 - createContext, 감싸지 않은 컴포넌트에서는 접근 불가 

- notifyComponents(store, storeState, treeState) : 값이 변경되면 콜백을 실행해 하위 컴포넌트에 상태 변화를 알림

- atom : Recoil의 최소 상태 단위, unique 해야함 

- selector : 하나 이상의 atom을 조합할 수 있는 API

- useRecoilValue(statementsAtom) : atom 의 값을 읽어오는 메서드

- useRecoilState(recoilState) : return [useRecoilValue(recoilState), useSetRecoilState(recoilState)]

 

Jotai

- Recoil 의 atom 에서 영감을 받은 라이브러리

- atom 으로 하나 이상의 상태 생성, selector 필요 X

- Recoil 에서는 atom 의 key 를 지정해줘야 했지만, Jotai 에서는 별도의 key 가 필요하지 않음 -> 객체의 참조를 WeakMap 에 보관해 관리

- useAtomValue

- Recoil 과는 다르게 컴포넌트 루트 레벨에 Context가 존재할 필요 X -> Context 가 없으면 Provider 가 없는 형태로, 루트 레벨에 기본 스토어 생성

- Provider 를 사용하면 Context 처럼 각 Provider 별로 atom 관리 가능

- renderedIfChanged 를 통해 최신 atom 렌더링

- Recoil 에 비해 간결한 API

 

Zustand

- 리덕스에 영감을 받아 만든 라이브러리

- 하나의 중앙 집중 스토어에서 상태 관리

- ./src/vanila.ts : 프레임워크 독립적, 순수 자바스크립트 환경에서 사용 가능, 리액트에서 사용시 별도 세팅(useStore)

- 컴포넌트 내/외부 store 생성 가능, 외부 생성시 createStore

- 리덕스에 비해 짧은 코드, 가볍고 편리

 

 

'React' 카테고리의 다른 글

React strict mode  (0) 2024.07.10
Deep Dive : useReducer, useImperativeHandle  (0) 2024.07.08
240706 리액트 스터디 : useRef  (0) 2024.07.07
240629 리액트 스터디 : useRef, useContext  (0) 2024.06.29
Deep Dive : useRef, useContext  (0) 2024.06.29

+ Recent posts