10일차 Vue 3 Pinia 적용, 로그인 상태 관리
10일차 Vue 3 Pinia 적용, 로그인 상태 관리
🎯 오늘의 목표
마지막 문제인 Vue로 로그인 기능을 구현하면서 발생했던 상태 공유 문제를 해결하기 위해서 Vue 3 상태관리 라이브러리인 Pinia를 적용해보자.
이 트러블슈팅을 마지막으로 토이 프로젝트를 정리하려고 한다.
✅ 문제 상황
처음에는 로그인 후 localStorage.setItem('token', token)으로 토큰을 저장하고,
Header.vue에서는 localStorage.getItem('token')을 사용해서 조건부 렌더링을 했다.
하지만 이 방식으로 구현하니까 동작을 제대로 하지 않았다.
- 컴포넌트 간 상태가 실시간으로 반영되지 않음 (새로고침해야 반영)
- Header나 App.vue에서 token 변경 감지가 안 됨
localStorage는 Vue의 반응형 시스템 밖에 있음localStorage에 값을 넣거나 바꿔도 Vue 컴포넌트는 그 변화를 실시간으로 감지하지 못함.
로그인 방식
- 로그인 성공 시
localStorage에 token 저장 - Header 컴포넌트에서는
localStorage.getItem('token')로 로그인 상태 판단
Login.vue
1
localStorage.setItem('token', token); // 로그인 시 token 저장
Header.vue
1
2
3
4
5
computed: {
isLoggedIn() {
return localStorage.getItem('token');
}
}
✅ Pinia란?
Pinia는 Vue 3에서 공식적으로 지원하는 상태 관리 라이브러리이다. Vue 팀이 직접 만들었다고 한다.
Vuex의 후속으로, 더 간결하고 직관적인 API를 통해 Composition API와의 궁합이 정말 좋다.
주요 특징
| 특징 | 설명 |
|---|---|
| Vue 공식 상태관리 | Vue 3에서 Vuex 대신 Pinia가 표준 |
| Composition API와 궁합 | setup() 함수 안에서 쉽게 store 사용 가능 |
| 타입스크립트 친화적 | 자동 타입 추론 및 타입 안전성 우수 |
| 반응형 기반 | ref나 reactive처럼 상태 변화에 반응 |
| DevTools 통합 | Vue DevTools에서 상태 추적 가능 |
Pinia로 해결한 구조
1. stores/user.js에서 전역 상태 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
token: localStorage.getItem('token') || null
}),
actions: {
setToken(token) {
this.token = token;
localStorage.setItem('token', token);
},
clearToken() {
this.token = null;
localStorage.removeItem('token');
}
}
});
2. main.js에서 Pinia 등록
1
2
3
4
import { createPinia } from 'pinia';
const app = createApp(App);
app.use(createPinia());
3. LoginForm.vue에서 token 저장
1
2
3
4
5
6
7
8
9
10
11
import { useUserStore } from '@/stores/user';
methods: {
async handleLogin() {
const res = await fetch('/login', { ... });
const token = await res.text();
const userStore = useUserStore();
userStore.setToken(token); // 저장
this.$router.push('/maindashboard');
}
}
4. Header.vue에서 상태 읽기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { useUserStore } from '@/stores/user';
import { computed } from 'vue';
setup() {
const userStore = useUserStore();
const isLoggedIn = computed(() => !!userStore.token);
const logout = () => {
userStore.clearToken();
router.push('/login');
};
return { isLoggedIn, logout };
}
→ isLoggedIn은 토큰이 바뀌면 자동으로 반응하고, Header도 실시간으로 바뀜!
🔚 마무리
Pinia를 도입하면서 컴포넌트 간의 로그인 상태 공유 문제를 깔끔하게 해결했고,
Header 메뉴 표시, 로그인/로그아웃 시 렌더링 문제 해결되었다.
처음 설계단계에서는 Vue 3와 JPA 구조 둘다 처음 해보는거라 막막했지만, 하나하나 구조를 만들고, 문제를 해결해나가면서 자신감이 쌓이기 시작하더니 개발속도도 정확도도 많이 향상되었다.
이번 토이 프로젝트를 통해서 실무에서 꼭 한번 다시 사용해보고 싶다는 욕심이 생겼다.
Frontend Repository : GitHub
This post is licensed under CC BY 4.0 by the author.


