Ch 04. Pinia로 전역 상태 관리하기
앞 챕터에서 살펴본 Composable은 컴포넌트마다 독립적인 인스턴스를 만들었습니다. 그런데 로그인 상태처럼 앱 전체에서 하나의 값을 공유해야 하는 경우에는 어떻게 해야 할까요? 이럴 때 필요한 것이 Pinia입니다.
Pinia vs Vuex
Vue 2 시대에는 Vuex가 공식 상태 관리 라이브러리였습니다. Pinia는 그 후계자로, 더 가볍고 TypeScript 친화적으로 설계되었습니다. Vuex의 mutations처럼 상태를 변경하기 위한 별도 레이어가 없어 코드가 간결합니다. Vue Devtools에서 타임라인 추적도 완전히 지원합니다.
설치와 설정
Pinia를 설치하고 Nuxt 모듈로 등록합니다.
npm install pinia @pinia/nuxt
// nuxt.config.tsexport default defineNuxtConfig({ modules: ['@pinia/nuxt']})
@pinia/nuxt 모듈이 등록되면 stores/ 폴더의 스토어 파일이 자동으로 임포트됩니다.
Store 정의하기
Setup Store 방식은 Composition API와 동일한 문법을 사용합니다. ref, computed, 함수를 조합해서 스토어를 정의하고 원하는 것을 반환하면 됩니다.
// stores/auth.tsimport { defineStore } from 'pinia'export const useAuthStore = defineStore('auth', () => { const user = ref<{ id: number; name: string } | null>(null) const isLoggedIn = computed(() => user.value !== null) function login(userData: { id: number; name: string }) { user.value = userData } function logout() { user.value = null } return { user, isLoggedIn, login, logout }})
defineStore의 첫 번째 인자는 스토어를 구분하는 고유 ID입니다. Vue Devtools에서 이 ID로 스토어를 식별합니다.
컴포넌트에서 사용하기
<script setup lang="ts">
const authStore = useAuthStore()
const { user, isLoggedIn } = storeToRefs(authStore)
</script>
<template>
<div v-if="isLoggedIn">
<p>안녕하세요, {{ user?.name }}님.</p>
<button @click="authStore.logout()">로그아웃</button>
</div>
<div v-else>
<button @click="authStore.login({ id: 1, name: '홍길동' })">로그인</button>
</div>
</template>
storeToRefs는 스토어의 상태와 computed 값을 반응성을 유지한 채 구조분해할 때 사용합니다. toRefs와 비슷하지만 Pinia 스토어 전용으로, 메서드는 포함되지 않습니다. 메서드는 스토어 객체에서 직접 호출합니다.
Vue Devtools에서 Pinia 확인하기
Vue Devtools의 Pinia 탭에서 모든 스토어의 현재 상태를 실시간으로 확인할 수 있습니다. 상태를 직접 수정하거나 Action 호출 이력을 타임라인으로 추적하는 기능도 제공합니다. 디버깅할 때 매우 유용합니다.