


4
좋아. 그럼 이제 **이 App을 기준으로
“Zustand를 쓴다면 어디까지, 어떻게 쓰는 게 최소·정답인가”**를 보자.
목표는 명확하다.
❌ 구조를 갈아엎지 않는다
⭕ 전역으로 빼야 할 것만 딱 분리한다
1️⃣ Zustand로 빼는 “최소 단위”는 이것뿐이다
다시 한 번 못 박자.
이 App에서 Zustand로 빼는 게 의미 있는 건 딱 이것뿐이다.
✔ 인증 상태 (auth)
- userInfo
- nickname
- isAuthenticated (파생)
- login / logout 책임
그 외:
- openTabs ❌
- alert ❌
- tabletMenuOpen ❌
👉 절대 같이 넣지 않는다
2️⃣ auth store의 역할 정의 (아주 중요)
Zustand store를 만들기 전에
역할을 문장으로 고정한다.
Auth Store는
“사용자가 누구인지”와
“로그인/로그아웃의 결과”만 책임진다
- 메뉴 로딩 ❌
- 라우팅 ❌
- UI 초기화 ❌
이걸 지키면 store가 절대 비대해지지 않는다.
3️⃣ authStore 최소 구현 예시
stores/authStore.js
import { create } from 'zustand'
export const useAuthStore = create((set) => ({
userInfo: null,
nickname: '',
// 파생 상태는 state로 두지 않는다
get isAuthenticated() {
return !!this.nickname
},
initFromStorage: () => {
try {
const stored = localStorage.getItem('userInfo')
if (stored) {
const parsed = JSON.parse(stored)
set({
userInfo: parsed,
nickname: parsed.nickname || ''
})
}
} catch (e) {}
},
login: (info) => {
set({
userInfo: info,
nickname: info.nickname || ''
})
localStorage.setItem('userInfo', JSON.stringify(info))
},
logout: () => {
set({
userInfo: null,
nickname: ''
})
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
sessionStorage.clear()
}
}))
여기서 중요한 설계 포인트
- isAuthenticated를 state로 두지 않음
- 항상 nickname에서 계산
- store 안에서 localStorage 책임까지 끝냄
- UI 상태는 단 1도 없음
4️⃣ App 컴포넌트는 이렇게 “얇아진다”
기존 코드에서 바뀌는 부분만 보자
import { useAuthStore } from './stores/authStore'
export default function App({ isMobile }) {
const {
userInfo,
nickname,
isAuthenticated,
initFromStorage,
login,
logout
} = useAuthStore()
🔹 마운트 시 인증 복구
useEffect(() => {
initFromStorage()
const token = localStorage.getItem('token')
if (!token) {
logout()
return
}
api.get('/auth/me')
.then(r => {
login(r.data)
})
.catch(() => {
logout()
})
}, [])
👉 App은 더 이상
setUserInfo, setNickname을 직접 만지지 않는다.
🔹 로그아웃 처리도 단순해진다
function confirmLogout() {
setOpenTabs([])
setActiveId(null)
logout()
navigate('/login')
}
Auth 관련 책임이 완전히 분리됐다.
5️⃣ 렌더 분기 로직은 더 명확해진다
if (!isAuthenticated && !isAuthPath) {
return <Navigate to="/login" replace />
}
이제 이 조건은:
- local state ❌
- effect ❌
- storage ❌
👉 순수 상태 판단
6️⃣ 왜 이게 “딱 적당한 Zustand 사용”인가
이 구조의 장점은 분명하다.
✔ 얻는 것
- 인증 로직 한 곳에 모임
- App 컴포넌트 단순화
- Mobile / PC 공통 auth 사용
- Context 없이 전역 상태 확보
❌ 일부러 안 한 것
- 메뉴 store ❌
- 탭 store ❌
- 팝업 store ❌
👉 Zustand를 “편의”가 아니라
“경계 분리 도구”로만 사용
7️⃣ 이 지점을 넘어서면 언제 위험해지나
아래 중 하나가 보이면
store 확장을 멈춰야 한다.
- setSomething이 계속 늘어난다
- UI 토글 상태를 넣고 싶어진다
- “여기서도 필요하네?”가 반복된다
그 순간:
❗ “이건 전역 상태 문제가 아니라
설계 문제일 확률이 높다”
8️⃣ 최종 결론 (아주 중요)
이 App 기준으로 정리하면 이렇다.
Zustand는
App을 단순하게 만들기 위해 쓰는 도구이지
App을 대신하는 도구가 아니다
- Auth → Zustand ⭕
- App Shell UI → App 로컬 ⭕
- Page UI → Page 로컬 ⭕
이 선을 지키면
Zustand는 절대 무거워지지 않는다.
다음으로 이어가면 딱 맞는 주제는 이거다.
👉 “그럼 Redux가 필요한 순간은 이 App에서 언제일까?”
- 메뉴/권한/라우팅 규칙이 복잡해질 때
- 상태 변경 이력이 필요해질 때
- 운영·감사·롤백 요구가 생길 때
여기까지 오면
도구 선택은 이미 감각이 아니라 판단이 된다.
'framework_library > react' 카테고리의 다른 글
| 컴포넌트의 유형 (0) | 2026.01.23 |
|---|---|
| 코드디벨럽3 (0) | 2026.01.11 |
| 코드 디벨럽 (0) | 2026.01.10 |
| React 소스 적용 (0) | 2026.01.10 |
| Redux는 왜 아직도 쓰이는가 (0) | 2026.01.10 |
댓글