리액트와 상태 관리 라이브러리 (2)
모던 리액트 Deep Dive: 리액트의 핵심 개념과 동작 원리부터 Next.js까지, 리액트의 모든 것 - 05장 리액트와 상태 관리 라이브러리 (2)
2. 리액트 훅으로 시작하는 상태 관리
useState와 Context를 동시에 사용해 보기
Context
생성: createContext 함수를 사용하여 컨텍스트를 생성한다. 이는 데이터를 전역으로 공유할 수 있는 컨테이너 역할을 한다.Provider
제공: 생성한 컨텍스트를 Provider로 감싸 하위 컴포넌트에 전달한다. 이렇게 하면 모든 하위 컴포넌트에서 해당 컨텍스트에 접근할 수 있다.useState
로 상태 관리: 각 컴포넌트 내에서 useState를 사용하여 필요한 상태 값을 관리한다.useContext
로 컨텍스트 값 가져오기: 컨텍스트를 사용하는 컴포넌트에서 useContext 훅을 사용하여 컨텍스트 값을 가져온다.
import React, { useState, createContext, useContext } from 'react';
// 1. createContext 함수를 사용하여 컨텍스트를 생성합니다.
const MyContext = createContext();
// 2. Provider로 컨텍스트를 감싸 하위 컴포넌트에 전달합니다.
function App() {
const [count, setCount] = useState(0);
return (
<MyContext.Provider value=8>
<ChildComponent />
</MyContext.Provider>
);
}
// 3. useContext 훅을 사용하여 컨텍스트 값을 가져옵니다.
function ChildComponent() {
const { count, setCount } = useContext(MyContext);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default App;
상태 관리 라이브러리 Recoil, Jotai, Zustand 살펴보기
Recoil
과Jotai
는Context
과Provider
, 그리고 훅을 기반으로 가능한 작은 상태를 효율적으로 관리하는 데 초점을 맞추고 있다.Zustand
는 리덕스와 비슷하게 하나의 큰 스토어를 기반으로 상태를 관리하는 라이브러리다.- 하나의 큰 스토어는
Context
가 아니라 스토어가 가지고 있는 클로저를 기반으로 생성되며, 이 스토어의 상태를 변경되면 이 상태를 구독하고 있는 컴포넌트에 전파애 리렌더링을 알리는 방식이다.
- 하나의 큰 스토어는
페이스북이 만든 상태 라이브러리 Recoil
Recoil
은 리액트를 만든 페이스북에서 만든 리액트를 위한 상태 관리 라이브러리다.
RecoilRoot
Recoil
을 사용하기 위해서는 RecoilRoot
를 애플리케이션의 최상단에 선언해 둬야 한다.
RecoilRoot
에서Recoil
에서 생성되는 상태값을 저장하기 위한 스토어를 생성하는 것을 확인할 수 있다.
RecoilRoot
의 구조를 대략 다음과 같다.
Recoil
의 상태값은RecoilRoot
로 생성된Context
의 스토어에 저장된다.- 스토어의 상태값에 접근할 수 있는 함수들이 있으며, 이 함수를 활용해 상태값을 접근하거나 상태값을 변경할 수 있다.
- 값의 변경이 발생하면 이를 참조하고 있는 함수 컴포넌트에 모두 알린다.
atom
atom
은 상태를 나타내는 Recoil
의 최소 상태 단위다.
atom
은 key
값을 필수로 가지며, 이 키는 다른 atom
과 구별하는 식별자가 되는 필수 값이다.
- 이 키는 애플리케이션 내부에서 유일한 값이어야 하기 때문에
atom
과selector
를 만들 때 반드시 주의를 기울여야 한다.
default
는 atom
의 초깃값을 의미한다.
- 이
atom
의 값을 컴포넌트에서 읽어오고 이 값의 변화에 따라 컴포넌트를 리렌더링하려면 다음 두 가지 훅을 사용하면 된다.
useRecoilValue
useRecoilValue
는 atom
의 값을 읽어오는 훅이다.
useRecoilState
useRecoilState
는 좀 더 useState
와 유사하게 값을 가져오고, 또 이 값을 변경할 수도 있는 훅이다.
현재 값을 가져오기 위해 이전에 작성한 훅인 useRecoilValue
를 그대로 사용하고 있으며, 상태를 설정하는 훅으로 useSetRecoilState
훅을 사용하고 있다.
- 이 훅은 내부에서 먼저 스토어를 가져온 다음에
setRecoilValue
를 호출해 값을 업데이트하고 있다.
특징
- 장점
- 페이스북 팀에서 주도적으로 개발하고 있기 때문에 앞으로도 기대가 되는 라이브러리
selector
를 필두로 다양한 비동기 작업을 지원하는API
를 제공하고 있기 때문에 비동기 작업을 수월하게 처리할 수 있다.- 리액트와 비슷하게 자체적인 개발도구를 지원한다.
- 단점
- 주 버전과 다르게 부 버전이 변경돼도 호환성이 깨지는 변경 사항이 발생할 수 있는 초기 버전으로 간주되어 라이브러리를 사용할 때에 추가적인 주의가 필요하다.
Recoil에서 영감을 받은, 그러나 조금 더 유연한 Jotai
Jotai
는 Recoil
의 atom
모델에 영감을 받아 만들어진 상태 관리 라이브러리다.
Jotil
는 상향식(bottom-up) 접근법을 취하고 있다.- 작은 단위의 상태를 위로 전파할 수 있는 구조를 취하고 있음을 의미
Context
의 문제점인 불필요한 리렌더링이 일어난다는 문제를 해결하고자 설계돼 있으며, 추가적으로 개발자들이 메모이제이션이나 최적화를 거치지 않아도 리렌더링이 발생되지 않도록 설계돼 있다.
atom
atom
은 최소 단위의 상태를 의미한다.
atom
하나만으로도 상태를 만들 수도, 또 이에 파생된 상태를 만들 수도 있다.
const counterAtom = atom(0)
Jotai
는 atom
을 생성할 때 별도의 key
를 넘겨주지 않아도 된다.
atom
내부에는key
라는 변수가 존재하긴 하지만 외부에서 받는 값은 아니며 단순히toString()
을 위한용도로 한정돼 있다.
즉 Jotai
에서의 atom
에 따라 상태를 저장하고 있지 않다.
useAtomValue
atom
의 값이 어디서 변경되더라도 useAtomValue
로 값을 사용하는 쪽에서는 언제든 최산 값의 atom
을 사용해 렌더링할 수 있게 된다.
useAtom
useAtom
은 useState
와 동일한 형태의 배열을 반환한다.
atom
의 현재 값을 나타내는useAtomValue
훅의 결과를 반환한다.useSetAtom
훅을 반환하는데, 이 훅은atom
을 수정할 수 있는 기증을 제공한다.
특징
Recoil
의 atom
개념을 도입하면서 API
가 간결하다.
- 객체의 참조를 통해 별도의 문자열 키가 없이 각 값들을 관리한다.
selector
가 없이도atom
만으로atom
값에서 또 다른 파생된 상태를 만들 수 있다.- 타입스크립트로 작성돼 있어 타입을 잘 지원한다.
- 리액트 18의 변경된
API
를 원할하게 지원한다.
작고 빠르며 확장에도 유연한 Zustand
Zustand
는 리덕스에 영감을 받아 만들어졌다.
Zustand
에서는 하나의 하나의 스토어를 중앙 집중형으로 활용해 이 스토어 내부에서 상태를 관리하고 있다.
import { create } from "zustand";
const useCounterStore = create((set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
dec: () => set((state) => ({ count: state.count - 1 })),
}));
function Counter(){
const { count, inc, dec } = useCounterStore()
return (
<div>
<span>{count}</span>
<button onClick={inc}>+</button>
<button onClick={dec}>-</button>
</div>
)
}
특징
Zustand
는 많은 코드를 작성하지 않아도 빠르게 스토어를 사용할 수 있다는 큰 장점이 있다.- 타입스크립트 기반으로 작성돼 있기 때문에 별도의
@types
를 설치하거나 임의로 작성된d.ts
에 대한 우려 없이 타입스크립트를 자연스럽게 쓸 수 있다. Zutand
는 리덕스와 마찬가지로 미들웨어를 지원한다.