Context API로 충분한 줄 알았는데... Zustand 써보니 달랐다!
지난 포스팅에서는 Context API의 기초 사용법을 다뤘습니다.
이번에는 Context API와 비슷하지만 더 간단하고 효율적인 상태 관리 도구, Zustand를 소개하려고 합니다.
01
Context API를 사용해 본 사람이라면...
Context API는 React에서 글로벌 상태를 관리할 때 기본적으로 많이 사용됩니다.
간단하고 익숙한 도구지만, 실제 프로젝트에서 사용하다 보면 몇 가지 불편함을 느낄 때가 있습니다.
- Provider 중첩
: 여러 컴포넌트가 상태를 공유하면 Provider를 계속 추가해야 해 코드가 복잡해집니다. - 리렌더링 문제
: 상태가 변경되면 관련된 모든 컴포넌트가 리렌더링 되어 성능이 저하될 수 있습니다. - 기능 한계
: 실시간 데이터 동기화나 대규모 상태 관리를 구현하려면 번거로울 때가 많습니다.
이런 문제를 겪으면서 새로운 상태 관리 도구를 고민하던 중, 저는 Zustand를 도입해 봤습니다.
Zustand는 간단하고 직관적인 API를 제공해 이러한 문제들을 효과적으로 해결할 수 있었습니다.
이제 Zustand가 어떤 도구인지 자세히 알아볼게요!
Zustand란 무엇인가?
Zustand는 React 애플리케이션에서 효율적으로 상태를 관리할 수 있는 경량 상태 관리 라이브러리입니다.
특히 다음과 같은 프로젝트에서 유용합니다.
- 상태 공유가 많은 프로젝트
: 여러 컴포넌트에서 상태를 공유해야 하는 경우, Context API보다 간결하게 관리할 수 있습니다. - 실시간 데이터 동기화
: 채팅 애플리케이션이나 WebSocket 기반 데이터 처리처럼 상태 변경이 잦은 프로젝트에서 성능이 뛰어납니다. - 복잡한 상태 구조 관리
: 대규모 상태 관리(예: 필터링, 다중 유저 데이터 등)에서도 Provider 없이 깔끔하게 처리할 수 있습니다. - 빠른 상태 초기화와 리셋
: 게임, 쇼핑몰 장바구니처럼 상태를 초기화하거나 리셋해야 하는 경우에도 효과적입니다.
Zustand의 가장 큰 장점은 코드가 간단하고, 필요한 컴포넌트만 리렌더링할 수 있다는 점입니다. 😊
Zustand 기본 사용법
1. 설치하기
먼저, 아래 명령어를 사용해 Zustand를 설치합니다.
npm install zustand
2. 상태 정의하기
Zustand에서 create 함수는 상태를 정의하고 업데이트 메서드를 포함합니다.
상태 변경에는 set을 사용하며, 현재 상태를 받아 새로운 상태를 반환해 상태 업데이트를 단순하고 직관적으로 처리할 수 있습니다.
import create from "zustand";
// 상태 정의
const useUserStore = create((set) => ({
user: null, // 초기 상태
setUser: (user) => set({ user }), // 상태 업데이트 메서드
}));
set은 상태를 업데이트할 때 사용할 수 있는 함수로, 현재 상태를 직접 수정하지 않고 새로운 상태를 반환합니다.
아래의 예제와 같이 작성되기도 합니다.
set({ user }); // 기존 상태를 덮어쓰고 새로운 상태를 적용
set((state) => ({ ...state, loggedIn: true })); // 기존 상태에 새로운 필드 추가
3. 상태 사용하기
정의한 상태는 React 컴포넌트에서 바로 사용할 수 있습니다.
const UserComponent = () => {
const { user, setUser } = useUserStore();
return (
<div>
<p>현재 사용자: {user ? user.name : "비회원"}</p>
<button onClick={() => setUser({ name: "김아무개" })}>
김아무개씨로 로그인
</button>
</div>
);
}
위 코드는 현재 사용자 정보를 표시하며, 버튼을 클릭하면 새로운 사용자 정보를 업데이트합니다.
Context API와의 코드 비교
1. Context API 사용 예제
import React, { createContext, useContext, useState } from 'react';
// Context 생성
const UserContext = createContext();
export const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
export function useUser() {
return useContext(UserContext);
}
// 상태 사용
const UserComponent = () => {
const { user, setUser } = useUser();
return (
<div>
<p>현재 사용자: {user ? user.name : "비회원"}</p>
<button onClick={() => setUser({ name: "김아무개" })}>
김아무개씨로 로그인
</button>
</div>
);
}
2. Zustand 사용 예제
import { create } from "zustand";
// Zustand 상태 정의
const useUserStore = create((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
// 상태 사용
const UserComponent = () => {
const { user, setUser } = useUserStore();
return (
<div>
<p>현재 사용자: {user ? user.name : "비회원"}</p>
<button onClick={() => setUser({ name: "김아무개" })}>
김아무개씨로 로그인
</button>
</div>
);
}
3. 주요 차이점
- Provider 필요 여부
: Context API는 Provider가 필요하지만, Zustand는 필요 없습니다. - 코드 간소화
: Zustand는 더 간단하고 직관적으로 상태를 정의할 수 있습니다. - 리렌더링
: Context API는 상태 변경 시 관련 컴포넌트를 모두 리렌더링하지만, Zustand는 필요한 부분만 리렌더링합니다.
프로젝트에 Zustand를 적용해 보자
Zustand를 사용해 로그인한 사용자 상태를 관리하는 코드를 만들어 보겠습니다.
사용자 정보를 저장하고 업데이트하는 상태를 정의하고, 이를 React 컴포넌트에서 활용합니다.
1. Zustand 상태 정의
아래는 로그인 상태를 관리하는 상태 정의 코드입니다.
import { auth } from "../api";
import { create } from "zustand";
// 상태 타입 정의
type AuthState = {
userId: number | null;
isInitialized: boolean;
login: (userId: number) => void;
logout: () => void;
};
// Zustand 상태 관리 훅
const useAuthStore = create<AuthState>((set) => ({
userId: null,
isInitialized: false,
login: (userId: number) => set({ userId, isInitialized: true }),
logout: async () => {
try {
await auth.userLogout();
set({ userId: null, isInitialized: true });
} catch (error) {
console.error("로그아웃 중 오류 발생:", error);
}
},
}));
- user: 로그인한 사용자 정보를 저장합니다.
- isInitialized
: 초기화 상태를 추적합니다.
: 초기 상태 로딩 여부를 확인하고, UI 깜빡임이나 동기화 문제를 방지하기 위해 사용하면 좋습니다. - login: 사용자 정보를 업데이트하는 함수입니다.
- logout: 사용자 정보를 초기화하는 함수입니다.
2. 상태 사용
Zustand 훅 useAuthStore를 호출해 상태(userId, isInitialized)와 메서드(login, logout)를 가져옵니다.
그리고 가져온 상태와 메서드를 기반으로 React 컴포넌트에서 로그인/로그아웃 상태를 관리합니다.
import React from "react";
import useAuthStore from "./useAuthStore"; // Zustand 훅을 가져옵니다.
const AuthComponent = () => {
const { userId, isInitialized, login, logout } = useAuthStore();
return (
<div>
{!isInitialized ? (
<p>Loading...</p> // 초기화 상태 표시
) : userId ? (
<div>
<p>Welcome, User ID: {userId}</p>
<button onClick={logout}>Logout</button>
</div> // 로그인 상태
) : (
<div>
<p>Please log in.</p>
<button onClick={() => login(123)}>Login as User 123</button>
</div> // 로그아웃 상태
)}
</div>
);
};
export default AuthComponent;
- 로그인 : 버튼 클릭 시 login 메서드를 호출해 상태를 업데이트합니다.
- 로그아웃 : 버튼 클릭 시 logout 메서드를 호출해 상태를 초기화합니다.
- 초기화 상태 표시 : isInitialized 상태를 활용해 UI를 로딩 상태와 구분합니다.
정리
Zustand는 간단한 API와 뛰어난 유연성 덕분에 로그인 상태 관리와 같은 작업을 매우 간편하게 만들어줍니다.
특히, Provider 중첩 없이 상태를 정의하고, 필요한 컴포넌트만 리렌더링할 수 있어 Context API보다 효율적입니다.
복잡한 상태 관리도 훨씬 쉽게 해결할 수 있을 겁니다.
Context API의 단점이 느껴졌다면, Zustand를 활용해 더 간단하고 효율적으로 상태 관리를 시작해 보세요.
다음 프로젝트에서 적용해 보길 추천합니다!