레슨 7 / 9·20분
Pinia 상태 관리
Pinia란?
Pinia는 Vue.js 공식 상태 관리 라이브러리입니다. Vuex의 후속으로, TypeScript 지원이 우수하고 Composition API와 자연스럽게 통합됩니다. Store를 정의하면 어떤 컴포넌트에서든 접근할 수 있는 전역 상태를 만들 수 있습니다.
typescript
// stores/counter.ts — Setup Store 방식 (Composition API 스타일)
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useCounterStore = defineStore('counter', () => {
// state
const count = ref(0);
// getters (computed)
const doubleCount = computed(() => count.value * 2);
const isPositive = computed(() => count.value > 0);
// actions (함수)
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
function reset() {
count.value = 0;
}
async function incrementAsync() {
await new Promise(r => setTimeout(r, 1000));
count.value++;
}
return {
count, doubleCount, isPositive,
increment, decrement, reset, incrementAsync,
};
});컴포넌트에서 Store 사용
vue
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
const store = useCounterStore();
// 반응성을 유지하며 구조 분해 — storeToRefs 사용
const { count, doubleCount } = storeToRefs(store);
// actions는 직접 구조 분해 가능
const { increment, decrement, reset } = store;
</script>
<template>
<div>
<p>카운트: {{ count }}</p>
<p>2배: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="reset">초기화</button>
</div>
</template>실전 예제: Todo Store
typescript
// stores/todo.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
type Todo = {
id: number;
text: string;
done: boolean;
};
export const useTodoStore = defineStore('todo', () => {
const todos = ref<Todo[]>([]);
let nextId = 1;
const remaining = computed(
() => todos.value.filter(t => !t.done).length
);
const completed = computed(
() => todos.value.filter(t => t.done)
);
function addTodo(text: string) {
todos.value.push({ id: nextId++, text, done: false });
}
function toggleTodo(id: number) {
const todo = todos.value.find(t => t.id === id);
if (todo) todo.done = !todo.done;
}
function removeTodo(id: number) {
todos.value = todos.value.filter(t => t.id !== id);
}
function clearCompleted() {
todos.value = todos.value.filter(t => !t.done);
}
return {
todos, remaining, completed,
addTodo, toggleTodo, removeTodo, clearCompleted,
};
});💡
storeToRefs()를 사용하지 않고 const { count } = store 처럼 직접 구조 분해하면 반응성이 사라집니다. state와 getters는 반드시 storeToRefs()로, actions는 직접 구조 분해하세요.