1. 상태 관리(useState)

Vue 2.x 버전을 썼던 사용자라면 Vuex 상태관리 라이브러리를 알 것이다. Nuxt 에서 SSR이 제공되고 Vue 3 에서는 Composite API 가 사용되면서 새로운 라이브러리로 대체되었다.

 

복잡한 어플리케이션을 작성하다보면 컴포넌트 혹은 페이지간 동일한 상태(값)에 대한 접근이 필요할 때가 있다.  Nuxt 는 useState 라는 SSR 친화적인 상태관리 컴포저블을 제공하고 있다.

 

자세한 내용은 Nuxt-개요 > 11. 상태관리 를 참고하자.

 

전역 상태 정의하기

useState 는 상태 참조를 키 값을 통해서 한다. 여기서는 userState 를 이용해 사용자 정의 상태관리자를 생성한다.

프로젝트 루트에서 /composable/states.ts 파일을 생성하고 아래와 같이 코딩한다.

 

/composables/states.ts

export const useCounter = () => useState<number>("couter", () => 0);
export const useColor = () => useState<string>("color", () =>  'pink');

위 상태 관리자를 페이지에서 호출해보자. /pages/states.vue 파일을 생성하고 아래와 같이 코딩한다.

<script setup lang="ts">
const counter = useCounter();
const color = useColor();
</script>
<template>
  <div>
    Counter: {{ counter }}
    <button class="bg-green-500 text-white px-2 mx-2 rounded" @click="counter++">
      +
    </button>
    <button class="bg-red-500 text-white px-2 mx-2 rounded" @click="counter--">
      -
    </button>
  </div>
  <div>
    Color: <span :style="{backgroundColor: color}">{{ color }}</span>
    <div :style="{backgroundColor: color}">
      Background Color
    </div>
  </div>
</template>

 

http://localhost:3000/states 화면을 열어서 '+', '-' 버튼을 눌러보자. 상태 값이 변하는 걸 확인할 수 있다.

 

복잡한 상태관리

어플리케이션 복잡도가 올라가면서 전역상태의 복잡도도 증가할 수 있다. useState 만으로도 충분하지만, 섬세한 상태관리가 필요할 때가 있다. 예를 들면 상태에 접근하는 행위를 제한하고 싶을 때 상태제어 행위(action) 을 정의해서 상태관리를 제한함과 동시에 정교하게 제어할 수 있다. 이럴 때는 Nuxt 에서 공식적으로 권장하는 피니아를 사용하자.

 

피니아를 사용한 상태관리

위 예제 couter 상태를 pinia 로 대체해보자.

설치

yarn add @pinia/nuxt
# or with npm
npm install @pinia/nuxt

 

모듈 설정

nuxt.config.ts 에서 modules 속성에 pinia 모듈 사용을 추가한다.

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: true },
  css: [
      "~/assets/main.css"
  ],
  modules:[
    '@pinia/nuxt'
  ],
  postcss: {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  },
})

 

pinia 스토어 생성

프로젝트 루트에서 stores/ 디렉터리를 생성하고, 해당 디렉터리에서 counter.ts 파일을 생성해서 아래와 같이 코딩한다.

 

/stores/counter.ts

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
    state: () => {
        return { count: 0 }
    },
    // could also be defined as
    // state: () => ({ count: 0 })
    actions: {
        increment() {
            this.count++;
        },
        decrement() {
            this.count--;
        }
    },
})

 

couterStore 를 쓸 수 있게 states.vue 소스를 변경하자.

 

/pages/states.vue

<script setup lang="ts">
import {useCounterStore} from "~/stores/counter";

const store = useCounterStore();
const color = useColor();
</script>
<template>
  <div>
    Counter: {{ store.count }}
    <button class="bg-green-500 text-white px-2 mx-2 rounded" @click="store.increment()">
      +
    </button>
    <button class="bg-red-500 text-white px-2 mx-2 rounded" @click="store.decrement()">
      -
    </button>
  </div>
  <div>
    Color: <span :style="{backgroundColor: color}">{{ color }}</span>
    <div :style="{backgroundColor: color}">
      Background Color
    </div>
  </div>
</template>

결과는 아래와 같이 동일하다.

 

아래 임포트 문이 추가 되었다.

import {useCounterStore} from "~/stores/counter";

 

상태관리자로 pinia 를 사용하기로 결정했다면 stores/ 디렉토리에 있는 스토어들을 자동으로 임포트 하도록 구성할 수 있다. nuxt.config.ts 를 아래와 같이 수정하자.

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
...
  modules: [
    '@pinia/nuxt'
  ],
  imports: {
    dirs: ['./stores/**'],
  },
...
})

 

소스가 아래처럼 깔끔해졌다.

<script setup lang="ts">
const store = useCounterStore();
const color = useColor();
</script>
<template>
  <div>
    Counter: {{ store.count }}
    <button class="bg-green-500 text-white px-2 mx-2 rounded" @click="store.increment()">
      +
    </button>
    <button class="bg-red-500 text-white px-2 mx-2 rounded" @click="store.decrement()">
      -
    </button>
  </div>
  ...
</template>
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유