dev.syw

useState로 SSR 친화 상태 공유, Pinia 스토어, 컴포저블 분리.

상태 관리

여러 컴포넌트가 같은 데이터를 공유해야 할 때 상태 관리가 필요합니다. Nuxt는 SSR을 고려한 useState 를 기본 제공하며, 더 큰 앱에는 Pinia를 권장합니다. 이번 레슨에서는 두 방법과 컴포저블로 상태를 정리하는 패턴을 다룹니다.

학습 목표

  • useState 가 일반 ref 와 무엇이 다른지 이해한다.
  • SSR 환경에서 상태가 안전하게 공유되는 이유를 안다.
  • Pinia를 설치하고 스토어를 만든다.
  • 컴포저블로 상태 로직을 분리한다.

왜 useState 인가

SSR 환경에서 컴포넌트 밖에 전역 ref 를 두면 모든 사용자가 같은 변수를 공유 해 데이터가 섞일 수 있습니다. useState 는 요청마다 격리된 상태를 만들고, SSR에서 만든 값을 클라이언트로 안전하게 전달합니다.

<script setup>
// 첫 인자는 고유 키, 둘째는 초기값 함수
const count = useState('count', () => 0);
</script>

<template>
  <button @click="count++">클릭 {{ count }}</button>
</template>

같은 키 'count' 를 쓰는 다른 컴포넌트는 같은 상태 를 공유합니다.

💡 TIPuseState 는 SSR로 직렬화되므로 함수·클래스 인스턴스 같은 직렬화 불가능한 값은 담지 마세요. 순수 데이터만 담습니다.

컴포저블로 상태 분리

useState 호출을 컴포저블로 감싸면 재사용과 타입이 깔끔해집니다.

// composables/useCounter.ts
export function useCounter() {
  const count = useState('count', () => 0);

  const increment = () => count.value++;
  const reset = () => (count.value = 0);

  return { count, increment, reset };
}
<script setup>
// composables/ 안의 함수는 자동 임포트됨
const { count, increment } = useCounter();
</script>

<template>
  <button @click="increment">{{ count }}</button>
</template>

Pinia 도입

상태가 많아지고 액션·게터가 복잡해지면 Pinia 가 더 적합합니다. Nuxt 모듈로 손쉽게 설치합니다.

npx nuxi module add pinia

nuxt.config.ts 에 모듈이 자동 등록됩니다.

export default defineNuxtConfig({
  modules: ['@pinia/nuxt'],
});

스토어 만들기

stores/ 폴더에 스토어를 정의합니다. 셋업 스타일이 <script setup> 과 가장 잘 어울립니다.

// stores/tasks.ts
export const useTasksStore = defineStore('tasks', () => {
  const items = ref([]);

  const count = computed(() => items.value.length);

  async function load() {
    items.value = await $fetch('/api/tasks');
  }

  function add(title: string) {
    items.value.push({ id: Date.now(), title });
  }

  return { items, count, load, add };
});
<script setup>
const store = useTasksStore();
await store.load();
</script>

<template>
  <p>총 {{ store.count }}개</p>
  <ul>
    <li v-for="t in store.items" :key="t.id">{{ t.title }}</li>
  </ul>
  <button @click="store.add('새 일감')">추가</button>
</template>

⚠️ 주의 — 스토어 상태를 구조 분해하면 반응성이 끊깁니다. 값만 따로 꺼내려면 storeToRefs(store) 를 쓰세요.

useState vs Pinia

기준useStatePinia
설치내장 (불필요)모듈 추가 필요
적합한 규모가벼운 공유 상태액션·게터 많은 도메인 상태
구조컴포저블로 직접 정리스토어 단위로 체계화
개발자 도구제한적Vue Devtools 지원

작은 앱이나 단순 공유 값은 useState, 비즈니스 로직이 모이는 큰 앱은 Pinia가 무난합니다.

요약

  • useState 는 요청마다 격리되고 SSR로 전달되는 공유 상태입니다.
  • 같은 키를 쓰면 여러 컴포넌트가 상태를 공유합니다.
  • 컴포저블로 useState 를 감싸면 재사용성이 좋아집니다.
  • 큰 앱에서는 @pinia/nuxt 모듈과 defineStore 로 체계적으로 관리합니다.

연습문제

  1. useState('theme', () => 'light') 로 다크/라이트 테마 상태를 만들고 토글 버튼을 붙여 보세요.
  2. composables/useCounter.ts 를 만들어 두 컴포넌트에서 같은 카운터를 공유하게 해보세요.
  3. Pinia를 설치하고 stores/tasks.ts 로 일감 목록 스토어를 만들어 보세요.
  4. storeToRefs 를 써서 스토어의 count 를 반응성을 유지한 채 꺼내 화면에 표시해 보세요.

힌트 — 전역 ref 대신 useState(키 필요), 큰 상태는 defineStore 를 떠올리세요.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

Nuxt.js” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.