--- name: api-pinia-store description: Pinia 스토어를 팀 컨벤션에 맞게 생성할 때 사용합니다. "스토어 만들어줘", "Pinia 스토어", "전역 상태", "store 추가", "상태관리", "useAuthStore", "useCartStore" 등을 요청하면 트리거됩니다. --- # Pinia 스토어 생성 이 skill은 Pinia 스토어를 팀 컨벤션에 맞게 생성합니다. **Setup Store(Composition API) 문법**을 기본으로 사용합니다. ## 작업 순서 1. **스토어 필요성 판단** - 아래 판단 가이드를 참고하여 스토어가 적합한지 확인 - 서버 데이터 → composable(`useFetch`), 로컬 상태 → `ref`/`reactive` 권장 2. **기존 스토어 탐색** - `stores/` 디렉토리에서 기존 스토어 패턴 확인 - 중복 생성 방지, 기존 스토어 확장 가능 여부 검토 3. **상태 설계** - TypeScript 인터페이스로 상태 형태 정의 - 최소한의 원시 상태만 저장, 파생 가능한 값은 `computed` 4. **Setup Store 구현** - state → `ref`/`reactive` - getters → `computed` - actions → 일반 함수 - 모두 하나의 객체로 반환 5. **persistence 추가 (필요 시)** - `pinia-plugin-persistedstate` 사용 여부 사용자 확인 6. **검증** - TypeScript 오류 확인 - SSR hydration 호환성 확인 ## 스토어 필요성 판단 가이드 | 상태의 성격 | 해결 방법 | 예시 | | --- | --- | --- | | 서버 데이터 (API 응답) | composable + `useFetch` | 상품 목록, 유저 프로필 | | 앱 전역 공유 상태 | **Pinia 스토어** | 인증, 테마, 사이드바 상태 | | 단일 페이지 폼 상태 | 로컬 `ref`/`reactive` | 회원가입 폼 | | 부모↔자식 공유 상태 | props/emits 또는 `provide/inject` | 아코디언 그룹 | | URL 기반 상태 | 라우트 쿼리/파라미터 | 필터, 정렬, 페이지 | ## Setup Store 템플릿 ```ts // stores/useAuthStore.ts import type { User } from '~/types/user'; export const useAuthStore = defineStore('auth', () => { // state const user = ref(null); const token = ref(null); // getters const isLoggedIn = computed(() => !!token.value); const userName = computed(() => user.value?.name ?? ''); // actions async function login(credentials: LoginCredentials) { const response = await $fetch('/api/auth/login', { method: 'POST', body: credentials, }); user.value = response.user; token.value = response.token; } function logout() { user.value = null; token.value = null; navigateTo('/login'); } return { // state user, token, // getters isLoggedIn, userName, // actions login, logout, }; }); ``` ## UI 상태 스토어 템플릿 ```ts // stores/useUiStore.ts export const useUiStore = defineStore('ui', () => { const isSidebarOpen = ref(false); const theme = ref<'light' | 'dark'>('light'); function toggleSidebar() { isSidebarOpen.value = !isSidebarOpen.value; } function setTheme(newTheme: 'light' | 'dark') { theme.value = newTheme; } return { isSidebarOpen, theme, toggleSidebar, setTheme, }; }); ``` ## Persistence 적용 패턴 ```ts // stores/useAuthStore.ts export const useAuthStore = defineStore('auth', () => { const token = ref(null); // ... 생략 return { token }; }, { persist: { pick: ['token'], }, }); ``` ## 스토어 네이밍 규칙 - 파일: `stores/use[Domain]Store.ts` - 함수: `use[Domain]Store` - defineStore ID: 소문자 도메인명 (예: `'auth'`, `'cart'`, `'ui'`) ## 주의사항 - **Option Store 대신 Setup Store 사용**: `