23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
React 프로젝트를 하다 보면 가장 먼저 부딪히는 과제 중 하나가 바로 상태 관리입니다.
컴포넌트가 많아지고 서로 데이터를 주고받을 때 단순히 props drilling이나 Context API만으로는 한계가 있습니다.
이때 필요한 것이 상태 관리 라이브러리입니다.
오랫동안 많은 개발자들이 사용해온 상태 관리 라이브러리는 Redux입니다.
Redux는 강력하고 확장성도 뛰어나지만 아래와 같은 단점이 있습니다.
보일러플레이트 코드가 많고, 파일 구조가 복잡해질 수 있음
액션, 리듀서, 스토어, 미들웨어 등 러닝 커브가 높음
서버 상태 관리나 캐싱을 직접 구현해야함
이러한 단점을 보완하기 위해 최근에는 더 단순하고 직관적인 상태 관리 라이브러리들이 각광받고 있습니다.
그중 대표적인 세 가지가 Zustand, Recoil, Jotai입니다.
Zustand는 독일어로 "상태"를 의미하는 이름을 가진 React 상태관리 라이브러리입니다.
2019년에 처음 출시되어 2020년부터 본격적으로 성장했으며, Redux의 복잡함과 Context API의 성능 문제를 해결하기 위해 만들어졌습니다.
1KB의 가벼운 크기와 극도로 간결한 API로, 현재 주간 다운로드 수 1,130만 건을 기록하는 인기 라이브러리입니다.
Zustand의 가장 큰 특징은 극도로 간결한 API입니다.
Provider 없이 create 함수 하나로 store를 만들고, action과 reducer를 분리할 필요 없이 바로 사용할 수 있습니다.
중앙집중식 상태관리 방식으로 Redux와 유사한 구조를 가지지만 보일러플레이트가 거의 없습니다. (Redux는 보일러플레이트가 많음)
성능 최적화가 기본으로 되어 있어 selector로 필요한 상태만 구독하면 해당 상태 변경 시에만 리렌더링됩니다.
TypeScript 타입이 자동으로 추론되고, React 외부에서도 store에 접근할 수 있습니다.
persist, devtools, immer 같은 유용한 미들웨어도 제공됩니다.
create 함수로 store를 생성합니다. set 함수로 상태를 업데이트하며, 함수 형태로 전달하면 이전 상태를 받아 새로운 상태를 반환할 수 있습니다.
객체를 직접 전달하면 해당 값으로 바로 업데이트됩니다.
// store.js
import { create } from 'zustand'
const useBearStore = create((set) => ({
bears: 0,
increaseBears: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
export default useBearStore컴포넌트에서는 Hook처럼 사용하며, selector 함수로 필요한 상태나 함수만 가져옵니다.
bears 값이 변경되면 BearCounter만 리렌더링되고, Controls는 리렌더링되지 않습니다.
Provider 없이 서로 다른 컴포넌트에서 동일한 store를 import해서 사용하면 상태가 자동으로 동기화됩니다.
import useBearStore from './store'
function BearCounter() {
const bears = useBearStore((state) => state.bears)
return <h1>{bears} bears around here...</h1>
}
function Controls() {
const increaseBears = useBearStore((state) => state.increaseBears)
return <button onClick={increaseBears}>Add bear</button>
}zustand는 빠른 프로토타이핑에 최적입니다. 복잡한 의존성이 적으며, 간결하고 빠르게 사용이 가능합니다.
Redux를 사용하기에 러닝커브가 높을 때 Zustand는 빠르게 사용법을 익히고 프로젝트에 적용이 가능합니다.
간단한 전역 상태관리나 React Query 와 함께 클라이언트 상태를 관리할 때 효과적입니다.
대규모 센터프라이즈나 복잡한 상태 의존성이 많은 경우에는 적합하지 않습니다.
공식 문서
Recoil은 Facebook(현 Meta)에서 만든 React 상태관리 라이브러리입니다.
2020년에 발표되어 React의 동시성 모드와의 완벽한 호환을 목표로 설계되었습니다.
"React처럼 생각하는 상태관리"를 표방하며 atom이라는 독특한 개념을 도입했습니다.
Meta에서 만들었다는 점에서 신뢰도가 높지만, 충분한 업데이트가 이루어지지 않고 있습니다.
Recoil의 핵심은 atom 기반 접근 방식입니다.
상태를 하나의 큰 store에 담지 않고 독립적인 작은 atom들로 분리해 관리하며, 각 atom은 고유한 key를 가집니다.
selector를 통한 파생 상태 관리가 강력한데, 의존하는 atom이 변경되면 자동으로 재계산되고 Recoil이 의존성 그래프를 자동으로 관리해줍니다.
비동기 처리가 자연스럽게 되어 selector에서 async/await을 직접 사용할 수 있고 Suspense와 통합되어 로딩 상태를 선언적으로 처리할 수 있습니다.
React의 동시성 모드를 완벽하게 지원하며 미래의 React 기능과의 호환성이 보장됩니다.
atom은 고유한 key와 초기값(default)으로 정의합니다.
selector는 다른 atom이나 selector에 의존하는 파생 상태를 만듭니다.
get 함수로 의존하는 atom을 가져오며, textState가 변경되면 charCountState가 자동으로 재계산됩니다.
// atoms.js
import { atom, selector } from 'recoil'
export const textState = atom({
key: 'textState',
default: '',
})
export const charCountState = selector({
key: 'charCountState',
get: ({get}) => {
const text = get(textState)
return text.length
},
})Recoil을 사용하려면 앱 최상단을 RecoilRoot로 감싸야 합니다.
이는 Provider 역할을 하며 모든 atom의 상태를 관리합니다.
// App.js
import { RecoilRoot } from 'recoil'
function App() {
return (
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
)
}useRecoilState는 useState와 비슷하게 값과 setter를 반환합니다.
useRecoilValue는 읽기만 할 때 사용합니다.
TextInput에서 text를 변경하면 charCountState가 자동으로 재계산되어 CharacterCount 컴포넌트가 업데이트됩니다.
// Component.js
import { useRecoilState, useRecoilValue } from 'recoil'
import { textState, charCountState } from './atoms'
function TextInput() {
const [text, setText] = useRecoilState(textState)
return <input value={text} onChange={(e) => setText(e.target.value)} />
}
function CharacterCount() {
const count = useRecoilValue(charCountState)
return <div>Character Count: {count}</div>
}Recoil은 복잡한 상태 의존성이 많은 프로젝트에 적합합니다.
하나의 상태가 여러 다른 상태에 의존하고 파생 상태가 많다면 selector의 자동 의존성 관리가 큰 도움이 됩니다.
비동기 로직이 많고 Suspense를 적극 활용하는 프로젝트, React의 최신 기능과 동시성 모드를 사용하는 프로젝트에서 미래 호환성이 보장됩니다.
상태 모듈화를 중시하는 팀에는 적합하지만, 아직 1.0 버전 이전이라는 점과 RecoilRoot Provider가 필요하다는 점은 고려해야 합니다.
공식 문서
Jotai는 일본어로 "상태"를 의미하며, Zustand를 만든 Daishi Kato가 2020년에 만든 또 다른 상태관리 라이브러리입니다.
Poimandres 그룹에서 관리하고 있으며, Recoil의 atom 개념을 채택하면서도 더 단순하고 가볍게 만든 것이 특징입니다.
"Recoil의 간소화 버전"이라고 불리며, 번들 크기는 약 2.5KB로 매우 작습니다.
Jotai는 Recoil의 atom 개념을 채택했지만 atom에 key가 필요 없어 보일러플레이트가 적습니다.
atom 자체가 참조로 동작하기 때문입니다.
API가 극도로 간소화 되어있서 Recoil보다 배우기 쉽고, 파생 atom을 함수로 간단히 정의할 수 있으며 비동기 atom도 자연스럽게 지원합니다.
Provider 없이도 사용할 수 있지만 필요하면 Provider로 scope를 나눌 수 있어 테스트나 SSR 환경에서 유용합니다.
TypeScript로 작성되어 타입 추론이 우수하고 별도의 타입 정의 없이도 자동으로 타입이 추론됩니다.
atom은 초기값만 전달하면 됩니다. Recoil과 달리 key가 필요 없습니다.
파생 atom은 함수를 전달하여 정의하며, get으로 다른 atom을 참조합니다.
countAtom이 변경되면 doubleCountAtom이 자동으로 재계산됩니다.
// atoms.js
import { atom } from 'jotai'
export const countAtom = atom(0)
export const doubleCountAtom = atom((get) => get(countAtom) * 2)useAtom은 값과 setter를 반환하고, useAtomValue는 읽기만 할 때 사용합니다.
Provider 없이 바로 사용할 수 있으며, count를 증가시키면 doubleCount도 자동으로 업데이트됩니다.
// Component.js
import { useAtom, useAtomValue } from 'jotai'
import { countAtom, doubleCountAtom } from './atoms'
function Counter() {
const [count, setCount] = useAtom(countAtom)
const doubleCount = useAtomValue(doubleCountAtom)
return (
<div>
<p>Count: {count}</p>
<p>Double: {doubleCount}</p>
<button onClick={() => setCount(c => c + 1)}>+1</button>
</div>
)
}비동기 atom은 async 함수를 전달하여 정의합니다. userIdAtom이 변경되면 userAtom이 자동으로 새로운 데이터를 fetch합니다. Suspense와 함께 사용하면 로딩 상태를 자연스럽게 처리할 수 있습니다.
// 비동기 atom 예시
import { atom } from 'jotai'
const userIdAtom = atom(1)
const userAtom = atom(async (get) => {
const userId = get(userIdAtom)
const response = await fetch(`/api/user/${userId}`)
return response.json()
})Jotai는 atom 패턴의 장점을 원하면서도 간결함을 중시하는 프로젝트에 적합합니다.
atom 패턴을 사용하고 싶으나 Recoil의 복잡함은 부담스럽다면 Jotai는 좋은 대안입니다.
번들 크기에 민감한 프로젝트에서 유리하고, TypeScript를 사용하는 팀에서 타입 추론이 매우 잘 됩니다.
중간 규모 프로젝트에서 상태를 세밀하게 분리하고 싶을 때 효과적이며, Zustand를 만든 같은 개발자가 만들어서 Zustand에서 마이그레이션하기도 쉽습니다.
Recoil 대비 가벼운 번들 크기, key 불필요, 안정된 1.0 이상 버전, 선택적 Provider 사용이 장점입니다.
공식 문서
프로젝트의 규모와 라이브러리 용도에 따라, 적절한 라이브러리를 선택해 상태관리를 손 쉽게 할 수 있습니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.