모던 리액트 Deep Dive를 학습하고 정리한 글입니다.
5.1 상태 관리가 필요한 이유
- 상태 : 어떠한 의미를 지닌 값. 어플리케이션의 시나리오에 따라 지속적으로 변경될 수 있는 값.
- 상태로 분류될 수 있는 것들
- UI : 다크/라이트 모드, 라디오, input 등
- URL : 브라우저에서 관리되고 있는 상태 값
- form : 로딩 중인지, 제출되었는지, 접근이 불가능한지, 값이 유효한지 모두 상태로 관리됨
- 서버에서 가져온 값 : 대표적으로 API 요청
- 상태 관리의 역사
- Flux 패턴의 등장
- MVC 패턴은 모델과 뷰가 많아지면 복잡도 증가
- 이렇게 MVC 패턴을 적용한 어플리케이션이 비대해지고 상태도 많아짐에 따라 상태를 추적하기 어려워 짐
- 페이스북은 이 문제를 양방향 데이터 바인딩을 원인으로 봄
- 컨트롤러에서 모델로는 단방향이지만, (컨트롤러 → 모델) 뷰와 모델이 서로 양방향으로 변경시킬 수 있기 때문에 관리가 어려워짐
- 따라서 단방향으로 데이터 흐름을 변경하는 것을 제안 ⇒ Flux 패턴
- 단방향으로 하게 되면 데이터의 흐름을 추적하기 쉽고 코드 이해가 수월해짐
- Flux 패턴
- 💡 Action → Dispatcher → Model → View → Action …
- Action : 어떤 작업을 처리할 액션과 이 액션이 발생되었을 때 함께 포함시킬 데이터를 의미
- Dispatcher : Action을 Store로 보내는 역할. 콜백 함수 형태
- Store : 실제 상태에 따른 값과 상태를 변경할 수 있는 메서드를 가짐
- View : 컴포넌트. Store에서 만들어진 데이터를 화면에 렌더링하는 역할
- 💡 Action → Dispatcher → Model → View → Action …
- Flux 패턴의 등장
-
- Redux 등장
- 리덕스도 처음에는 Flux 구조를 구현하기 위해 만들어진 라이브러리 중 하나였다
- Flux 구조에 Elm 아키텍처를 도입한 것이 리덕스
- Elm : 웹 페이지를 선언적으로 작성하기 위한 언어
- 하나의 상태 객체를 Store에 저장 → 이 객체를 업데이트 하는 작업을 dispatch 하여 업데이트 수행 ⇒ 이러한 작업을 Reducer 함수로 발생시킴
- Reducer 함수 : 상태에 대해 완전히 새로운 복사본을 반환한 뒤, 새로운 복사본이 된 상태를 어플리케이션에 전파
- prop drilling 해결, connect만 쓰고도 Store에 접근할 수 있게 됨
- 반면, 보일러플레이트가 많음
- Context API
- 전역 상태를 하위 컴포넌트에 주입 가능 — Context Provider
- 하지만 상태 관리가 아닌 상태 “주입”을 도와주는 기능
- 렌더링을 막아주는 기능이 없음
- React Query, SWR
- fetch를 관리하는데 특화된 라이브러리
- API 호출에 대한 상태를 관리하기 때문에 HTTP 요청에 특화된 라이브러리라 볼 수 있다
- 캐시를 활용한다는 점에서 상태 관리 라이브러리보다는 제한적이지만 그래도 상태 관리 라이브러리라고 볼 수 있다
- fetch를 관리하는데 특화된 라이브러리
- Recoil, Jotai, Zustand, Valtio 등 상태 관리 라이브러리 등장
- 훅을 활용하여 작은 크기의 상태를 효율적으로 관리할 수 있게 됨
- Redux 등장
5.2 리액트 훅으로 시작하는 상태 관리
useState와 useReducer
- 컴포넌트 내부의 지역 상태 관리 가능
- 하지만, 훅을 사용할 때마다 컴포넌트별로 초기화 ⇒ 컴포넌트에 따라 다른 상태를 가질 수밖에 없다
- 해당 컴포넌트보다 상위에 상태를 만든 뒤 하위 컴포넌트로 뿌려줄 수 있기는 하다. 하지만 제한적
상태 관리 라이브러리 — Recoil, Jotai, Zustand
- Recoil, Jotai : Context와 Provider, 훅을 기반으로 가능한 작은 상태를 효율적으로 관리하는 것에 초점
- Zustand : 리덕스와 비슷하게, 하나의 큰 스토어를 기반으로 상태를 관리
- 이 큰 스토어는 Context가 아닌 스토어가 가지는 클로저를 기반으로 생성됨
Recoil (0.7.5 버전 기준으로 작성)
- 최소 상태 : Atom
- 아직 실험단계, 현재 메타에서 지원을 끊음
- Selector를 필두로 다양한 비동기 작업을 지원하는 API 제공 ⇒ 리덕스와 달리 추가적인 미들웨어를 사용하지 않더라도 비동기 작업 비교적 간단하게 처리 가능
- RecoilRoot
- Recoil을 사용하기 위해 RecoilRoot를 최상단에 선언해 두어야 함
- Context를 만들어 상태값을 저장하기 위한 스토어를 context에 생성해줌
- 스토어의 상태값에 접근할 수 있는 함수가 있음 - 상태 접근, 변경 가능
- 값이 변경되면 이 값을 참조하고 있는 하위 컴포넌트에 모두 알림
- Atom
- 상태를 나타내는 Recoil의 최소 단위
- key 값을 필수로 가짐 — 다른 atom과 구별되는 식별자. 유일한 값이어야 함
- Selector
- Atom 값을 바탕으로 새로운 파생 상태를 조립할 수 있음
- useStateSelector와 유사한 역할
- useRecoilValue
- atom의 값을 읽어오는 훅
- useRecoilState
- atom의 값을 가져고오 변경할 수 있는 훅
Jotai (1.8.3 버전 기준으로 작성)
- Recoil의 atom에서 영감을 받아 만들어짐
- 타입스크립트 기반으로 작성됨
- 상향식 (bottom-up) 접근법
- 리덕스처럼 하나의 큰 상태를 내려주는 것이 아니라, 작은 단위의 상태를 위로 전파하는 구조
- 리액트의 context의 문제점인 불필요한 리렌더링을 해결하고자 설계
- 메모이제이션이나 최적화를 시키지 않아도 리렌더링X
- Atom
- Jotai에서는 atom 하나만으로 상태와 파생 상태 모두 만들 수 있음
- 별도의 key가 필요 없음
- useAtomValue
- useAtom
- useState와 동일한 형태의 배열을 반환 [값, set]
Zustand(4.1.1 기준으로 작성)
- 리덕스를 바탕으로 만들어짐
- 하나의 스토어를 중앙 집중형으로 활용하여 스토어 내부에서 상태를 관리
- 미들웨어 지원 ⇒ 기본적인 상태 관리 작업 외 추가적인 작업 정의 가능 (sessionStorage 저장 등)
- 라이브러리 크기가 작은 편
- 리덕스보다 간단하고 빠르게 상태 정의 가능
- 타입스크립트 기반으로 작성됨
라이브러리 선택 시 maintainer가 많고 다운로드가 활발하며, 이슈 관리가 잘되고 있는지를 고려하는 것이 좋다.
'IT (프론트엔드)' 카테고리의 다른 글
Github Actions로 CI 구축 & 최적화 (CI 시간 단축, Chromatic 자동 배포, Github Bot으로 배포 주소 남기기) (0) | 2024.12.23 |
---|---|
Lexical Environment (렉시컬 환경)에 대해 알아보자 (+호이스팅, TDZ) (0) | 2024.12.01 |
CSR vs SSR: 웹 성능 최적화와 혼합 렌더링 방식, Hydration의 역할 (3) | 2024.10.13 |
프론트엔드 에러는 왜 추적해야 할까? (Sentry) (0) | 2024.08.18 |
Webpack 적용기 (0) | 2024.07.06 |