Files
2nd/10_Wiki/Topics/Frontend/Pinia.md
T
2026-05-10 22:08:15 +09:00

230 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: wiki-2026-0508-pinia
title: Pinia
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Vue Store, Pinia Store]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [vue, state-management, frontend]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: TypeScript
framework: Vue 3 / Pinia 2
---
# Pinia
## 매 한 줄
> **"매 Vue 3 매 official state management — 매 Vuex 4 후속, 매 Composition API native + 매 TypeScript-first"**. Eduardo San Martin Morote 매 2019 발표 매 Vue Core 팀 채택, 매 store 매 composable function 매 표현 매 boilerplate 90% 감소, 매 2026 매 Vue 표준 store library.
## 매 핵심
### 매 vs Vuex
- **No mutations**: 매 actions 가 directly state 수정 — 매 mutation indirection 제거.
- **Flat stores**: 매 nested module 매 X — 매 cross-store import 매 graph 형성.
- **Type inference**: 매 zero manual typing — 매 store usage 매 fully typed.
- **Devtools**: 매 timeline + state inspect + time-travel 지원.
### 매 store kinds
- **Options Store**: `state`, `getters`, `actions` — 매 Vuex-like familiar shape.
- **Setup Store**: `ref`/`computed`/`function` — 매 Composition API native.
### 매 응용
1. SPA global state (auth, user prefs).
2. Server-side rendering (Nuxt 3 매 native integration).
3. Cross-component caching (API result, derived state).
4. Plugin extension (persistedstate, undo/redo).
## 💻 패턴
### Setup store (modern preferred)
```ts
// stores/counter.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() { count.value++ }
function reset() { count.value = 0 }
return { count, doubled, increment, reset }
})
```
### Options store
```ts
export const useUserStore = defineStore('user', {
state: () => ({
user: null as User | null,
loading: false,
}),
getters: {
isLoggedIn: (s) => s.user !== null,
displayName: (s) => s.user?.name ?? 'Guest',
},
actions: {
async login(creds: Credentials) {
this.loading = true
try {
this.user = await api.login(creds)
} finally {
this.loading = false
}
},
logout() { this.user = null },
},
})
```
### Component usage
```vue
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
// 매 reactivity 매 유지: storeToRefs 매 ref 변환
const { count, doubled } = storeToRefs(store)
// actions 매 destructure OK (not reactive)
const { increment } = store
</script>
<template>
<button @click="increment">{{ count }} (×2 = {{ doubled }})</button>
</template>
```
### App setup (Vue 3)
```ts
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
```
### Cross-store usage
```ts
import { useAuthStore } from './auth'
export const useCartStore = defineStore('cart', () => {
const auth = useAuthStore() // 매 다른 store 매 import + use
const items = ref<CartItem[]>([])
async function checkout() {
if (!auth.isLoggedIn) throw new Error('Login required')
return api.checkout(auth.user!.id, items.value)
}
return { items, checkout }
})
```
### $patch 매 batch update
```ts
const store = useUserStore()
store.$patch({ loading: false, user: newUser }) // 매 single devtools entry
// or function form for complex mutations
store.$patch((state) => {
state.cart.push(item)
state.lastUpdated = Date.now()
})
```
### $subscribe 매 store mutation 감지
```ts
store.$subscribe((mutation, state) => {
localStorage.setItem('cart', JSON.stringify(state))
}, { detached: true })
```
### Plugin (persistedstate)
```ts
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPersist)
// store 정의 시:
export const useUserStore = defineStore('user', {
state: () => ({ token: '' }),
persist: true, // localStorage 매 자동 sync
})
```
### SSR (Nuxt 3)
```ts
// composables/useAuth.ts
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null)
// 매 Nuxt 3 매 자동 hydration — server 매 set 한 state 매 client 매 transfer
return { user }
})
```
### Testing
```ts
import { setActivePinia, createPinia } from 'pinia'
import { beforeEach, expect, test } from 'vitest'
beforeEach(() => setActivePinia(createPinia()))
test('counter increments', () => {
const store = useCounterStore()
expect(store.count).toBe(0)
store.increment()
expect(store.count).toBe(1)
expect(store.doubled).toBe(2)
})
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Vue 3 new project | Pinia (default) |
| Vuex 4 migration | Pinia (incremental, alias module) |
| Composition API preference | Setup store |
| Vuex-familiar team | Options store |
| Component-local state | `ref`/`reactive` (no store) |
| Server state caching | TanStack Query / 매 store 의 X |
| SSR (Nuxt) | Pinia (built-in support) |
**기본값**: 매 setup store + TypeScript + storeToRefs.
## 🔗 Graph
- 부모: [[Vue]] · [[State-Management]]
- 변형: [[Vuex]] (predecessor) · [[Redux]] · [[Zustand]] · [[Jotai]]
- 응용: [[Nuxt]] · [[Vue-SSR]]
- Adjacent: [[Composition-API]] · [[TanStack-Query]] · [[Reactivity]]
## 🤖 LLM 활용
**언제**: 매 Vue 3 global state, 매 cross-component sharing, 매 Nuxt 3 SSR state, 매 Vuex migration target.
**언제 X**: 매 component-local state (ref 만 충분), 매 server cache (TanStack Query 적합), 매 React project (Zustand/Redux).
## ❌ 안티패턴
- **storeToRefs 없이 destructure**: 매 reactivity loss — 매 `const { count } = store` 의 X.
- **Store action 매 component logic 침범**: 매 store 매 단순 state holder 매 됨 — 매 business logic 매 store 에 응집.
- **Nested store hierarchy 시도**: 매 flat 의 X — 매 cross-import 매 graph 형성.
- **Mutation pattern 의 반복**: 매 Vuex habit — 매 action 에서 직접 state 수정 OK.
- **매 component 매 store instance 다중 생성**: 매 useStore() 매 singleton — 매 매 호출 동일 instance.
## 🧪 검증 / 중복
- Verified (Pinia 2.x docs, Vue.js official).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — setup/options store + cross-store + SSR + plugin patterns |