Post

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 사용 가능
타입스크립트 친화적자동 타입 추론 및 타입 안전성 우수
반응형 기반refreactive처럼 상태 변화에 반응
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.