diff --git a/src/app/(with-layout)/_shared/services/logout-api.ts b/src/app/(with-layout)/_shared/services/logout-api.ts new file mode 100644 index 0000000..8b53abb --- /dev/null +++ b/src/app/(with-layout)/_shared/services/logout-api.ts @@ -0,0 +1,13 @@ +import httpClient from "@/lib/http-client"; +import { CustomError } from "@/lib/error/custom-error"; +import { ErrorCode } from "@/lib/error/error-code"; + +export async function logout(): Promise { + const api = httpClient(); + const res = await api("/api/logout", { + method: "POST" + }); + if (!res.ok) { + throw new CustomError(ErrorCode.INVALID_REQUEST); + } +} \ No newline at end of file diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index ee57cb8..cb357c7 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -3,8 +3,7 @@ import { LoginRequest, TokenPair } from "@/app/(without-layout)/login/_shared/se import { CustomError } from "@/lib/error/custom-error"; import { ErrorCode } from "@/lib/error/error-code"; import { isLocalhost } from "@/lib/utils"; - -const API_BASE_URL = process.env.API_BASE_URL ?? ''; +import { API_BASE_URL } from "@/lib/api/types"; export async function POST(req: NextRequest): Promise { const ctx = req.headers.get('content-type') || ''; diff --git a/src/app/api/logout/route.ts b/src/app/api/logout/route.ts new file mode 100644 index 0000000..4d88a72 --- /dev/null +++ b/src/app/api/logout/route.ts @@ -0,0 +1,15 @@ +import { API_BASE_URL } from "@/lib/api/types"; +import { CustomError } from "@/lib/error/custom-error"; +import { ErrorCode } from "@/lib/error/error-code"; + +export async function POST(): Promise { + const form = new FormData(); + try { + return await fetch(`${API_BASE_URL}/api/admin/logout`, { + method: 'POST', + body: form + }); + } catch { + throw new CustomError(ErrorCode.INVALID_REQUEST); + } +} \ No newline at end of file diff --git a/src/components/common/app-sidebar.tsx b/src/components/common/app-sidebar.tsx index 6e7ebbc..0809c21 100644 --- a/src/components/common/app-sidebar.tsx +++ b/src/components/common/app-sidebar.tsx @@ -1,3 +1,5 @@ +"use client" + import { Sidebar, SidebarContent, @@ -10,9 +12,13 @@ import { SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar"; -import { Home, Inbox, Settings, User } from "lucide-react"; +import { Home, Inbox, User } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { logout } from "@/app/(with-layout)/_shared/services/logout-api"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; // Menu items. const items = [ @@ -30,15 +36,28 @@ const items = [ title: "회원관리", url: "/member", icon: User, - }, - { - title: "로그아웃", - url: "/logout", - icon: Settings } ] export default function AppSidebar() { + const router = useRouter(); + const [loading, setLoading] = useState(false); + + const onClickLogout = async () => { + if (loading) { + return; + } + setLoading(true); + try { + await logout(); + localStorage.removeItem('accessToken'); + localStorage.removeItem('refreshToken'); + router.replace('/login'); + } finally { + setLoading(false); + } + } + return ( @@ -66,7 +85,19 @@ export default function AppSidebar() { - + + + + + + + - ) + ); }; \ No newline at end of file diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts new file mode 100644 index 0000000..94c41fd --- /dev/null +++ b/src/lib/api/types.ts @@ -0,0 +1,3 @@ +const API_BASE_URL = process.env.API_BASE_URL + +export { API_BASE_URL } \ No newline at end of file