주스탠드(Zustand)는 Redux와 유사하고, 조타이(Jotai)는 Recoil과 유사한 방식으로 상태 관리를 제공한다. Redux는 불필요한 복잡성, 장황한 코드, 많은 보일러플레이트로 인해 관리가 어려운 문제가 있으며, Recoil은 페이스북에서 개발했으나 현재 업데이트 속도가 느려지고 있어 기술의 장기적 지속 가능성에 우려가 있다.
흥미로운 점은 주스탠드와 조타이가 같은 개발자(Daishi Kato)에 의해 개발됐다는 것이다. 두 도구는 더 간단하면서도 강력한 상태 관리 솔루션으로 주목받고 있으며, 각각 다른 철학과 구조를 기반으로 설계되어 필요에 따라 적절히 선택하는 것이 중요하다. 전역 상태 관리와 리렌더링 성능 최적화 측면에서도 큰 차이가 없으므로, 프로젝트 요구사항에 따라 선택하면 된다.
기본적으로 Zustand와 Jotai 모두 상태가 변경될 때 해당 상태에 구독되어 있는 컴포넌트들만 리렌더링되도록 최적화되어 있다. 이는 두 라이브러리의 주요 장점 중 하나로, 불필요한 리렌더링을 줄여 성능을 향상시키는 방식이다.
또한, 주스탠드와 조타이는 지속적인 업데이트와 개발자와의 소통을 통해 기술의 장기적 지속 가능성과 최신 트렌드에 부합하는 점, 그리고 학습 용이성 측면에서도 매력적인 선택지로 평가받고 있다.
이 글에서는 두 도구의 상태 관리 방식, 사용 패턴 등 주요 차이점을 깊이 있게 다루며, 어떤 상황에서 각각의 도구를 사용하는 것이 적합한지 알아볼 것이다.
Jotai는 일본어로 "state(상태)"를 의미한다.
Zustand는 독일어로 "state(상태)"를 의미한다.
기능/특징 | Zustand | Jotai |
---|---|---|
상태 관리 방식 | 중앙 집중형, 단일 스토어 | 분산형, 여러 개의 작은 아톰(원자) 단위 |
리렌더링 제어 | 전역적으로 최적화 가능 | 개별 아톰 단위로 세밀한 제어 가능 |
사용 패턴 | Flux 패턴, 중앙 스토어에서 모든 상태 관리 | Atomic 패턴, 각 컴포넌트에서 개별적으로 관리 |
상태의 범위 | React 외부에서도 접근 가능 | React 컴포넌트 외부에서 직접 접근 불가 |
Zustand는 하나의 중앙 스토어(store)를 생성해, 모든 상태를 그 안에 정의하고 관리한다. 컴포넌트들이 그 스토어에서 필요한 상태를 불러와 사용하며, 상태가 중앙에서 관리되기 때문에 탑다운 방식이라고 한다. 즉, 중앙 집중적으로 상태를 관리하고, 컴포넌트들이 그 상태를 필요할 때마다 불러와 사용한다.
상태를 단일 Store 객체로 정의하고, 상태와 업데이트 로직을 Store에서 한꺼번에 관리한다. 각 컴포넌트는 Store에서 상태를 가져와 사용하는 방식이며, 상태 접근 및 업데이트 로직이 한 곳에서 관리되는 일관된 패턴을 따른다.
예를 들어, 블로그 웹사이트를 만든다고 하면, 하나의 중앙 스토어에서 블로그 목록, 사용자 정보, 포스트 데이터 같은 것을 관리하고, 필요한 컴포넌트들이 이 중앙 상태를 공유하게 된다.
모든 상태가 한 곳에서 관리되기 때문에, 애플리케이션의 전체 상태를 쉽게 파악할 수 있다. 상태를 추적하거나 디버깅할 때 중앙 저장소만 보면 되므로 유지보수가 용이하다.
하지만 반대로, 상태가 복잡해질수록 관리하는 스토어가 거대해지고, 의존성이 커질 수 있다. 상태가 거대해질수록 특정 상태가 업데이트될 때, 다른 많은 컴포넌트가 그 상태를 필요로 할 경우 의존 관계가 복잡해지고, 변경 사항을 처리하는 데 여러 고려 사항이 생기게 된다. 특히 서로 다른 상태들이 강하게 연결되어 있을 때, 이를 분리하거나 특정 컴포넌트에서만 사용하는 상태로 유지하기가 어려워진다.
예를 들어, 중앙 스토어에 상태들이 많이 포함되어 있다면, 특정 상태가 업데이트될 때 의도치 않게 다른 상태나 의존하는 컴포넌트들이 영향을 받을 수 있다. 이를 관리하고 추적하는 것은 프로젝트 규모가 커질수록 점점 더 어렵고 복잡해질 수 있다.
쉽게 설명하자면, Zustand는 애플리케이션의 상태를 한 곳에서 모아 관리하는 방식으로, 리액트의 기본 상태 흐름과 비슷하게 동작한다.
이 방식은 전체 상태를 중앙에서 일괄적으로 관리하고 추적하기 때문에, 애플리케이션의 상태를 쉽게 파악할 수 있고, 변경도 한 곳에서 관리할 수 있어 간편하다. 예를 들어, 쇼핑몰 애플리케이션에서 '사용자 정보'나 '장바구니'와 같은 데이터가 있다고 할 때, 이 모든 데이터를 하나의 큰 저장소에서 관리하고, 필요한 컴포넌트들이 그 데이터를 공유하며 사용하게 된다.
이런 방식은 여러 컴포넌트에서 동일한 데이터를 공유해야 하거나, 애플리케이션 전체에서 사용되는 중요한 상태를 한 곳에서 관리하는 것이 필요할 때 유용하다. 다만, 애플리케이션이 커질수록 중앙에 모인 상태들이 많아져 관리가 복잡해질 수 있다는 단점도 있다.
하지만 Zustand에서는 이런 문제를 개선하기 위한 방법을 제시한다.
// 개별 스토어 생성
export const createFishSlice = (set) => ({
fishes: 0,
addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
})
// 개별 스토어 생성
export const createBearSlice = (set) => ({
bears: 0,
addBear: () => set((state) => ({ bears: state.bears + 1 })),
eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),
})
// 두 스토어 결합
import { create } from 'zustand'
import { createBearSlice } from './bearSlice'
import { createFishSlice } from './fishSlice'
export const useBoundStore = create((...a) => ({
...createBearSlice(...a),
...createFishSlice(...a),
}))
// 결합된 스토어 사용
import { useBoundStore } from './stores/useBoundStore'
function App() {
const bears = useBoundStore((state) => state.bears)
const fishes = useBoundStore((state) => state.fishes)
const addBear = useBoundStore((state) => state.addBear)
return (
<div>
<h2>Number of bears: {bears}</h2>
<h2>Number of fishes: {fishes}</h2>
<button onClick={() => addBear()}>Add a bear</button>
</div>
)
}
export default App
Jotai는 상태를 작은 원자(atom) 단위로 나누어 관리하는 분산형(bottom-up) 접근 방식을 따른다. 각 컴포넌트는 자신이 필요한 atom을 구독하여 그 상태를 사용하며, 상태는 중앙에서 관리되는 것이 아니라 각 컴포넌트에서 독립적으로 관리된다. 이러한 접근 방식을 바텀업 방식이라고 부른다. 즉, 상태가 분산되어 컴포넌트에서 필요할 때마다 불러와 사용하는 구조이다.
Jotai의 철학은 각 상태를 작은 Atom 단위로 나누어 관리하며, 각 Atom은 개별 컴포넌트에서만 구독되고 사용된다. 상태가 서로 분리되어 있기 때문에, 컴포넌트마다 필요한 상태만 가져오며, 필요에 따라 Atom들 간에 의존 관계를 형성해 파생된 상태를 만들 수도 있다.
예를 들어, 블로그 웹사이트에서 포스트의 제목, 내용, 작성자 정보 등을 각각 개별적인 atom으로 만들어 각 컴포넌트가 관리하고 업데이트하는 방식이다.
이러한 접근 방식은 React의 useState
와 유사한 개념으로, 각 상태를 개별적으로 정의하고 사용하는 방식이다. 이를 통해 보다 세밀한 리렌더링 제어가 가능하다. 특정 상태가 변경될 때, 그 상태에 의존하는 컴포넌트들만 영향을 받게 되므로 복잡한 애플리케이션에서 성능 최적화에 유리하다. 상태가 독립적으로 관리되기 때문에 변경 사항이 다른 상태로 전파되지 않는다는 장점도 있다.
세밀한 리렌더링 제어가 가능하다는 것은, 개별 Atom을 구독하기 때문에 상태 간의 영향도가 적다는 것을 의미한다. 상태가 독립적으로 존재하며, 그 상태를 사용하는 컴포넌트만 리렌더링되므로, 상태 간의 강한 연결을 피할 수 있어 복잡한 상태 간의 연쇄적인 리렌더링을 방지할 수 있다.
하지만, Jotai의 접근 방식은 상태를 여러 atom으로 분산하기 때문에, 애플리케이션의 전체 상태를 파악하기 어렵고, 디버깅이 복잡해질 수 있는 단점이 있다. 상태를 여러 컴포넌트에서 공유하려면 추가적인 작업이 필요할 수도 있다. 상태를 잘게 나눈다는 것이 항상 좋은 것은 아니며, 너무 세분화되면 오히려 관리가 어려워질 수 있다.
import { atom } from "jotai";
export const vehicleModelAtom = atom<string>("");
export const vehicleYearAtom = atom<number>(2023);
export const vehicleHorsepowerAtom = atom<number>(150);
export const vehicleFuelTypeAtom = atom<string>("Gasoline");
export const vehicleAtom = atom((get) => ({
model: get(vehicleModelAtom),
year: get(vehicleYearAtom),
engine: {
horsepower: get(vehicleHorsepowerAtom),
fuelType: get(vehicleFuelTypeAtom),
},
}));
이 예시에서 Jotai는 상태를 작은 단위인 atom
으로 나누어 정의하며, 필요한 컴포넌트에서만 이 atom들을 구독하여 사용한다. 예를 들어, vehicleModelAtom
은 차량의 모델명을 관리하는 상태이고, 다른 Atom들도 각각 독립적인 상태를 관리한다. 이를 필요에 따라 vehicleAtom
에서 결합해 사용하면서, 상태 간의 유연한 결합과 독립적 관리를 동시에 할 수 있다.
주스탠드(Zustand)는 상태 관리에 있어 Flux 패턴을 따르는 라이브러리이다. 이 패턴은 단일 Store 중심으로 상태를 관리하며, 상태가 변경될 때마다 관련된 뷰에 변경 사항을 전달하는 방식이다. 상태 변경은 Action을 통해 트리거되며, 모든 Action은 Dispatcher를 통해 스토어에 전달된다. 뷰에서는 상태를 읽고 변경 사항을 감지하여 UI를 업데이트한다.
Zustand는 중앙 집중형 상태 관리를 지향하여, 모든 상태를 하나의 Store에 모아 관리하고, 여러 컴포넌트가 이를 필요에 따라 구독하는 방식이다. 이는 데이터가 단방향으로 흐르는 Flux 패턴과 유사하며, 상태 관리의 경로가 명확하다. 이 방식은 Redux와 비슷하게 중앙에 스토어를 두고 여러 컴포넌트에서 상태를 공유하는 것을 중점으로 두고 있다.
조타이(Jotai)는 Atomic 패턴을 따르는 상태 관리 라이브러리로, 상태를 작은 원자(atom) 단위로 나누어 관리한다. 각 상태는 독립적으로 정의되며, 다른 상태와의 종속성을 최소화하여 독립적이고 유연한 상태 관리를 지향한다. 이러한 원자적 구조를 통해 상태가 필요한 컴포넌트에서만 해당 상태를 구독하며 업데이트할 수 있어, 상태 간 의존성을 줄이고 리렌더링을 최소화할 수 있다.
조타이는 React의 useContext
& useState
와 유사한 방식으로 atom
을 통해 상태를 정의하고, useAtom
훅을 사용해 상태와 그 변경 함수를 제공한다(훅 기반). 이러한 접근 방식은 직관적이고 간단한 상태 관리를 가능하게 하며, 상태의 독립성과 분산형 관리 철학을 중요하게 여긴다.
useState
처럼 단순하다.Zustand의 상태는 React 외부에 존재한다. React의 useState
와 달리 React 외부에서도 접근 가능한 상태다. 이는 상태가 React의 내부 훅 시스템에만 한정되지 않고, 전역적인 JavaScript 메모리에 저장된다는 의미다. Zustand에서는 상태가 중앙에서 관리되기 때문에 어떤 컴포넌트에서든, 심지어 React 컴포넌트가 아닌 곳에서도 이 상태에 접근할 수 있다. 쉽게 말해, 상태가 React와 분리되어 있기 때문에, React 외부의 코드에서도, 애플리케이션 어디서든 상태를 관리하고 사용할 수 있는 것이다.
Zustand는 클로저를 활용해 스토어의 상태를 관리한다. 클로저는 함수가 선언될 때 그 주변 환경을 기억하는 특성으로, Zustand에서 상태는 스토어를 조회하거나 변경하는 함수의 바깥 스코프에 유지된다. 이렇게 함으로써 상태는 애플리케이션의 생명 주기 전반에 걸쳐 일관성 있게 관리되며, React 외부에 위치하면서도 안전하게 상태를 관리하고 사용할 수 있다. 이는 상태가 중앙에서 유지되며, 의도치 않은 변경을 방지할 수 있게 해준다.
상태가 모듈 파일에 정의되고, 어디서나 해당 모듈을 가져다 쓰면 접근할 수 있는 전역 상태이다. 즉, Zustand는 모듈 수준 상태를 가진다.
Jotai의 상태는 React 내부에 존재한다. 이는 React의 useState
와 비슷하게 동작하며, 상태가 특정 컴포넌트 또는 컴포넌트 계층 구조 내에서 관리된다는 의미다. 상태를 관리하기 위해 Jotai의 atom
과 useAtom
훅을 사용하게 되는데, 이 상태는 React 컴포넌트 시스템 안에서만 접근 가능하다. 즉, React 컴포넌트 밖에서 이 상태에 직접 접근할 수 없다는 의미다. 상태가 각 Provider 내부에 국한되며, Provider는 상태의 범위를 정의하고, 같은 atom
이라도 Provider가 다르면 별도의 상태로 간주된다.
<Provider>
<Counter /> // 첫 번째 Provider의 상태를 공유
<Counter /> // 첫 번째 Provider의 상태를 공유
</Provider>
<Provider>
<Counter /> // 두 번째 Provider의 상태를 공유
<Counter /> // 두 번째 Provider의 상태를 공유
</Provider>
Provider
를 여러 번 사용한 이유는, 각 Provider가 별도의 상태를 관리하기 때문이다. 같은 atom
을 사용하더라도, Provider마다 서로 다른 상태가 설정된다. 그래서 하나의 Provider에 있는 컴포넌트는 그 Provider의 상태를 공유하지만, 다른 Provider는 별도의 상태를 갖는다. 각각의 Provider
가 별도의 상태를 관리하므로, 같은 컴포넌트라도 다른 Provider
내에 있으면 서로 다른 상태를 갖게 된다.
Jotai는 기본적으로 Provider-less 모드를 제공하므로, 별도의 프로바이더 없이 전역 상태로 사용할 수 있다. 단, 필요에 따라 프로바이더를 추가해 특정 상태 범위를 제한하고 상태를 분리하여 관리하기 위해서 범위를 제한할 수 있다. 이는 여러 개의 인스턴스를 가지는 상태를 관리하거나, 특정 컴포넌트 트리에서만 상태를 사용하도록 범위를 설정하고자 할 때 유용하다. 즉, React의 특성에 더 잘 맞는 지원을 한다.
Jotai의 상태는 React 내부에 존재하므로, React 컴포넌트와 깊이 연관되어 있으며 상태의 범위는 특정 Provider 내로 한정된다. Provider는 Jotai에서 사용하는 모든 작은 상태 단위(아톰)들을 보관하고 관리하는 일종의 컨테이너 역할을 하는데, 컴포넌트들이 필요할 때마다 그 값을 가져다 사용할 수 있도록 한다. 이는 동일한 컴포넌트를 여러 번 사용하더라도 각기 다른 상태를 가지도록 할 수 있어 상태 격리가 가능하다는 의미이다.
애플리케이션에서 Provider를 하나만 사용하거나, 별도의 프로바이더 없이 전역 상태로 사용하는 경우 Jotai의 상태 관리 방식과 Zustand의 방식은 거의 비슷하게 작동한다. 두 방식 모두 애플리케이션 전체에서 동일한 상태를 사용할 수 있다는 점에서는 큰 차이가 없다. 하지만, 상태 관리 방식, 구조, 그리고 상태의 범위에 대한 명확한 차이는 다른 목차에서 계속 설명하고 있다.
상태가 React의 Context를 통해 정의되고, 특정 컴포넌트 계층 내에서만 접근 가능한 상태이다. 즉, Jotai는 컨텍스트 수준 상태이다.
2024.10.17 기준, Zustand 1.2kb, Jotai 8.3kb로 Zustand가 더 가볍다.
실제 Zustand와 Jotai를 조직 단위로 사용해본 경험자들의 리뷰를 정리하자면 아래와 같다.
useState
와 useContext
의 대체재가 필요하다면 Jotai가 잘 맞습니다.
좋은 글 감사드립니다 ! 글이 너무 읽기 쉽게 잘 작성되어있네요 👍
덕분에 좋-다이 쓰려구요 !