Vue 상태관리 전략 가이드
Vue 프로젝트에서 상태관리는 종종 Pinia를 도입하는 순간 해결된 것처럼 보인다. 하지만 실제 문제는 라이브러리 선택이 아니라, 어떤 정보를 어떤 수명주기와 책임으로 관리할지를 구분하는 데 있다. 상태관리 전략이 없는 팀은 스토어가 커질수록 더 느려지고, 디버깅이 어려워지며, SSR 환경에서는 hydration 불일치까지 겪게 된다.
좋은 Vue 상태관리 전략은 “무엇을 전역에 둘까”보다 먼저 “무엇을 전역에 두지 말아야 할까”를 정하는 데서 시작한다.
아키텍처 그림 설명
[URL / Router State]
|
v
[Server State Layer]
|
v
[Pinia Domain State]
cart / auth / editor session
|
v
[Local Component State]
input / toggle / transient UI
Vue에서 상태가 무너지는 패턴은 대개 모든 값을 Pinia로 올리는 데서 시작합니다. 실제로는 URL, 서버 데이터, 도메인 상태, 로컬 UI 상태가 서로 다른 규칙을 가져야 합니다. 이 그림은 Pinia가 중심 저장소라기보다, 여러 층 사이에서 정말 공유가 필요한 도메인 상태만 맡아야 한다는 점을 보여줍니다.
상태는 도구보다 성격으로 분류해야 한다
Vue 앱에서 상태를 잘못 다루는 가장 흔한 이유는 서로 다른 성격의 데이터를 하나의 스토어 패턴으로 처리하기 때문이다. 실무에서는 최소한 다음 네 가지로 구분하는 편이 좋다.
- 로컬 UI 상태: 드롭다운 열림 여부, 현재 탭, 폼 입력 중간값
- 전역 UI 상태: 토스트, 전역 모달, 사용자 선호 테마
- 서버 상태: API 응답, 목록, 상세 데이터, 재검증 대상
- 도메인 상태: 장바구니, 편집 세션, 다단계 비즈니스 흐름
이 구분이 없으면 Pinia 스토어가 모든 값을 흡수하는 저장 창고가 된다. 결국 어떤 상태가 언제 초기화돼야 하는지, 어떤 상태가 URL과 동기화돼야 하는지, 어떤 상태가 새로고침 후 복구돼야 하는지가 모호해진다.
Pinia는 전역 저장소이지 데이터베이스가 아니다
Pinia는 Vue 생태계에서 매우 자연스러운 선택이지만, 이것이 모든 데이터의 집합소가 돼서는 안 된다. 특히 서버에서 가져온 목록과 상세 데이터를 모두 Pinia에 장기 저장하면 다음 문제가 생긴다.
- 최신성 기준이 불명확해진다.
- 중복 요청 제어가 어렵다.
- 화면 진입과 이탈 시 정리 정책이 모호해진다.
- SSR 환경에서는 요청 간 상태 오염 위험이 커진다.
따라서 Pinia에는 진짜로 여러 화면에서 공유돼야 하고, 도메인적으로 의미가 있는 상태만 남기는 것이 좋다.
서버 상태와 클라이언트 상태를 섞지 마라
많은 Vue 프로젝트에서 API 응답을 받아 Pinia에 넣고, 컴포넌트는 그 값을 바라보게 만드는 구조를 택한다. 초반에는 단순해 보이지만, 곧 캐싱, 재요청, 로딩 상태, 에러 상태, 낙관적 업데이트가 복잡하게 얽힌다.
서버 상태는 원격 데이터이므로 “소유”보다 “동기화”의 관점에서 다뤄야 한다. 즉 전역 스토어에 영구 저장하기보다, 재검증 기준과 로딩 정책을 명확히 하는 패턴이 필요하다. 규모가 크다면 서버 상태 전용 도구를 검토하는 것도 합리적이다.
스토어는 기능별로 쪼개되, 책임이 중복되지 않게 하라
Pinia 스토어를 나눌 때도 주의가 필요하다. 인증, 사용자 설정, 장바구니, 에디터, 검색 필터처럼 도메인 기준으로 분리하는 것은 좋지만, 서로가 같은 데이터를 중복 소유하면 상태 일관성이 무너진다.
예를 들어 사용자 정보를 authStore, profileStore, headerStore가 동시에 들고 있다면 어느 값이 기준인지 알기 어렵다. 실무에서는 “누가 이 데이터를 소유하는가”를 명확히 두고, 다른 스토어는 참조나 파생 값만 사용하도록 구조를 잡는 편이 낫다.
URL과 상태의 관계를 명확히 하라
검색 조건, 정렬 기준, 페이지 번호, 선택된 탭처럼 사용자가 링크로 공유하거나 새로고침 후 유지되길 기대하는 상태는 스토어보다 URL에 두는 것이 맞는 경우가 많다. 반대로 단순 모달 열림 여부나 드래그 중 상태는 URL로 내보낼 필요가 없다.
이 구분이 불명확하면 Vue Router와 Pinia가 같은 상태를 서로 다르게 표현하게 되고, 복원 가능한 화면과 복원 불가능한 화면이 섞인다. 결과적으로 deep link 품질이 떨어진다.
SSR에서는 상태 초기화 규칙이 더 중요하다
Nuxt나 SSR 환경에서는 상태 전략이 더 엄격해야 한다. 요청마다 독립된 상태를 만들어야 하고, 서버에서 생성된 값이 클라이언트에서 동일하게 복원되어야 한다. 전역 싱글턴처럼 상태를 다루면 요청 간 데이터가 섞이거나 hydration mismatch가 발생할 수 있다.
따라서 SSR에서는 다음을 특히 조심해야 한다.
- 요청 단위로 초기화되지 않는 전역 객체
- 브라우저 전용 API에 의존하는 초기 상태 계산
- 서버와 클라이언트에서 다른 기본값을 갖는 플래그
- 세션/권한 데이터를 늦게 주입해 UI가 깜빡이는 문제
좋은 상태관리 전략의 기준
Vue 프로젝트에서 상태관리 전략이 잘 잡혀 있는지는 다음으로 판단할 수 있다.
- 상태의 소유자가 명확한가
- 새로고침 후 복구돼야 할 상태와 그렇지 않은 상태가 구분되는가
- 서버 상태의 최신성 규칙이 설명 가능한가
- 전역 스토어를 거치지 않고도 기능 단위 테스트가 가능한가
- 상태 초기화와 정리 시점이 명확한가
자주 생기는 안티패턴
- 모든 API 응답을 Pinia에 넣고 재사용하려고 함
- 단순 로컬 상태까지 전역 스토어로 올림
- 여러 스토어가 같은 엔티티를 중복 소유함
- URL이 표현해야 할 상태를 스토어에만 저장함
- SSR 환경에서 상태 초기화 규칙 없이 클라이언트 중심 구조를 유지함
마무리
Vue 상태관리의 핵심은 Pinia를 잘 쓰는 것이 아니라, 상태의 성격과 소유권, 복원 방식, 최신성 규칙을 분명히 하는 것이다. 상태를 전역에 모으는 행위는 편리해 보이지만, 규모가 커질수록 시스템의 결합도를 높인다.
결국 좋은 상태관리 전략은 스토어를 많이 만드는 전략이 아니라, 전역 상태를 최소화하면서도 도메인 흐름은 명확히 표현하는 전략이다.
운영 환경에서 어려워지는 지점
- Vue에서 로컬 UI 상태, 서버 캐시, 폼 상태, 워크플로우 상태가 섞이기 시작하면 상태 전략이 급격히 어려워진다.
- 팀이 실패하는 이유는 대개 “잘못된 라이브러리”를 골라서가 아니라 상태 소유권 규칙을 정하지 않았기 때문이다.
- 초기에 편해 보이는 전역 상태는 가장 큰 숨은 결합 지점이 되기 쉽다.
중요한 아키텍처 결정
- 일시적인 뷰 상태는 정말 여러 떨어진 기능이 함께 써야 할 때가 아니면 로컬에 둔다.
- 공유 클라이언트 상태는 Pinia 또는 범위를 제한한 composable가 명확한 소유권과 재사용 가치를 줄 때만 둔다.
- 서버에서 온 데이터는 자체 캐시와 invalidation 정책을 가진 서버 상태로 다룬다.
실무 예시
안정적인 전략은 보통 상태를 출처와 수명 기준으로 구분한다.
local state -> 모달 열림, 입력 draft, 탭 선택
server state -> 가져온 목록, 프로필 요약, 권한
workflow state -> 체크아웃 진행, 위저드 draft
shared client state -> 테마, 인증 세션, feature flag
피해야 할 안티패턴
- 반복된다는 이유만으로 모든 값을 전역 상태로 올리는 것.
- 접근이 편하다는 이유로 서버 캐시를 클라이언트 store에 복제하는 것.
- 상태 계층이 해결해야 할 문제를 정의하기 전에 도구부터 고르는 것.
운영 체크리스트
- 로컬, 공유, 서버 상태의 소유권 규칙을 문서화한다.
- 기능이 늘 때 전역 store가 줄어드는지 커지는지 검토한다.
- 재렌더링 비용과 stale state 사고를 측정한다.
- 화면 간 workflow의 optimistic update와 rollback 동작을 테스트한다.
최종 판단
Vue 상태 관리는 상태를 서로 다른 아키텍처 클래스들로 다룰 때 잘 작동한다. 모든 것을 하나의 store에 넣는 전략은 결국 모든 복잡성을 한곳에 모은다.
Continue Reading
다음으로 읽기 좋은 글
Nuxt 아키텍처 설계 가이드
Nuxt 기반 프론트엔드 아키텍처를 설계할 때 고려해야 할 렌더링 전략, 데이터 계층, 라우팅 경계, 캐싱, 운영 모델을 실무 관점에서 정리합니다.
🖥️ FrontendReact 상태관리 전략 가이드
React 애플리케이션에서 로컬 상태, 서버 상태, 전역 상태를 어떻게 분리하고 설계해야 하는지 실무 관점에서 정리합니다.
⚙️ BackendCQRS + Event Sourcing 실전 구현 가이드
CQRS와 Event Sourcing을 도메인 경계와 운영 복잡성의 관점에서 설명하고, Aggregate, Event Store, Projection, Snapshot, 정합성 모델의 선택 기준을 정리합니다.
💬 LanguageI/O 경계에서의 타입 좁히기 전략
언어의 타입 시스템은 경계 안에서 강하지만, 외부 입력 앞에서는 다시 확인이 필요합니다. I/O 경계에서 타입을 좁히는 전략을 정리합니다.
다음 탐색