상태 설계는 만드는 개발자마다 중요하게 생각하는 지점이 갈릴 수도 있고 한번 설계되면 프로젝트를 새로 만들지 않는 이상 고치기가 쉽지 않아서 깊은 고려를 하고 시작해야 되는 부분.
Redux가 여전히 대세로 쓰이고 있고 많은 프로젝트에서 전역 상태가 무분별하게 사용되고 있다. 점차 전역 상태 라이브러리를 안쓰는 게 좋다는 흐름이 생기고 있고 리액트 팀에서는
Recoil
을 만들어서 기존의 전역 상태 라이브러리를 대체 하려고 하고 있다.이제는 전역 상태보다는 지역 별로 잘 분리된 상태관리가 권장되고 있다.
react는 상태를 기반으로 ui를 제어하는 방식의 프론트엔드 라이브러리
상태(state)
- 컴포넌트 안에서 관리 되고 시간이 지나면서 바뀌는 동적인 데이터
- State는 해당 State를 기반으로 동작되는 모든 컴포넌트의 상위 컴포넌트에 존재하는 컴포넌트에 위치되는게 추천된다.
- 상태의 구분은 범위와 역할에 따라 나눌 수 있다.
범위
- 지역 상태 : 몇몇 컴포넌트에 국한되어 영향을 주는 것
- 전역 상태 : 많은 컴포넌트에 영향을 주는 것
역할
어플리케이션의 인터렉티브한 부분을 컨트롤하는
UI 상태
, 서버로부터 데이터를 가져와 캐싱 해놓는 서버 캐시 상태
, Form의 로딩, Submitting, disabled, validation 등등 데이터를 다루는 Form 상태
, 브라우저에 의해서 관리되고 새로고침해도 변함 없는 URL 상태
등등...✅상태관리가 필요한 이유 : prop으로 상태를 넘겨주면서 불필요한 컴포넌트들을 거치기도 하고 prop drilling 현상이 발생하기 때문
Context API
- useReducer와 같은 hook과 함게 사용하여 상태 관리
로직
- Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value prop이 바뀔 때마다 다시 렌더링됨
한계
context의 값이 변경되면 해당 값을 소비(
useContext
, consumer
)하는 모든 컴포넌트는 다시 렌더링 → 그렇기 때문에 큰 규모의 프로젝트일 경우에는 과도한 rerendering을 일으킬 수 있다는 문제가 있다. (→ Recoil이 나오는 계기가 됨)왜냐하면 Context API는 데이터 서브셋을 대상으로 변경을 감지하고 업데이트할 수 없기 때문이다.
Redux
- 지금까지 인기가 가장 많은 라이브러리
- But, 높은 점유율의 사용량에 비해 만족도가 낮다.
단점
- 초기 세팅 시 코드를 많이 작성해야한다.(간단한 구조일 때는 단점이 될 수 있다)
- React에 최적화 된 라이브러리가 아니었기에 사용에 불편함이 있다.(정확히 어떤 불편함이지..?)
- 비동기 처리를 위해
redux-saga
등이 필요하고 코드가 복잡해진다
- 구성 : action, reducer, selector, store
- "대학생 개발팀으로 이루어져 처음 개발해보는 친구들에게 Redux의 많은 코드들을 단시간에 배우기엔 러닝커브가 높고 복잡했기 때문에 Recoil을 선택하게 되었습니다."
- 기존에는 부모에서 자식의 자식의 자식까지 상태가 흘렀었는데, 리덕스를 사용하면 스토어를 사용하여 상태를 컴포넌트 구조의 바깥에 두고, 스토어를 중간자로 두고 상태를 업데이트 하거나, 새로운 상태를 전달받을 수 있음


Recoil
- Redux, MobX등의 서드파티 라이브러리와 다르게 오직 리액트 만을 위해서 생겨난 라이브러리
- 서드 파티 라이브러리들은 외부에서 상태를 관리한뒤 react-redux등을 통해서 리액트 라이프 사이클에 접근했지만 Recoil은 깊은 부분까지 리액트 상태를 직접 다룬다.(서드파티 라이브러리를 쓰게되면 리액트의 내부 스케줄러에는 접근할수 없어서 내부 성능 로직 개선이 어렵게 된다.)
- hook을 써본 React 개발자라면 쉽게 적응할 수 있고 전역 state 라는 개념만 이해한다면 atom 정도는 쉽게 활용할 수 있었다.(리덕스에 비해 러닝커브가 낮다!)
- context API 기반으로 구현되어 있으며, 함수형 컴포넌트에서만 사용가능
로직
- Atom이라는 작은 데이터 조각을 만들어서 해당 State 변화 시에 이를 참조하는 컴포넌트들만 리렌더를 시키는 단순한 로직
- 구조 : Atom, useRecoilState 훅, selector 등...
- Atom
- 상태의 단위
- Redux의
store
와 유사한 개념 atom
이 업데이트 되면, 해당atom
을 구독하고 있던 모든 컴포넌트들의 state가 새로운 값으로 리렌더된다.- key와 default로 구성
key
: unique 한 id (고유한 atom을 구분하기 위한 것 같다..!)default
: 기본으로 atom에 저장되는 값을 지정
코드 예시
// state.js export const cookieState = atom({ key: 'cookieState', default: [] });
- useRecoilState 훅
- atom의 state를
get
과set
할 수 있다. (useState와 방식도 기능도 비슷한 것 같다!) - ➕
useRecoilValue
(get만),useSetRecoilValue
(set만)
- selector
- 원래의
state
를 그냥 가져오는 것이 아닌,get
프로퍼티를 통해state
를 가공하여 반환할 수 있게 해준다.
코드 예시 (text를 받아 text.length로 가공하여 반환하는)
const charCountState = selector({ key: 'charCountState', // unique ID (with respect to other atoms/selectors) get: ({get}) => { const text = get(textState); return text.length; }, });
장점
- 비동기처리도 가능하다
- 캐싱 지원
단점:
- 얼마 안된 기술이라 생태계가 작다. 공식문서의 예제에 의존해야 하고 어떤식으로 써야하는 정형화된 구조나 패턴이 없어 아직은 큰 규모의 프로젝트에 도입하기가 조금 꺼려질 수 있다.
- devtools의 부재(솔직히 redux도 강의에서밖에 안써봐서 잘 모르겠다 큰 단점인지 분간이 안감)
참조
React Query
서버의 상태를 불러오고, 캐싱하며, 지속적으로 동기화하고 업데이트하는 작업을 하는 라이브러리
요청의 상태 및 정보를 훅을 통해 간단히 사용 가능
- 더 좋은 사용자 경험을 위해 캐시를 추가하거나 retry, 상태 최신화를 위한 refetch
- 큰 리소스 소모 없이 data fetch가 가능하다(api요청 및 관리를 효율적으로 할 수 있다)
- queryClient : api 호출 후 내부적으로 관리해주는 역할, api데이터를 캐싱함
- 동일한 api를 요청할 경우 queryClient에 저장돼있는 캐시데이터를 컴포넌트에서 사용가능하게 반환
useQuery
,useMutation
훅 제공
- useMutation은 post, put, delete → 서버 데이터 변경
- 잘 사용하게 도와주는 라이브러리다!
- useQuery의 세번째 인자로 캐시가 유지될 시간과 브라우저의 포커스에 따라 최신데이터 확인 여부를 확인하는 옵션 등을 설정 가능함


- mutate : 원하는 시점에 서버로 api요청을 보낼 수 있다
- persist data cache를 간단하게 구성 가능하다
- 로컬 스토리지 등 자바스크립트에서 접근할 수 있는 데이터 스토어에 쿼리클라이언트에 정보를 저장하여 사용자의 페이지 이탈, 브라우저 종료 후 재접속 시에 기존에 캐싱해놓은 정보를 사용하여 사용자에게 화면을 빠르게 보여줄 수 있다.
- 아직 실험적인 기능이기때문에 유의해야함!!
장점
- api별로 커스텀 훅을 만들어서 사용하면 api 관리에 용이(래핑해서 사용하면 함수 이름으로 직관적으로 파악할 수 있기 때문)
- api 요청 후 전처리와 후처리를 진행하기 용이
- 기존 리덕스사용에 비해 프로젝트의 복잡도 감소, 불필요한 로직 제거
단점
- 리코일과 비슷하게 나온지 얼마 안된 라이브러리라 정보가 부족하다는 점. 생태계가 작다.