File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1- import { cache } from "react" ;
2-
31import { cookies } from "next/headers" ;
42
53import { fetch as apiFetch } from "@/shared/api/server" ;
@@ -14,7 +12,7 @@ interface UserApiResponse {
1412 isOwner : boolean ;
1513}
1614
17- export const getUserProfileServer = cache ( async ( targetUserId : number ) => {
15+ export const getUserProfileServer = async ( targetUserId : number ) => {
1816 const cookieStore = await cookies ( ) ;
1917 const accessToken = cookieStore . get ( "accessToken" ) ?. value ;
2018
@@ -45,4 +43,4 @@ export const getUserProfileServer = cache(async (targetUserId: number) => {
4543 } catch {
4644 return null ;
4745 }
48- } ) ;
46+ } ;
Original file line number Diff line number Diff line change @@ -3,4 +3,9 @@ export type { UserProfileType } from "./model/types";
33export type { UserBasicInfoResponseType } from "./model/types" ;
44export { DASHBOARD_TABS } from "./model/dashboard-tabs.config" ;
55export type { TabIdType , TabConfig } from "./model/types" ;
6- export { getUserProfile , updateUserProfileImage , updateUserName } from "./api/user-api" ;
6+ export {
7+ getUserProfile ,
8+ getUserBasic ,
9+ updateUserProfileImage ,
10+ updateUserName ,
11+ } from "./api/user-api" ;
Original file line number Diff line number Diff line change 1+ "use client" ;
2+
3+ import { useQuery } from "@tanstack/react-query" ;
4+
5+ import { getUserBasic } from "@/entities/user" ;
6+
7+ const userBasicKey = [ "user" , "basic" ] as const ;
8+
9+ async function fetchUserBasic ( ) {
10+ try {
11+ return await getUserBasic ( ) ;
12+ } catch {
13+ return null ;
14+ }
15+ }
16+
17+ export function useUserBasic ( ) {
18+ return useQuery ( {
19+ queryKey : userBasicKey ,
20+ queryFn : fetchUserBasic ,
21+ retry : false ,
22+ staleTime : 1000 * 60 * 5 ,
23+ refetchOnWindowFocus : false ,
24+ } ) ;
25+ }
Original file line number Diff line number Diff line change 1- import { cookies } from "next/headers" ;
21import Image from "next/image" ;
32import Link from "next/link" ;
43
5- import { LogIn } from "lucide-react" ;
6-
74import Logo from "@/shared/assets/icons/windfall.svg" ;
8- import { ROUTES } from "@/shared/config/routes" ;
95import { Container } from "@/shared/ui" ;
10- import Button from "@/shared/ui/button/button" ;
11- import HeaderActions from "@/widgets/header/ui/header-actions" ;
6+ import HeaderAuthSlot from "@/widgets/header/ui/header-auth-slot" ;
127import HeaderSearch from "@/widgets/header/ui/header-search" ;
138
149export async function Header ( ) {
15- const cookieStore = await cookies ( ) ;
16- const userId = cookieStore . get ( "userId" ) ?. value ;
17-
18- const numericUserId = Number ( userId ) ;
19- const hasValidUserId = Number . isInteger ( numericUserId ) && numericUserId > 0 ;
20-
2110 return (
2211 < header className = "bg-background h-header sticky top-0 z-50 hidden select-none md:flex" >
2312 < Container className = "lg:px flex items-center justify-around gap-4 px-2 min-[960px]:px-3 lg:px-5 xl:px-7" >
@@ -30,16 +19,7 @@ export async function Header() {
3019
3120 < HeaderSearch />
3221
33- { hasValidUserId ? (
34- < HeaderActions userId = { numericUserId } />
35- ) : (
36- < Button asChild size = "lg" >
37- < Link href = { ROUTES . login } aria-label = "로그인" >
38- < LogIn className = "size-5" />
39- < span className = "font-semibold" > 로그인</ span >
40- </ Link >
41- </ Button >
42- ) }
22+ < HeaderAuthSlot />
4323 </ Container >
4424 </ header >
4525 ) ;
Original file line number Diff line number Diff line change 1+ "use client" ;
2+
13import Link from "next/link" ;
24
35import { Bell , Plus } from "lucide-react" ;
46
5- import { getUserProfileServer } from "@/entities/user/api/user-api.server" ;
67import { ROUTES } from "@/shared/config/routes" ;
78import { Button } from "@/shared/ui" ;
89import HeaderUserMenu from "@/widgets/header/ui/header-user-menu" ;
910
10- export default async function HeaderActions ( { userId } : { userId : number } ) {
11- const profile = await getUserProfileServer ( userId ) ;
12-
13- const avatarUrl = profile ?. avatarUrl ;
14- const avatarAlt = profile ?. name ?? "프로필" ;
15-
11+ export default function HeaderActions ( {
12+ avatarUrl,
13+ avatarAlt,
14+ } : {
15+ avatarUrl ?: string ;
16+ avatarAlt : string ;
17+ } ) {
1618 return (
1719 < div className = "flex shrink-0 items-center gap-3" >
1820 < Button asChild aria-label = "경매 등록" size = "icon-lg" className = "min-[960px]:hidden" >
@@ -33,7 +35,7 @@ export default async function HeaderActions({ userId }: { userId: number }) {
3335 < span className = "absolute top-1 right-1 h-2 w-2 rounded-full bg-red-500" />
3436 </ Button >
3537
36- < HeaderUserMenu avatarUrl = { avatarUrl ?? undefined } avatarAlt = { avatarAlt } />
38+ < HeaderUserMenu avatarUrl = { avatarUrl } avatarAlt = { avatarAlt } />
3739 </ div >
3840 ) ;
3941}
Original file line number Diff line number Diff line change 1+ "use client" ;
2+
3+ import Link from "next/link" ;
4+
5+ import { LogIn } from "lucide-react" ;
6+
7+ import { useUserBasic } from "@/features/user/api/use-user-basic" ;
8+ import { ROUTES } from "@/shared/config/routes" ;
9+ import Button from "@/shared/ui/button/button" ;
10+ import HeaderActions from "@/widgets/header/ui/header-actions" ;
11+
12+ export default function HeaderAuthSlot ( ) {
13+ const { data, isPending } = useUserBasic ( ) ;
14+
15+ if ( isPending ) {
16+ return < div className = "h-10 w-24" aria-hidden /> ;
17+ }
18+
19+ if ( ! data ) {
20+ return (
21+ < Button asChild size = "lg" >
22+ < Link href = { ROUTES . login } aria-label = "로그인" >
23+ < LogIn className = "size-5" />
24+ < span className = "font-semibold" > 로그인</ span >
25+ </ Link >
26+ </ Button >
27+ ) ;
28+ }
29+
30+ return (
31+ < HeaderActions
32+ avatarUrl = { data . userProfileUrl || undefined }
33+ avatarAlt = { data . username || "프로필" }
34+ />
35+ ) ;
36+ }
Original file line number Diff line number Diff line change 33import Link from "next/link" ;
44import { useRouter } from "next/navigation" ;
55
6+ import { useQueryClient } from "@tanstack/react-query" ;
67import { LogOut , Moon , Sun , User } from "lucide-react" ;
78import { useTheme } from "next-themes" ;
89
@@ -26,6 +27,7 @@ interface HeaderUserMenuProps {
2627
2728export default function HeaderUserMenu ( { avatarUrl, avatarAlt } : HeaderUserMenuProps ) {
2829 const router = useRouter ( ) ;
30+ const queryClient = useQueryClient ( ) ;
2931 const { resolvedTheme, setTheme } = useTheme ( ) ;
3032 const isDarkMode = resolvedTheme === "dark" ;
3133 const themeLabel = isDarkMode ? "라이트 모드" : "다크 모드" ;
@@ -40,6 +42,7 @@ export default function HeaderUserMenu({ avatarUrl, avatarAlt }: HeaderUserMenuP
4042 } catch {
4143 showToast . error ( "로그아웃에 실패했습니다. 잠시 후 다시 시도해주세요." ) ;
4244 } finally {
45+ queryClient . setQueryData ( [ "user" , "basic" ] , null ) ;
4346 router . refresh ( ) ;
4447 router . push ( ROUTES . login ) ;
4548 }
You can’t perform that action at this time.
0 commit comments