[Vue] pinia 를 이용한 상태관리 (+compositionApi, optionsApi 차이)

2025. 3. 12. 17:08Vue

 

개요

- vue3 프로젝트를 하면서 vuex를 통해 관리하던 상태변경을 pinia를 이용해야했다.

- pinia 와 vuex의 차이점을 알고 사용해보기로 함

- 이에 앞서 이전 버전과 다른 vue3의 설계구조를 알아야 함

 

-  Composition API vs Options API 의 차이 

 composition API란 무엇일까? 

-> vue3에서 도입된 새로운 방식의 컴포넌트 설계 방법.

이전에는 options API(옵션 객체 data, methods, computed, etc.를 이용하여 구성) 를 사용해 컴포넌트를 작성.

composition API는 좀 더 유연하고 효율적인 코드를 작성할 수 있도록 해준다. 주요 개념은 **반응성(reactivity)**과 컴포넌트의 상태와 기능을 함수 기반으로 구성하는 것!

 

ex. options API를 이용한 컴포넌트 작성

 

 

 

** Composition API vs Options API 의 특징 정리

특징 Composition API Options API
구성방식 함수 기반으로 상태와 로직을 분리하여 구성 옵션 객체 (data, methods, computed, etc.)를 이용하여 구성
재사용성 로직을 기능별로 분리해 재사용하기 용이 여러 기능을 한 컴포넌트에 넣기 때문에 재사용성이 떨어질 수 있음
유지보수성 코드의 의도를 명확히 분리하여 유지보수성이 높음 하나의 객체에 모든 것을 정의하므로 복잡한 컴포넌트는 관리하기 어려움
타입스크립트 지원 타입스크립트와 잘 통합되며, 명시적 타입 정의가 가능 타입스크립트 사용 시 옵션마다 타입을 명시해야 해서 다소 복잡
학습 곡선 처음에는 다소 복잡할 수 있지만, 익숙해지면 유연하고 강력함 Vue에 익숙한 사람이라면 배우기 쉽고 직관적

 

 

왜 Composition API를 사용할까?

  • 코드 재사용: Composition API는 기능별로 코드를 나누어 더 쉽게 재사용할 수 있게 만든다.
  • 유지보수성: 비슷한 기능을 가진 코드들을 한 곳에 모아서 관리할 수 있어 유지보수가 용이하다.
  • 타입스크립트 친화적: Composition API는 타입스크립트와 함께 사용하기 더 자연스럽고, 코드에 대한 타입 추론을 잘 지원한다.
  • 클린 코드: 비슷한 기능들을 한 파일에 묶어서 컴포넌트가 커지는 문제를 해결할 수 있다.

 

->  유연한 방식의 컴포넌트 작성으로 특히 대규모 애플리케이션에서 코드의 재사용성과 유지보수성을 크게 향상시킬 수 있다.

 

 

Composition API의 주요 기능과 함수들

1. setup(): Composition API에서 가장 중요한 함수. setup()은 컴포넌트가 생성될 때 호출되며, 데이터, 메소드, 계산된 속성, 라이프사이클 훅 등을 선언. 이곳에서 선언된 값들은 컴포넌트 내에서 사용할 수 있다.

 

2. ref(): ref()는 반응형 데이터를 만들 때 사용. 이 함수로 만든 데이터는 vue의 반응성 시스템에 의해 자동으로 추적되고 업데이트 된다.

 

* setup()과 ref()를 사용한 코드예시

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

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0); // 반응형 상태

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

    return { count, increment }; // 템플릿에서 사용할 값 반환
  }
}
</script>

 

 

3. reactive(): reactive()는 객체를 반응형으로 만들어주는 함수로 객체나 배열을 반응형 상태로 만들 때 사용.

const state = reactive({
  count: 0,
  name: 'Vue'
});

 

 

4. computed(): computed()는 계산된 속성을 만들 때 사용하는데 특정 값이 변경될 때만 계산되므로 성능상 유리하다.

const doubledCount = computed(() => count.value * 2);

 

5. watch(): watch()는 특정 반응형 데이터의 변경을 감지하고 그에 따른 작업을 실행할 수 있게 해준다. 주로 상태 변화에 따라 비동기 작업을 처리할 때 유용하다. (폼요소)

watch(count, (newCount) => {
  console.log('count가 변경됨:', newCount);
});

 

 

** watch 함수의 인자

  1. 첫 번째 인자: 감시할 반응형 데이터 또는 함수
  2. 두 번째 인자: 반응형 데이터가 변경될 때 실행될 콜백 함수
  3. (선택적) 세 번째 인자: 옵션 객체 (옵션으로 사용할 수 있는 추가 설정)

-  첫 번째 인자 : 감시할 데이터 = 반응형 데이터 또는 함수

  • 반응형 데이터: ref나 reactive로 만든 데이터를 감시
  • 함수: 상태나 계산된 속성(computed)을 감시하려면 그 값을 반환하는 함수를 사용할 수 있다. 이때 함수는 데이터의 변화를 감지할 수 있는 기준이 된다.

-  두 번째 인자: 콜백 함수

=>  두 번째 인자는 감시 대상이 변경될 때 호출될 콜백 함수. 이 함수는 다음 두 가지 인자를 받는다.

  • newValue: 감시하는 데이터가 변경된 후의 새로운 값
  • oldValue (선택적): 감시하는 데이터가 변경되기 전의 이전 값. oldValue는 두 번째 인자에서 선택적으로 사용할 수 있다.
const count = ref(0);
watch(count, (newCount, oldCount) => {
  console.log('count가 변경됨. 이전 값:', oldCount, '새로운 값:', newCount);
});

 

위 예시에서 watch는 count라는 반응형 데이터를 감시하고, count의 값이 변경되면 두 번째 인자로 전달된 콜백 함수가 호출된다.

newCount는 count가 변경된 후의 값, oldCount는 변경되기 전의 값을 나타낸다.

 

 

-  세 번째 인자:  옵션 객체

=> 세 번째 인자는 옵션 객체로, watch의 동작 방식을 제어할 수 있는 추가 설정을 제공한다. 주요 옵션은 다음과 같다.

  • immediate: true로 설정하면, 감시 시작 시점에서 즉시 콜백을 실행. 기본값은 false로, 데이터가 처음 변할 때만 콜백이 실행.
  • deep: true로 설정하면, 객체나 배열의 내부 속성까지 깊이 추적하여 변경 사항을 감지. 기본값은 false.
const state = reactive({ count: 0, name: 'Vue' });
watch(
  () => state,  // 감시할 반응형 데이터
  (newState, oldState) => {
    console.log('상태가 변경되었습니다:', newState);
  },
  { deep: true }  // 객체 내부의 변화까지 추적
);

이 예시에서 deep: true를 설정하면, state 객체의 내부 속성인 count나 name이 변경될 때마다 watch 콜백이 실행.

 

 

 

 

6. onMounted(), onUpdated(), onUnmounted(): Composition API에서는 라이프사이클 훅을 함수 형태로 사용할 수 있다. onMounted()는 컴포넌트가 마운트된 후 실행되며, onUpdated()는 상태가 업데이트될 때 호출된다. onUnmounted()는 컴포넌트가 파괴될 때 실행된다.

import { onMounted } from 'vue';

onMounted(() => {
  console.log('컴포넌트가 마운트되었습니다!');
});

 

** Pinia의 주요 특징

  1. composition API와 통합: pinia는 vue 3의 composition API와 매우 잘 통합된다. ref, reactive, computed 등의 vue 3 API를 사용할 수 있다.
  2. 모듈화: pinia는 스토어를 모듈화하여 여러 개의 스토어를 간편하게 관리할 수 있다. 각 스토어는 독립적으로 설정되고 사용가능하다.
  3. typescript 지원: typeScript를 기본적으로 지원하여, 타입 안전성을 제공한다.
  4. 단순한 API: pinia의 api는 vuex보다 간결하고 직관적이다. 설정이 적고, store를 정의하는 방식이 간단하다.
  5. devtools 통합: vue devtools와의 통합이 잘 되어 있어, 상태를 쉽게 추적하고 디버깅할 수 있다.

=>  pinia는 vue 3에 최적화된 라이브러리로, vuex에 비해 더 직관적이고 간단한 설정을 제공한다.

        vuex는 여전히 vue 2와의 호환성을 고려해 설계된 부분이 있기 때문에, vue 3에서는 pinia가 더 나은 선택이 될 수 있다.

 

 

 

** Pinia와 Vuex의 차이점

특징 Pinia Vuex
설정 및 사용 훨씬 간단하고 직관적이며 설정이 적음 설정이 많고, 복잡한 구성 요소가 많음
상태 관리 defineStore를 사용하여 상태를 정의, Composition API와 잘 통합됨 state, getters, mutations, actions로 상태 관리
모듈화 각 스토어는 독립적이고 모듈화된 방식으로 관리  모듈 시스템이 있으나 상대적으로 복잡함
typescript 지원 기본적으로 typescript를 잘 지원하며 타입 추론이 매우 좋음 typescript를 지원하지만 설정이 더 번거로움 
vue devtools pinia 전용 devtools 지원, 상태 추적이 직관적임 vuex devtools 지원, 상태 추적이 가능하지만 다소 복잡함
반응성 ref, reactive등 vue3의 composition API와 잘통함 vuex의 상태는 반응성 구현이 상대적으로 복잡함
플러그인 시스템 pinia자체가 가벼워서 플러그인 시스템을 쉽게 사용할 수 있음 vuex 플러그인 시스템은 약간 복잡할 수 있음

 

 

 

 

 

** vuex 활용버전

vuex 설치 후, Store() 설정하기

vuex.store()을 통해 state, action, mutaions 파일을 따로 만들고 store에 설정하기

 

 

store를 가져와 app.use()에 적용

 

 

 

vuex 에서 mapState, mapActions, mapMutations 등을 가져와 함수형식으로 사용하기

vuex에서 mapState, mapMutaions를 이용

 

 

 

- vuex와 pinia의 차이로 주목해야할 점

** vuex에서의 mutations

vuex는 상태 관리 라이브러리로, 상태를 변경하는 방법에 대한 규칙을 엄격하게 제어.

**mutations**는 상태를 변경하는 유일한 방법으로 state를 수정하는 함수가 mutations 안에 정의된다. 이 방법은 동기적으로만 상태를 변경할 수 있게 하여 예측 가능한 방식으로 상태를 관리할 수 있도록 했다.

 

 

mutaions내 상태변경을 위한 함수정의
 
 
 
commit을 통해 mutation을 호출하고, 그 안에서 state를 수정하는 방식으로 전개.
이 방식은 명확하고 예측 가능한 상태 변경을 보장하지만, 코드가 상대적으로 길고 복잡하다.

 

commit을 활용하여 mutaions에 등록된 함수를 호출

 

 

 

 

** pinia 활용버전 -> npm i pinia 설치 후 활용 

 

 

defineStore()를 활용해 store를 간편하게 적용

 

main.js에서 pinia를 활용해 app.use(pinia) 적용

 

 

 

useCounterStor()을 호출하여 등록된 함수를 적용

 

 

 

** Pinia에서의 상태 변경

pinia에서는 mutations 개념이 없다. 대신 상태(state)를 변경하는 로직을 바로 actions 안에서 처리.

actions는 상태를 변경하거나 비동기 작업을 처리할 수 있는 함수로 사용. 상태를 변경하는 데 특별한 제약이 없으며, actions 안에서 바로 state를 수정할 수 있다.

 

pinia에서 mutations를 없앤 이유

  1. 간소화된 코드: pinia에서는 상태 변경이 직접적이므로 별도로 mutation을 정의할 필요가 없어 코드가 더 간단하고 이해하기 쉬워짐
  2. vuex보다 더 유연한 상태 변경: mutation을 통해서만 상태를 변경해야 한다는 규칙이 없음. 코드가 더 유연해짐.
  3. Composition API와의 통합: pinia는 vue 3의 Composition API와 잘 통합되어, 상태를 쉽게 선언하고 조작할 수 있음. this를 통해 직접 상태를 수정하는 것이 Composition API의 방식과 더 잘 맞음

**mutations**가 아니라, **actions**에서 상태를 직접 수정하고 있다는 점. 이로 인해 상태 변경이 더욱 직관적이고 간편해진다.

 

 

*상태변경을 직접하는 pinia에서 actions의 예시

this.cards 를 이용하여 action에서 직접적으로 상태변경을 함

 

 

 

vuex에서는 상태를 변경할 때 mutations를 사용하여 상태 변경을 강제하고 그 변경은 반드시 mutations 내에서만 이루어진다.

그러나 Pinia에서는 mutations가 없고, 대신 **actions**에서 상태를 직접적으로 변경. actions는 동기적, 비동기적 상태 변경 모두를 처리할 수 있다. Pinia는 간단하고 직관적인 상태 관리를 제공하기 위해 mutations의 개념을 제거하고, 상태를 바로 수정할 수 있는 방법을 제공한다는 점. 이렇게 pinia는 vuex보다 더 직관적이고 가벼운 상태 관리 방식을 제공하며, Composition API와 잘 맞아 떨어진다.