이미지 로딩 중...
CodeDeck AI
2025. 11. 8. · 1 Views
Next.js Zustand 상태관리 완벽 가이드
Next.js 프로젝트에서 Zustand를 사용한 전역 상태 관리 방법을 단계별로 학습합니다. 설치부터 실전 활용까지 초급자도 쉽게 따라할 수 있는 예제로 구성되어 있습니다.
들어가며
이 글에서는 Next.js Zustand 상태관리 완벽 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- Zustand_설치_및_기본_스토어_생성
- 컴포넌트에서_스토어_사용하기
- TypeScript로_타입_안전한_스토어_만들기
- 여러_상태를_관리하는_실전_스토어
- Next.js에서_지속성_추가하기_persist
- 비동기_액션_처리하기
- 스토어_분리하고_결합하기
- 셀렉터로_성능_최적화하기
- Next.js_App_Router에서_사용하기
- 액션_외부에서_호출하기
- 개발자_도구_연동하기_devtools
- 초기_상태_리셋하기
1. Zustand_설치_및_기본_스토어_생성
개요
Zustand를 설치하고 가장 기본적인 상태 스토어를 생성하는 방법입니다. create 함수로 스토어를 만들고 상태와 액션을 정의합니다.
코드 예제
import { create } from 'zustand'
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))
설명
create 함수로 스토어를 만들고, set 함수를 사용해 상태를 업데이트하는 액션을 정의합니다. 매우 간단한 구조로 Redux보다 훨씬 적은 코드로 상태 관리가 가능합니다.
2. 컴포넌트에서_스토어_사용하기
개요
생성한 Zustand 스토어를 React 컴포넌트에서 사용하는 방법입니다. 훅처럼 간단하게 호출하여 상태와 액션에 접근할 수 있습니다.
코드 예제
export default function Counter() {
const count = useStore((state) => state.count)
const increment = useStore((state) => state.increment)
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>증가</button>
</div>
)
}
설명
useStore 훅으로 필요한 상태와 액션만 선택적으로 가져올 수 있습니다. 셀렉터 함수를 사용하면 불필요한 리렌더링을 방지할 수 있습니다.
3. TypeScript로_타입_안전한_스토어_만들기
개요
TypeScript를 사용하여 타입 안전성을 보장하는 Zustand 스토어를 만드는 방법입니다. 인터페이스로 스토어의 구조를 명확히 정의합니다.
코드 예제
interface CounterStore {
count: number
increment: () => void
decrement: () => void
}
const useCounterStore = create<CounterStore>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))
설명
인터페이스로 스토어의 타입을 정의하고 제네릭으로 전달하면, 타입 추론과 자동완성이 완벽하게 작동합니다.
4. 여러_상태를_관리하는_실전_스토어
개요
사용자 정보와 로그인 상태를 관리하는 실전 예제입니다. 여러 상태와 액션을 하나의 스토어에서 관리할 수 있습니다.
코드 예제
interface User {
id: string
name: string
}
const useAuthStore = create<{
user: User | null
isLoggedIn: boolean
login: (user: User) => void
logout: () => void
}>((set) => ({
user: null,
isLoggedIn: false,
login: (user) => set({ user, isLoggedIn: true }),
logout: () => set({ user: null, isLoggedIn: false }),
}))
설명
복잡한 객체 상태와 관련 액션들을 하나의 스토어로 묶어 관리합니다. 로그인/로그아웃 로직을 중앙에서 관리하여 코드 재사용성이 높아집니다.
5. Next.js에서_지속성_추가하기_persist
개요
새로고침해도 상태가 유지되도록 localStorage에 자동 저장하는 기능입니다. Zustand의 persist 미들웨어를 사용합니다.
코드 예제
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
const useStore = create(
persist(
(set) => ({
theme: 'light',
setTheme: (theme: string) => set({ theme }),
}),
{ name: 'theme-storage' }
)
)
설명
persist 미들웨어로 스토어를 감싸면 자동으로 localStorage에 저장됩니다. 페이지를 새로고침해도 상태가 유지됩니다.
6. 비동기_액션_처리하기
개요
API 호출과 같은 비동기 작업을 Zustand에서 처리하는 방법입니다. async/await를 사용하여 로딩 상태도 함께 관리합니다.
코드 예제
const useDataStore = create<{
data: any[]
isLoading: boolean
fetchData: () => Promise<void>
}>((set) => ({
data: [],
isLoading: false,
fetchData: async () => {
set({ isLoading: true })
const response = await fetch('/api/data')
const data = await response.json()
set({ data, isLoading: false })
},
}))
설명
액션 함수를 async로 만들어 비동기 작업을 처리하고, 로딩 상태를 함께 업데이트합니다. API 호출 전후로 상태를 관리할 수 있습니다.
7. 스토어_분리하고_결합하기
개요
기능별로 스토어를 분리한 후 필요할 때 결합하여 사용하는 패턴입니다. 코드 구조가 깔끔해지고 유지보수가 쉬워집니다.
코드 예제
const createUserSlice = (set) => ({
user: null,
setUser: (user) => set({ user }),
})
const createCartSlice = (set) => ({
items: [],
addItem: (item) => set((state) => ({
items: [...state.items, item]
})),
})
const useStore = create((...a) => ({
...createUserSlice(...a),
...createCartSlice(...a),
}))
설명
각 기능을 별도의 함수로 분리하고 스프레드 연산자로 결합합니다. 대규모 프로젝트에서 상태 관리 코드를 모듈화할 수 있습니다.
8. 셀렉터로_성능_최적화하기
개요
필요한 상태만 구독하여 불필요한 리렌더링을 방지하는 방법입니다. 얕은 비교로 성능을 최적화합니다.
코드 예제
import { shallow } from 'zustand/shallow'
function TodoList() {
const { todos, addTodo } = useStore(
(state) => ({
todos: state.todos,
addTodo: state.addTodo
}),
shallow
)
return <div>{todos.map(todo => <p>{todo}</p>)}</div>
}
설명
shallow 비교를 사용하면 선택한 값이 실제로 변경될 때만 리렌더링됩니다. 여러 값을 한 번에 선택할 때 성능이 향상됩니다.
9. Next.js_App_Router에서_사용하기
개요
Next.js 13+ App Router에서 Zustand를 사용하는 방법입니다. 클라이언트 컴포넌트로 명시하여 사용합니다.
코드 예제
'use client'
import { useStore } from '@/store/useStore'
export default function ClientComponent() {
const count = useStore((state) => state.count)
const increment = useStore((state) => state.increment)
return <button onClick={increment}>Count: {count}</button>
}
설명
'use client' 지시어를 추가하여 클라이언트 컴포넌트임을 명시합니다. Zustand는 클라이언트 사이드 상태 관리 라이브러리이므로 서버 컴포넌트에서는 사용할 수 없습니다.
10. 액션_외부에서_호출하기
개요
컴포넌트 외부나 유틸리티 함수에서 스토어의 액션을 호출하는 방법입니다. getState로 현재 상태에 접근할 수 있습니다.
코드 예제
const useStore = create((set, get) => ({
count: 0,
increment: () => set({ count: get().count + 1 }),
}))
// 컴포넌트 외부에서 호출
export function incrementFromOutside() {
useStore.getState().increment()
}
설명
useStore.getState()로 스토어의 현재 상태와 액션에 접근할 수 있습니다. API 유틸리티나 이벤트 핸들러에서 유용하게 사용됩니다.
11. 개발자_도구_연동하기_devtools
개요
Redux DevTools를 Zustand와 연동하여 상태 변화를 시각적으로 디버깅하는 방법입니다.
코드 예제
import { devtools } from 'zustand/middleware'
const useStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}),
{ name: 'CounterStore' }
)
)
설명
devtools 미들웨어를 추가하면 Redux DevTools에서 Zustand 상태를 모니터링할 수 있습니다. 상태 변화 추적과 타임 트래블 디버깅이 가능합니다.
12. 초기_상태_리셋하기
개요
스토어의 상태를 초기값으로 되돌리는 리셋 기능을 구현하는 방법입니다. 로그아웃이나 초기화가 필요할 때 유용합니다.
코드 예제
const initialState = { count: 0, user: null }
const useStore = create((set) => ({
...initialState,
increment: () => set((state) => ({ count: state.count + 1 })),
reset: () => set(initialState),
}))
// 사용: useStore.getState().reset()
설명
초기 상태를 별도 변수로 분리하고 reset 액션을 만들어 언제든 초기화할 수 있습니다. 사용자 로그아웃 시 모든 상태를 한 번에 정리할 때 편리합니다. --- 이상으로 Next.js에서 Zustand를 활용한 상태 관리 가이드를 마칩니다. 각 카드의 코드를 직접 실행해보며 학습하시면 더욱 효과적입니다!
마치며
이번 글에서는 Next.js Zustand 상태관리 완벽 가이드에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#Next.js #Zustand #StateManagement #GlobalState #TypeScript