✓ 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 |