상태 관리 도구, 뭐 써야 할지 고민된다면?
오늘은 내가 최근에 써본 Zustand에 대해 소개해보려고 한다.
왜 Zustand를 쓰는지, 기존 상태 관리 도구들과 비교했을 때 어떤 장점이 있는지, 그리고 기본적인 사용법까지 함께 정리해보겠다. 그러면 바로 시작!!
독일어로 상태라는 뜻을 가진 상태 관리 라이브러리이다.
이 외에도 다양한 장점들이 존재합니다.
컴포넌트는 store를 구독(subscribe)해서 필요한 상태만 받아오고 해당 값이 바뀔 때만 리렌더링이 발생한다.
create() 함수를 통해 store를 생성하고 내부적으로는 클로저를 이용해 상태 기억set, get 등의 내부 메서드로 상태를 읽고 수정하며useStore((state) => state.원하는값) 방식으로 접근-> 덕분에 불필요한 렌더링을 줄이고, 간단한 코드로 글로벌 상태 관리를 구현할 수 있다!
useStore((state) => ({ ... })) 형태로 객체 여러 값을 가져오면, 참조가 바뀌어 매번 리렌더링이 발생할 수 있다. 그래서 이걸 해결하려면?
// 해결 방법 -> useShallow
import { useShallow } from "zustand/react/shallow";
const { count, increment, decrement } = useTestStore(
useShallow((state) => ({
count: state.count,
increment: state.increment,
decrement: state.decrement,
}))
);
// npm <- pnpm은 그냥 앞에 p하나만 붙여주면 된다.
npm add zustand
// yarn 설치
$ yarn add zustand
store란? 애플리케이션의 여러 상태(State)를 중앙에서 관리하는 패턴
// 전역 Store 생성 및 활용
import React from "react";
import { create } from "zustand";
type Props = {};
interface TestStore {
count: number;
increment: () => void;
decrement: () => void;
}
const useTestStore = create<TestStore>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
const ZustandTestpage = (props: Props) => {
return <div>ZustandTestpage</div>;
};
export default ZustandTestpage;
import useStore from '../store.ts'
const ZustandTestpage = (props: Props) => {
const { count, increment, decrement } = useTestStore();
// const count = useTestStore((state) => state.count);
// const count = useTestStore(useShallow((state) => state.count));
return (
<div>
<div>{count}</div>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
};
export default ZustandTestpage;
스키마 선언 및 유효성 검사 라이브러리
Zod를 React 에서 사용할 땐 react-hook-form과 같이 사용하면 좋다고한다. 필요한 이유가 TypeScript의 한계 때문?
TypeScript는 타입을 엄격하게 관리해주긴 하지만, 어디까지나 컴파일 시점에서만 가능하다.
실제로 코드를 실행하는 건 JavaScript이기 때문에, 런타임에서의 타입 에러는 막을 수 없다는 한계가 있다.
게다가 TypeScript는 number 타입까진 제한할 수 있어도,
특정 범위의 숫자나 특정 문자열만 허용하는 식의 정교한 제약은 어렵고, 정수/실수 구분도 불가능하다.
그래서 이런 부족한 부분을 보완해주는 게 바로 Zod 같은 런타임 타입 검증 라이브러리다.
pnpm add zod
pnpm add react-hook-form zod @hookform/resolvers
스키마란? 쉽게 말해서 데이터의 형태와 구조를 뜻한다.
// 예시
const schema = z.object({
name: z.string().min(3),
email: z.string().email(),
age: z.number(),
});
Zod 스키마를 정의하고 나면, 두 종류(parse, safeParse)의 스키마 함수를 통해 유효성 검사를 할 수 있다. 함수에 검증하고 싶은 값을 넘겨서 호출하면 검증 결과를 확인할 수 있다.
// 예시
schema.parse({
name: "jieun",
email: "email@email.com",
age: 20,
}); // 통과
schema.parse({
name: "jieun",
email: "email@email.com",
}); // 검증 실패
parse
: 오류가 발생하는 경우 오류 메시지와 함께 throw 함수를 호출해 서버가 중단된다.
safeParse
: 오류가 발생하는 경우, 서버 중단 없이 결과 객체에 오류 메시지 및 여러 정보를 전달하고, 개발자는 이 객체에서 프로퍼티를 추출해 검증 로직을 작성할 수 있다.
"use client";
import React from "react";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
type Props = {};
const ZodTestpage = (props: Props) => {
const schema = z.object({
name: z.string().min(10, "이름은 16글자 이상으로"),
email: z.string().email("이메일이 아닌 것 같아"), // 에러 메시지 작성 가능함
});
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
defaultValues: {
name: "",
email: "",
},
});
const onSubmit = (values: z.infer<typeof schema>) => {
console.log(values);
};
return (
<div>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="container mx-auto flex flex-col gap-3"
>
<input
type="text"
{...form.register("name")}
className="border-2 border-gray-300 rounded-md p-2"
/>
{form.formState.errors.name && (
<p className="text-red-500">{form.formState.errors.name.message}</p>
)}
<input
type="text"
{...form.register("email")}
className="border-2 border-gray-300 rounded-md p-2"
/>
{form.formState.errors.email && (
<p className="text-red-500">{form.formState.errors.email.message}</p>
)}
<button type="submit" className="bg-blue-500 rounded-md p-2">
submit
</button>
</form>
</div>
);
};
export default ZodTestpage;

이번 글에서는 Zustand와 Zod에 대해 간단히 알아보았다. 두 라이브러리 모두 사용법이 간단하면서도 실용성이 높아, 작은 사이드 프로젝트는 물론 실무에서도 충분히 활용할 수 있다.
부담 갖지 말고 가볍게 써보면서 익숙해져 보는 것도 좋을 것 같다.