로그인 상태에 따른 네비게이션 및 페이지 보호 로직 추가:
Some checks failed
Build Push and Restart Compose / deploy (push) Failing after 1m12s

- `+layout.svelte`에서 로그인 상태에 따라 공개/보호 네비게이션 항목 분리 및 표시.
- 로그인 여부 확인 로직 `+layout.ts`로 이전 (`fetch` 결과에 따라 `loggedIn` 값 반환).
- 보호 페이지(`autotrade`, `asset`)는 미로그인 시 `/login` 리다이렉트 (`next` 파라미터 포함).
- 인증 예외 경로 `/api/watchlist`에 추가.
This commit is contained in:
hayato5246
2026-04-06 20:11:24 +09:00
parent 47bb040eb8
commit 5a29d50752
5 changed files with 56 additions and 18 deletions

View File

@@ -2,16 +2,21 @@
import { page } from '$app/state'
import { goto } from '$app/navigation'
let { children } = $props()
let { children, data } = $props()
const navItems = [
const publicNav = [
{ href: '/', label: '시세' },
{ href: '/theme', label: '테마' },
{ href: '/kospi200', label: '코스피200' },
]
const protectedNav = [
{ href: '/asset', label: '자산' },
{ href: '/autotrade', label: '자동매매' },
]
let loggedIn = $derived(data.loggedIn)
async function logout() {
await fetch('/logout', { method: 'POST', credentials: 'include' })
goto('/login')
@@ -33,7 +38,7 @@
<!-- 네비 링크 -->
<nav class="flex items-center gap-1 flex-1">
{#each navItems as item}
{#each publicNav as item}
<a
href={item.href}
class="px-3 py-1.5 rounded-md text-sm font-medium transition-colors
@@ -44,15 +49,37 @@
{item.label}
</a>
{/each}
{#if loggedIn}
{#each protectedNav as item}
<a
href={item.href}
class="px-3 py-1.5 rounded-md text-sm font-medium transition-colors
{isActive(item.href)
? 'bg-blue-600 text-white'
: 'text-gray-300 hover:text-white hover:bg-gray-700'}"
>
{item.label}
</a>
{/each}
{/if}
</nav>
<!-- 로그아웃 -->
<button
onclick={logout}
class="text-xs text-gray-400 hover:text-white transition-colors shrink-0"
>
로그아웃
</button>
<!-- 로그인/로그아웃 -->
{#if loggedIn}
<button
onclick={logout}
class="text-xs text-gray-400 hover:text-white transition-colors shrink-0"
>
로그아웃
</button>
{:else}
<a
href="/login"
class="text-xs text-gray-400 hover:text-white transition-colors shrink-0"
>
로그인
</a>
{/if}
</div>
</div>
</header>

View File

@@ -1,13 +1,9 @@
import { redirect } from '@sveltejs/kit'
// 인증 가드: 세션 유효하지 않으면 /login으로 리다이렉트
// 로그인 상태 확인 (리다이렉트하지 않음 — 공개 페이지도 접근 가능)
export async function load({ fetch }) {
try {
const res = await fetch('/api/auth/check', { credentials: 'include' })
if (!res.ok) throw redirect(302, '/login')
} catch (e) {
// redirect 재throw
if (e && typeof e === 'object' && 'status' in e) throw e
throw redirect(302, '/login')
return { loggedIn: res.ok }
} catch {
return { loggedIn: false }
}
}

View File

@@ -0,0 +1,7 @@
import { redirect } from '@sveltejs/kit'
// 자산 페이지: 로그인 필요
export async function load({ parent }) {
const { loggedIn } = await parent()
if (!loggedIn) throw redirect(302, '/login?next=/asset')
}

View File

@@ -0,0 +1,7 @@
import { redirect } from '@sveltejs/kit'
// 자동매매 페이지: 로그인 필요
export async function load({ parent }) {
const { loggedIn } = await parent()
if (!loggedIn) throw redirect(302, '/login?next=/autotrade')
}