
Zustand로 로그인 상태 관리하기👤
최근에 전역 상태 관리 라이브러리를 사용해보고 싶다는 생각을 했다. 프로젝트가 커지면서 상태 관리가 점점 더 복잡해졌고, React에서 제공하는 Context API로 해결하려니 코드가 점점 길어지고 관리가 힘들어졌다... Provider 쓰구,, 넘 불편쓰,,🚬 Redux도 고려했지만, 설정이 복잡하고 보일러플레이트 코드가 많아서 사용하기 쉽다고 많이 들어본 Zustand를 사용해 보기로 결심했다..! 😎
Zustand는 생각한 것보다 훨씬 간단하고 직관적이었다. 상태를 관리하기 위한 별도의 복잡한 설정 없이 스토어만 만들면 되는 점이 easy 뽀인트 + Context API보다 코드가 훨씬 간결해짐! 👍
이제 Zustand를 사용해 로그인 상태 관리와 로딩 화면 처리를 어떻게 구현했는지 사용법 정리해보겠돠!
0. Zustand란? 🤔
Zustand는 React 애플리케이션의 상태 관리를 간편하고 직관적으로 할 수 있는 경량 라이브러리다. Redux와 비슷하지만, 훨씬 간단하고 사용하기 쉬운 점이 특징이다. React Context와 useState의 장점을 결합한 형태로 작동해, 상태를 직접적으로 저장하고 변경할 수 있다.
https://zustand.docs.pmnd.rs/getting-started/introduction
Introduction - Zustand
How to use Zustand
zustand.docs.pmnd.rs
1. Zustand 설치
pnpm add zustand
2. 스토어 생성
import { getCookie } from 'cookies-next'
import { create } from 'zustand'
interface AuthStore {
isLoggedIn: boolean
accessToken: string | null
login: (token: string) => void
logout: () => void
}
export const useAuthStore = create<AuthStore>((set) => {
const token = getCookie('accessToken') as string | null
return {
isLoggedIn: !!token, // 초기 로그인 상태
accessToken: token, // 토큰 상태
login: (token: string) => set({ isLoggedIn: true, accessToken: token }), // 로그인 함수
logout: () => set({ isLoggedIn: false, accessToken: null }), // 로그아웃 함수
}
})
위 코드는 Zustand의 create 함수를 사용해 AuthStore라는 상태를 관리하는 useAuthStore 훅을 생성한 것이다.
여기서 useAuthStore는 로그인 상태를 전역적으로 관리할 수 있게 해준다. 로그인할 때 login 메서드를 호출하고, 로그아웃할 때는 logout 메서드를 사용한다. 중요한 건 쿠키에서 토큰을 읽어 로그인 상태를 자동으로 설정하는 점이다. 쿠키에 토큰이 있으면 바로 로그인된 상태로 처리된다. 🎉
이제 이 훅을 사용해서 어디서든 로그인 상태를 관리할 수 있돠!!
3. 로그인 페이지에서 Zustand 사용하기
사용자가 로그인 정보를 입력하고 로그인 버튼을 누르면, Zustand의 login 메서드를 호출해서 로그인 상태를 업데이트한다. (setLogin(token) 토큰을 전달해 로그인 상태임을 알려듐!!)
export default function Login() {
const [isLoading, setIsLoading] = useState(false)
const { login: setLogin } = useAuthStore()
const onSubmit = async (data: { email: string; password: string }) => {
setIsLoading(true)
try {
const res = await login(data)
const { token } = res
setCookie('accessToken', token, {
path: '/',
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
})
setLogin(token) // Zustand의 로그인 상태 업데이트
alert('로그인 성공!')
router.push('/home')
} catch (error) {
alert('로그인에 실패했습니다.')
console.error('로그인 에러:', error)
} finally {
setIsLoading(false)
}
}
if (isLoading) return <Loading /> // 로딩 컴포넌트 표시
return (
<div className="flex flex-col items-center justify-center">
<form onSubmit={handleSubmit(onSubmit)}>
{/* 로그인 폼 */}
</form>
</div>
)
}
4. 로딩 화면 구현
로그인 처리는 시간이 걸릴 수 있기 때문에, 사용자가 기다리는 동안 로딩 화면을 표시하는 것이 중요하다.! ((lottie로 컴포넌트 맹글어줌.!
import loadingAnimation from '@/assets/lottie/loading.json'
import dynamic from 'next/dynamic'
const Lottie = dynamic(() => import('lottie-react'), { ssr: false })
export default function Loading() {
return (
<div className="flex h-screen items-center justify-center">
<Lottie
animationData={loadingAnimation}
loop={true}
autoplay={true}
height={300}
width={300}
/>
</div>
)
}
[👊🏻에러 일지] - ReferenceError: document is not defined (feat. Next.js + lottie)
ReferenceError: document is not defined (feat. Next.js + lottie)
프로젝트 잘 진행하다가 dev실행했을 때 갑자기 오류가 났다.... 팀원이 lottie를 적용한 toastify를 수정 후 그 PR이 develop 브랜치에 머지 후에 갑자기 아래와 같은 오류가 발생했다.. 보니 lottie가 문
bori-note.tistory.com
5. 로그인 상태에 따른 페이지 리디렉션 처리
이제 로그인 상태를 구별할 수 있으니 페이지 리디렉션을 처리해보자!
로그인하지 않은 사용자가 보호된 페이지(메인 페이지나 프로필 페이지 같은..)에 접근하려고 하면 로그인 페이지로 리디렉션하고, 이미 로그인한 사용자가 로그인 페이지에 접근하면 메인 페이지로 리디렉션하도록 설정했다!
Next.js에서는 useRouter 훅을 이용하여 페이지를 제어할 수 있는데 여기에 Zustand로 관리한 로그인 상태를 결합해서 로그인 상태에 따라 페이지 리디렉션을 구현할 수 있돠!
export default function App({ Component, pageProps }: AppProps) {
const router = useRouter()
const noLayoutPages = ['/', '/login', '/signup']
const hideLayout = noLayoutPages.includes(router.pathname)
const { isLoggedIn } = useAuthStore()
const [loading, setLoading] = useState(true)
useEffect(() => {
// 로그인 상태가 아니면 로그인 페이지로 리디렉션
if (!isLoggedIn && !['/login', '/signup'].includes(router.pathname)) {
alert('로그인이 필요합니다!')
router.push('/login')
}
// 로그인한 사용자가 로그인 또는 회원가입 페이지에 접근하면 홈으로 리디렉션
if (
isLoggedIn &&
(router.pathname === '/login' || router.pathname === '/signup')
) {
router.push('/home')
} else {
setLoading(false)
}
}, [isLoggedIn, router, router.pathname])
if (loading) return <Loading />
return (
<div className="flex min-h-screen flex-col">
{!hideLayout && <Header />}
<main className="mt-20 flex-1">
<Component {...pageProps} />
</main>
{!hideLayout && <Footer />}
</div>
)
}
이제 테스트를 해보면 로그인하지 않은 유저가 주소창에 직접적으로 메인 페이지나 프로필 페이지에 접근하려하면 alert가 뜨고 로그인 페이지로 이동한다 🥳🥳🥳

'✨FRONTEND > 📍Next.js' 카테고리의 다른 글
| 드롭다운 메뉴 직접 구현하기 (0) | 2025.04.04 |
|---|---|
| Next.js 마크다운 미리보기 적용하기 (0) | 2025.03.30 |
| Vercel Organization 우회 배포 & PR Preview 설정하기 (1) | 2025.03.11 |
| Next.js App Router + PWA + Firebase로 알림 구현하기 (1) | 2025.01.26 |
| Chart.js 라이브러리로 달성률 시각화 구현하기(feat. Doughnut 차트) (0) | 2024.12.20 |