2025-10-10 11:22:43 +09:00
|
|
|
"use client";
|
2025-10-10 16:07:56 +09:00
|
|
|
// 클라이언트 훅(useState/useEffect)을 사용하여 세션 표시/로그아웃을 처리합니다.
|
2025-10-13 08:01:24 +09:00
|
|
|
import Image from "next/image";
|
|
|
|
|
import Link from "next/link";
|
2025-10-09 15:36:13 +09:00
|
|
|
import { ThemeToggle } from "@/app/components/ThemeToggle";
|
2025-10-09 16:35:19 +09:00
|
|
|
import { SearchBar } from "@/app/components/SearchBar";
|
2025-10-10 11:22:43 +09:00
|
|
|
import { Button } from "@/app/components/ui/Button";
|
|
|
|
|
import React from "react";
|
2025-10-09 15:36:13 +09:00
|
|
|
|
2025-10-09 15:22:05 +09:00
|
|
|
export function AppHeader() {
|
2025-10-10 11:22:43 +09:00
|
|
|
const [user, setUser] = React.useState<{ nickname: string } | null>(null);
|
2025-10-13 08:13:53 +09:00
|
|
|
const [categories, setCategories] = React.useState<Array<{ id: string; name: string; slug: string; boards: Array<{ id: string; name: string; slug: string }> }>>([]);
|
|
|
|
|
const [openSlug, setOpenSlug] = React.useState<string | null>(null);
|
2025-10-10 16:07:56 +09:00
|
|
|
// 헤더 마운트 시 세션 존재 여부를 조회해 로그인/로그아웃 UI를 제어합니다.
|
2025-10-10 11:22:43 +09:00
|
|
|
React.useEffect(() => {
|
|
|
|
|
fetch("/api/auth/session")
|
|
|
|
|
.then((r) => r.json())
|
|
|
|
|
.then((d) => setUser(d?.ok ? d.user : null))
|
|
|
|
|
.catch(() => setUser(null));
|
2025-10-13 08:13:53 +09:00
|
|
|
fetch("/api/categories", { cache: "no-store" })
|
|
|
|
|
.then((r) => r.json())
|
|
|
|
|
.then((d) => setCategories(d?.categories || []))
|
|
|
|
|
.catch(() => setCategories([]));
|
2025-10-10 11:22:43 +09:00
|
|
|
}, []);
|
|
|
|
|
const onLogout = async () => {
|
|
|
|
|
await fetch("/api/auth/session", { method: "DELETE" });
|
|
|
|
|
setUser(null);
|
|
|
|
|
location.reload();
|
|
|
|
|
};
|
2025-10-09 15:22:05 +09:00
|
|
|
return (
|
2025-10-13 08:01:24 +09:00
|
|
|
<header style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: 12 }}>
|
|
|
|
|
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
|
|
|
|
<Link href="/" aria-label="홈">
|
|
|
|
|
<Image src="/logo.png" alt="logo" width={120} height={28} priority />
|
|
|
|
|
</Link>
|
|
|
|
|
</div>
|
2025-10-09 16:35:19 +09:00
|
|
|
<nav style={{ display: "flex", gap: 12, alignItems: "center" }}>
|
2025-10-13 08:13:53 +09:00
|
|
|
{categories.map((cat) => (
|
|
|
|
|
<div
|
|
|
|
|
key={cat.id}
|
|
|
|
|
onMouseEnter={() => setOpenSlug(cat.slug)}
|
|
|
|
|
onMouseLeave={() => setOpenSlug((s) => (s === cat.slug ? null : s))}
|
|
|
|
|
style={{ position: "relative" }}
|
|
|
|
|
>
|
|
|
|
|
<Link href={`/boards?category=${cat.slug}`}>{cat.name}</Link>
|
|
|
|
|
{openSlug === cat.slug && (
|
|
|
|
|
<div style={{ position: "absolute", top: "100%", left: 0, background: "white", border: "1px solid #eee", padding: 8, minWidth: 200, zIndex: 50 }}>
|
|
|
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
|
|
|
|
|
{cat.boards.map((b) => (
|
|
|
|
|
<Link key={b.id} href={`/boards/${b.id}`}>{b.name}</Link>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
2025-10-09 16:35:19 +09:00
|
|
|
<SearchBar />
|
2025-10-09 15:36:13 +09:00
|
|
|
<ThemeToggle />
|
2025-10-10 11:22:43 +09:00
|
|
|
{user ? (
|
|
|
|
|
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
|
|
|
<span>{user.nickname}님</span>
|
|
|
|
|
<Button variant="ghost" onClick={onLogout}>로그아웃</Button>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
2025-10-13 08:01:24 +09:00
|
|
|
<Link href="/login">로그인</Link>
|
2025-10-10 11:22:43 +09:00
|
|
|
)}
|
2025-10-09 15:22:05 +09:00
|
|
|
</nav>
|
|
|
|
|
</header>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|