4.3 KiB
4.3 KiB
name, description
| name | description |
|---|---|
| api-pinia-store | Pinia 스토어를 팀 컨벤션에 맞게 생성할 때 사용합니다. "스토어 만들어줘", "Pinia 스토어", "전역 상태", "store 추가", "상태관리", "useAuthStore", "useCartStore" 등을 요청하면 트리거됩니다. |
Pinia 스토어 생성
이 skill은 Pinia 스토어를 팀 컨벤션에 맞게 생성합니다. Setup Store(Composition API) 문법을 기본으로 사용합니다.
작업 순서
-
스토어 필요성 판단
- 아래 판단 가이드를 참고하여 스토어가 적합한지 확인
- 서버 데이터 → composable(
useFetch), 로컬 상태 →ref/reactive권장
-
기존 스토어 탐색
stores/디렉토리에서 기존 스토어 패턴 확인- 중복 생성 방지, 기존 스토어 확장 가능 여부 검토
-
상태 설계
- TypeScript 인터페이스로 상태 형태 정의
- 최소한의 원시 상태만 저장, 파생 가능한 값은
computed
-
Setup Store 구현
- state →
ref/reactive - getters →
computed - actions → 일반 함수
- 모두 하나의 객체로 반환
- state →
-
persistence 추가 (필요 시)
pinia-plugin-persistedstate사용 여부 사용자 확인
-
검증
- TypeScript 오류 확인
- SSR hydration 호환성 확인
스토어 필요성 판단 가이드
| 상태의 성격 | 해결 방법 | 예시 |
|---|---|---|
| 서버 데이터 (API 응답) | composable + useFetch |
상품 목록, 유저 프로필 |
| 앱 전역 공유 상태 | Pinia 스토어 | 인증, 테마, 사이드바 상태 |
| 단일 페이지 폼 상태 | 로컬 ref/reactive |
회원가입 폼 |
| 부모↔자식 공유 상태 | props/emits 또는 provide/inject |
아코디언 그룹 |
| URL 기반 상태 | 라우트 쿼리/파라미터 | 필터, 정렬, 페이지 |
Setup Store 템플릿
// stores/useAuthStore.ts
import type { User } from '~/types/user';
export const useAuthStore = defineStore('auth', () => {
// state
const user = ref<User | null>(null);
const token = ref<string | null>(null);
// getters
const isLoggedIn = computed(() => !!token.value);
const userName = computed(() => user.value?.name ?? '');
// actions
async function login(credentials: LoginCredentials) {
const response = await $fetch<AuthResponse>('/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 상태 스토어 템플릿
// 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 적용 패턴
// stores/useAuthStore.ts
export const useAuthStore = defineStore('auth', () => {
const token = ref<string | null>(null);
// ... 생략
return { token };
}, {
persist: {
pick: ['token'],
},
});
스토어 네이밍 규칙
- 파일:
stores/use[Domain]Store.ts - 함수:
use[Domain]Store - defineStore ID: 소문자 도메인명 (예:
'auth','cart','ui')
주의사항
- Option Store 대신 Setup Store 사용:
<script setup>패턴과의 일관성 - 서버 데이터를 Pinia에 저장하지 않음:
useFetch/useAsyncData영역 - SSR hydration: 스토어 상태는 HTML로 직렬화됨. 함수, DOM 참조, 클래스 인스턴스 등 직렬화 불가능한 값 저장 금지
- 스토어 간 순환 참조 금지: A → B → A 형태의 의존성 제거
- 대형 모놀리식 스토어 지양: 도메인별 분리 (auth, ui, cart 등)
- Action 내 직접 API 호출 주의: 프로젝트 규칙에 따라 composable에서 호출 후 결과만 저장하는 패턴 확인
any타입 사용 금지 (rules/coding-conventions.md참조)