feat: 둘러보기(데모)를 실제 화면 그대로 — 데모 모드 + API 더미 응답

- src/demo: demo.on 플래그 + DTO 형태 더미데이터 + mock API(읽기 더미/쓰기 차단)
- accountApi: Proxy 로 데모 시 읽기→더미, 쓰기→안내. 실제 뷰 그대로 렌더
- App: authed=인증||데모 → 실제 사이드바/셸 표시 + 데모 배너(로그인/나가기)
- 라우터: 데모 허용(가계부/고정/계좌/분류) 외 보호화면은 DemoLockedView(로그인 유도)
- 사이드바: 데모 시 잠금(🔒) 배지, 메뉴는 showMenu(인증||데모)
- 로그인/로그아웃 시 데모 자동 해제, 랜딩 '둘러보기'→enterDemo
- 기존 독립 DemoView 제거

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-28 07:49:37 +09:00
parent 80e1faec09
commit 4ffc01f484
10 changed files with 331 additions and 483 deletions
+56 -5
View File
@@ -1,6 +1,6 @@
<script setup>
import { onMounted, onUnmounted, watch } from 'vue'
import { RouterView, useRoute } from 'vue-router'
import { computed, onMounted, onUnmounted, watch } from 'vue'
import { RouterView, useRoute, useRouter } from 'vue-router'
import AppHeader from '@/components/layout/AppHeader.vue'
import AppSidebar from '@/components/layout/AppSidebar.vue'
import AppBottomNav from '@/components/layout/AppBottomNav.vue'
@@ -11,10 +11,20 @@ import AppDialog from '@/components/ui/AppDialog.vue'
import { Capacitor } from '@capacitor/core'
import { useAuthStore } from '@/stores/auth'
import { useUiStore } from '@/stores/ui'
import { demo, exitDemo } from '@/demo'
const auth = useAuthStore()
const ui = useUiStore()
const route = useRoute()
const router = useRouter()
// 로그인 또는 둘러보기(데모) 상태면 사이드바/전체 셸 표시
const authed = computed(() => auth.isAuthenticated || demo.on)
function leaveDemo() {
exitDemo()
router.push('/')
}
// 앱(Capacitor 네이티브) 또는 데스크톱(Electron) 클라이언트에서 전체 이용.
// 웹(브라우저)은 안내 페이지만 노출. 개발 모드(npm run dev)는 예외로 전체 앱 표시.
@@ -43,14 +53,21 @@ watch(() => route.fullPath, () => ui.closeSidebar())
<!-- (Capacitor): 전체 기능 -->
<template v-else>
<div class="layout" :class="{ 'sidebar-open': ui.sidebarOpen, 'no-sidebar': !auth.isAuthenticated }">
<div class="layout" :class="{ 'sidebar-open': ui.sidebarOpen, 'no-sidebar': !authed }">
<AppHeader class="layout-top" />
<!-- 로그인 전에는 메뉴가 없어 사이드바를 숨김( 제거·중앙 정렬) -->
<template v-if="auth.isAuthenticated">
<!-- 로그인/둘러보기 전에는 메뉴가 없어 사이드바를 숨김( 제거·중앙 정렬) -->
<template v-if="authed">
<AppSidebar class="layout-left" />
<div class="sidebar-backdrop" @click="ui.closeSidebar()"></div>
</template>
<main class="layout-body">
<div v-if="demo.on" class="demo-bar">
<span>👀 <b>둘러보기 모드</b> · 샘플 데이터입니다.</span>
<span class="demo-actions">
<button type="button" class="db-login" @click="ui.openLogin('/account')">로그인</button>
<button type="button" class="db-exit" @click="leaveDemo">나가기</button>
</span>
</div>
<RouterView />
</main>
<AppBottomNav class="layout-bottom" />
@@ -84,6 +101,40 @@ watch(() => route.fullPath, () => ui.closeSidebar())
grid-area: left;
}
.demo-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
flex-wrap: wrap;
margin-bottom: 1rem;
padding: 0.55rem 0.9rem;
border: 1px solid hsla(160, 100%, 37%, 0.4);
border-radius: 8px;
background: hsla(160, 100%, 37%, 0.08);
font-size: 0.88rem;
}
.demo-actions {
display: flex;
gap: 0.4rem;
}
.demo-bar button {
padding: 0.35rem 0.8rem;
border-radius: 6px;
font-weight: 600;
font-size: 0.85rem;
cursor: pointer;
}
.db-login {
border: 1px solid hsla(160, 100%, 37%, 1);
background: hsla(160, 100%, 37%, 1);
color: #fff;
}
.db-exit {
border: 1px solid var(--color-border);
background: var(--color-background);
color: var(--color-text);
}
.layout-body {
grid-area: body;
padding: 1.5rem 2rem;