[JavaScript][Vue.js] Pinia와 Store 정리 - 전역 상태 관리 이해하기

2026. 8. 26. 16:57·개발/JavaScript
반응형

Vue 애플리케이션이 커지면 여러 컴포넌트가 같은 데이터를 함께 사용해야 하는 경우가 많아진다.

예를 들어 다음과 같은 데이터가 있다.

로그인 사용자 정보
장바구니
알림 개수
다크모드 설정
날씨 단위

이런 데이터를 부모에서 자식으로 계속 Props로 전달하면 구조가 복잡해질 수 있다.

App
 ↓
Layout
 ↓
Header
 ↓
Menu
 ↓
UserInfo

중간 컴포넌트가 데이터를 사용하지 않더라도 계속 전달해야 하는 문제가 생긴다.

이럴 때 사용하는 것이 Pinia다.

Pinia는 Vue 애플리케이션에서 여러 컴포넌트가 함께 사용하는 데이터를 중앙에서 관리하기 위한 전역 상태 관리 라이브러리다.


Pinia란?

Pinia는 여러 컴포넌트가 같은 상태를 공유할 수 있도록 Store라는 중앙 저장소를 제공한다.

구조는 다음과 같다.

Component A ─┐
             │
Component B ─┼──→ Store
             │
Component C ─┘

각 컴포넌트가 서로 직접 데이터를 전달하지 않고 같은 Store를 바라보는 방식이다.

예를 들어 로그인 정보를 Store에 저장하면 다음과 같이 사용할 수 있다.

Header
→ 로그인 사용자 이름 표시

MyPage
→ 사용자 정보 표시

Navigation Guard
→ 로그인 여부 확인

모두 같은 로그인 상태를 공유할 수 있다.


Store란?

Store는 여러 컴포넌트가 함께 사용할 상태와 로직을 보관하는 저장소다.

예를 들어 로그인 Store라면 다음과 같은 데이터를 관리할 수 있다.

authStore

user
token
isLoggedIn
login()
logout()

즉 Store에는 단순한 데이터만 들어가는 것이 아니라, 데이터를 가공하거나 변경하는 로직도 함께 들어간다.

Pinia Store의 핵심은 크게 세 가지로 나눌 수 있다.

State
Getters
Actions

Vue의 Composition API와 연결하면 다음과 같다.

Pinia         Vue

State    →    ref / reactive
Getters  →    computed
Actions  →    function

State

State는 Store가 가지고 있는 원본 데이터다.

예를 들어 카운터 Store라면 다음과 같다.

const count = ref(0)

여기서 count가 State다.

로그인 Store라면 다음과 같은 값들이 State가 될 수 있다.

const user = ref(null)
const token = ref(null)

정리하면:

State는 애플리케이션에서 여러 컴포넌트가 공유하는 원본 상태다.


Getters

Getters는 State를 기반으로 계산한 값이다.

Vue의 computed()와 같은 역할을 한다.

예를 들어:

const count = ref(10)

const doubleCount = computed(() => {
  return count.value * 2
})

여기서:

count
→ State

doubleCount
→ Getter

가 된다.

로그인 여부도 Getter로 만들 수 있다.

const isLoggedIn = computed(() => {
  return token.value !== null
})

즉 Getter는 원본 상태를 직접 저장하는 것이 아니라 State를 이용해 만들어진 계산 결과다.


Actions

Actions는 State를 변경하거나 특정 로직을 실행하는 함수다.

예를 들어:

const increment = () => {
  count.value++
}

이 함수가 Action이다.

로그인 Store라면 다음처럼 작성할 수 있다.

const login = (userData, authToken) => {
  user.value = userData
  token.value = authToken
}

로그아웃은 다음과 같이 만들 수 있다.

const logout = () => {
  user.value = null
  token.value = null
}

즉:

Action은 Store의 State를 변경하거나 서버 통신 같은 로직을 처리하는 함수다.


Pinia Store 만들기

예를 들어 카운터 Store를 만든다고 해보자.

파일 구조는 다음과 같이 만들 수 있다.

src/
└─ stores/
   └─ counter.js

Store 코드는 다음과 같다.

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {

  const count = ref(0)

  const doubleCount = computed(() => {
    return count.value * 2
  })

  const increment = () => {
    count.value++
  }

  return {
    count,
    doubleCount,
    increment,
  }
})

구조를 정리하면 다음과 같다.

useCounterStore

├─ count
│   → State
│
├─ doubleCount
│   → Getter
│
└─ increment()
    → Action

defineStore()는 Pinia Store를 만드는 함수다.

defineStore('counter', () => {
  ...
})

첫 번째 값인 'counter'는 Store를 구분하는 고유한 이름이다.


컴포넌트에서 Store 사용하기

Store를 사용할 컴포넌트에서는 먼저 import한다.

<script setup>
import { useCounterStore } from '@/stores/counter'

const counterStore = useCounterStore()
</script>

이제 Template에서 State, Getter, Action을 사용할 수 있다.

<template>
  <p>
    Count: {{ counterStore.count }}
  </p>

  <p>
    Double: {{ counterStore.doubleCount }}
  </p>

  <button @click="counterStore.increment">
    증가
  </button>
</template>

동작 구조는 다음과 같다.

Component
   ↓
useCounterStore()
   ↓
Store 접근
   ↓
State 읽기
Getter 사용
Action 실행

main.js에서 Pinia 등록하기

Pinia를 사용하려면 Vue 애플리케이션에 먼저 등록해야 한다.

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'

const app = createApp(App)

app.use(createPinia())

app.mount('#app')

여기서:

createPinia()

는 Pinia 인스턴스를 만들고,

app.use()

를 통해 Vue 애플리케이션에 등록한다.

구조는 다음과 같다.

createPinia()
      ↓
Pinia 생성
      ↓
app.use()
      ↓
Vue Application에 등록

날씨 프로젝트에 적용하기

예를 들어 날씨 프로젝트에서 섭씨와 화씨 설정을 여러 화면에서 함께 사용한다고 해보자.

다음과 같은 Store를 만들 수 있다.

configStore

unit
→ celsius / fahrenheit

unitSymbol
→ ℃ / ℉

toggleUnit()
→ 단위 변경

코드는 다음과 같이 구성할 수 있다.

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useConfigStore = defineStore('config', () => {

  const unit = ref('celsius')

  const unitSymbol = computed(() => {
    return unit.value === 'celsius'
      ? '℃'
      : '℉'
  })

  const toggleUnit = () => {
    unit.value =
      unit.value === 'celsius'
        ? 'fahrenheit'
        : 'celsius'
  }

  return {
    unit,
    unitSymbol,
    toggleUnit,
  }
})

여기서 역할은 다음과 같다.

unit
→ State

unitSymbol
→ Getter

toggleUnit()
→ Action

이 Store를 여러 컴포넌트에서 사용할 수 있다.

             configStore
                 │
       ┌─────────┼─────────┐
       ▼         ▼         ▼

UnitToggler  HomeView   DetailView

예를 들어 UnitToggler에서:

configStore.toggleUnit()

을 실행하면 Store의 값이 변경된다.

그리고 HomeView, DetailView도 같은 Store를 사용하고 있다면 해당 값이 함께 반영된다.


Props와 Pinia 차이

Props는 부모와 자식 관계에서 데이터를 전달할 때 사용한다.

Parent
   ↓ Props
Child

Pinia는 컴포넌트의 부모·자식 관계와 상관없이 공통 상태를 공유할 때 사용한다.

Component A
     │
     ▼
    Store
     ▲
     │
Component B

비교하면 다음과 같다.

구분 Props Pinia
범위 부모 → 자식 애플리케이션 전역
목적 컴포넌트 데이터 전달 전역 상태 관리
관계 부모·자식 구조 필요 관계 없어도 됨
대표 예 WeatherCard 데이터 로그인, 장바구니, 설정

즉 단순히 자식에게 필요한 데이터를 전달한다면 Props를 사용하고, 여러 페이지나 여러 컴포넌트에서 같은 데이터를 공유해야 한다면 Pinia를 사용하는 구조다.


provide / inject와 Pinia 차이

provide / inject 역시 깊은 컴포넌트로 데이터를 전달할 수 있다.

하지만 범위가 다르다.

provide / inject
→ 특정 컴포넌트 트리 안에서 공유

Pinia
→ 애플리케이션 전체에서 공유

예를 들어 특정 Form 내부 설정값을 여러 하위 컴포넌트가 사용하는 경우에는 provide / inject를 사용할 수 있다.

반면 다음과 같은 데이터는 Pinia가 적합하다.

로그인 상태
사용자 정보
장바구니
전역 테마
알림 상태
공통 설정값

Pinia의 전체 구조

Pinia 구조를 한 번에 보면 다음과 같다.

                  Pinia

                   │
                 Store
                   │
        ┌──────────┼──────────┐
        │          │          │
        ▼          ▼          ▼

      State      Getter      Action

      ref()     computed()   function

        │          │          │
        └──────────┴──────────┘
                   │
                   ▼

              Components

컴포넌트들은 각각 Store를 불러와 같은 상태를 공유한다.

          Component A
              │
              ▼
          ┌─────────┐
          │  Store  │
          └─────────┘
          ▲    ▲    ▲
          │    │    │
 Component B   │  Component C
               │
          Component D

이 구조 덕분에 컴포넌트 간에 Props를 여러 단계로 전달하지 않고도 공통 상태를 사용할 수 있다.


정리

Pinia는 Vue 애플리케이션의 전역 상태 관리 라이브러리다.

그리고 Store는 Pinia 안에서 실제 데이터를 관리하는 저장소다.

Store의 핵심은 다음 세 가지다.

State
→ 원본 상태
→ ref / reactive

Getters
→ State를 이용한 계산값
→ computed

Actions
→ State 변경 및 로직 실행
→ function

Vue Composition API와 연결하면 다음처럼 이해할 수 있다.

Vue Component

ref()
computed()
function()

      ↓

Pinia Store로 이동

      ↓

State
Getter
Action

즉 기존에는 각 컴포넌트가 자기 상태를 가지고 있었다면, 여러 컴포넌트가 공통으로 사용해야 하는 상태를 Store로 끌어올려 중앙에서 관리하는 것이다.

한 문장으로 정리하면 다음과 같다.

Pinia는 Vue 애플리케이션의 여러 컴포넌트가 함께 사용할 상태를 중앙에서 관리하는 도구이며, Store는 그 안에서 State, Getter, Action을 관리하는 전역 저장소다.

반응형

'개발 > JavaScript' 카테고리의 다른 글

[JavaScript][Vue.js] Slot 정리 - 부모가 자식 컴포넌트에 UI를 주입하는 방법  (0) 2026.08.26
[JavaScript][Vue.js] provide / inject 정리 - 깊은 컴포넌트 간 데이터 전달  (0) 2026.08.26
[JavaScript][Vue.js] Props와 Emits 정리 - 부모·자식 컴포넌트 통신  (0) 2026.08.26
[JavaScript][Vue.js] Component Lifecycle과 Lifecycle Hook 정리  (0) 2026.08.26
[JavaScript][Vue.js] Composition API란? - Options API와의 차이부터 ref, computed, watch까지  (0) 2026.08.26
'개발/JavaScript' 카테고리의 다른 글
  • [JavaScript][Vue.js] Slot 정리 - 부모가 자식 컴포넌트에 UI를 주입하는 방법
  • [JavaScript][Vue.js] provide / inject 정리 - 깊은 컴포넌트 간 데이터 전달
  • [JavaScript][Vue.js] Props와 Emits 정리 - 부모·자식 컴포넌트 통신
  • [JavaScript][Vue.js] Component Lifecycle과 Lifecycle Hook 정리
danieLee
danieLee
개발일지
  • danieLee
    Code log
    danieLee
  • 전체
    오늘
    어제
    • 분류 전체보기 (77)
      • 개발 (76)
        • C++ (3)
        • java (6)
        • JavaScript (9)
        • python (0)
        • AWS (2)
        • Docker (6)
        • git (0)
        • 백엔드 (4)
        • Spring (7)
        • Django (2)
        • AI (3)
        • 코테 준비 (13)
        • 알고리즘 (6)
        • SKALA 4기 (13)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    API
    파이썬
    java
    4기
    코테
    js
    Ai
    대학생
    프로그래머스
    알고리즘
    백엔드
    개념
    개발
    서버
    skala
    개발자
    vue.js
    프론트
    spring
    JavaScript
  • 최근 댓글

  • 최근 글

  • 반응형
  • hELLO· Designed By정상우.v4.10.1
danieLee
[JavaScript][Vue.js] Pinia와 Store 정리 - 전역 상태 관리 이해하기
상단으로

티스토리툴바