props,emits을 사용하는 경우도 있지만 vue3에는 pinia라는 상태관리 라이브러리가 존재한다. 이번 글에서는 pinia를 통해 전에 블로그에서 작성했던 코드를 변경해 보겠다.
📌 pinia는 다음과 같은 상황에서 주로 사용된다.
📌 Pinia의 주요 특징
먼저 postsStore.ts를 생성하여 Pinia store를 만든다.
import { defineStore } from 'pinia';
import { getPosts } from '../apicontroller/posts';
import { PostData, PostDto } from '../assets/interfaces/index';
import { menuList } from '../assets/column/index';
export const usePostsStore = defineStore('posts', {
state: () => ({
currentPage: 1,
rowsPerPage: 20,
totalPages: 0,
posts: [] as PostData[],
currentRoute: '',
}),
getters: {
tableTitle: (state) => {
const menuItem = menuList.find((item) => item.route === state.currentRoute);
return menuItem ? menuItem.label : '';
},
},
actions: {
async fetchPosts() {
try {
const response = await getPosts(
this.currentPage,
this.rowsPerPage,
this.currentRoute || '/posts'
);
this.posts = response.items.map((item: PostDto) => ({
postId: item.post_id,
postTitle: item.post_title,
writer: item.post_username,
department: item.mem_address1,
postDatetime: item.post_datetime,
postHit: item.post_hit,
}));
this.totalPages = response.totalPages;
} catch (error) {
console.error('게시물 가져오기 실패: ', error);
}
},
setCurrentPage(page: number) {
this.currentPage = page;
this.fetchPosts();
},
setCurrentRoute(route: string) {
this.currentRoute = route;
this.fetchPosts();
},
},
});
다음 PostsComponent.vue를 고쳤다.
<template>
<div class="q-pa-md">
<q-table
flat
bordered
:title="store.tableTitle"
:rows="store.posts"
:columns="postColumn"
:rows-per-page-options="[store.rowsPerPage]"
row-key="postId"
hide-bottom
/>
</div>
</template>
<script setup lang="ts">
import { onMounted, watch } from 'vue';
import { usePostsStore } from '../stores/postsStore';
import { postColumn } from '../assets/column/index';
const props = defineProps<{
routeInfo: string;
}>();
const store = usePostsStore();
watch(() => props.routeInfo, (newRoute) => {
store.setCurrentRoute(newRoute);
});
onMounted(() => {
store.setCurrentRoute(props.routeInfo);
});
</script>
PageComponent.vue
<template>
<div class="q-pa-lg flex flex-center">
<q-pagination
v-model="currentPage"
:max="store.totalPages"
:max-pages="9"
:ellipses="false"
:boundary-numbers="false"
direction-links
boundary-links
icon-first="skip_previous"
icon-last="skip_next"
icon-prev="fast_rewind"
icon-next="fast_forward"
/>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { usePostsStore } from '../stores/postsStore';
const store = usePostsStore();
const currentPage = computed({
get: () => store.currentPage,
set: (value) => store.setCurrentPage(value),
});
</script>
PostsPage.vue
<template>
<q-page class="column items-center justify-start">
<div class="col-12 q-pa-md">
<posts-component
:route-info="currentRoute"
></posts-component>
</div>
<div class="col-12 q-pa-md">
<page-component></page-component>
</div>
</q-page>
</template>
<script setup lang="ts">
import { computed, watch } from 'vue';
import { useRoute } from 'vue-router';
import { usePostsStore } from '../stores/postsStore';
import PostsComponent from 'components/PostsComponent.vue';
import PageComponent from 'components/PageComponent.vue';
const route = useRoute();
const store = usePostsStore();
const currentRoute = computed(() => route.fullPath);
watch(currentRoute, (newRoute) => {
store.setCurrentRoute(newRoute);
});
</script>
코드를 보면 props, emits을 사용했을 때보다 코다 훨씬 간결해 진걸 확인할 수 있다.
📌 defineProps와 defineEmits 사용할 떄 장단점
📌 Pinia 사용할 때 장단점
📖 결론
- 작은 규모라면 props/emits, 큰 규모라면 Pinia가 유리할 수 있다.
- 간단한 상태라면 props/emits, 복잡한 상태 관리가 필요하다면 Pinia가 좋다.
- 앱이 커질 가능성이 있다면 Pinia를 고려해볼 만하다.
- 대규모 상태 관리가 필요한 경우 Pinia가 성능상 이점을 줄 수 있다.