diff --git a/next.config.ts b/next.config.ts index a20f13a9..9ae4dc70 100644 --- a/next.config.ts +++ b/next.config.ts @@ -11,11 +11,12 @@ const nextConfig: NextConfig = { { protocol: "https", hostname: "static.toss.im" }, { protocol: "https", hostname: "windfall-bucket.s3.ap-northeast-2.amazonaws.com" }, { protocol: "https", hostname: "wind-fall.store" }, + { protocol: "http", hostname: "img1.kakaocdn.net" }, { protocol: "http", hostname: "phinf.pstatic.net" }, - { protocol: "https", hostname: "phinf.pstatic.net" }, { protocol: "http", hostname: "ssl.pstatic.net" }, - { protocol: "https", hostname: "ssl.pstatic.net" }, { protocol: "http", hostname: "lh3.googleusercontent.com" }, + { protocol: "https", hostname: "phinf.pstatic.net" }, + { protocol: "https", hostname: "ssl.pstatic.net" }, { protocol: "https", hostname: "lh3.googleusercontent.com" }, ], }, diff --git a/src/app/(protected)/users/[userId]/[tab]/page.tsx b/src/app/(protected)/users/[userId]/[tab]/page.tsx index a81ea0f6..dad43623 100644 --- a/src/app/(protected)/users/[userId]/[tab]/page.tsx +++ b/src/app/(protected)/users/[userId]/[tab]/page.tsx @@ -1,3 +1,4 @@ +import { getUserProfileServer } from "@/entities/user/api/user-api.server"; import { UserTabContent } from "@/screens/user"; interface PageProps { @@ -9,8 +10,8 @@ interface PageProps { export default async function Page({ params }: PageProps) { const { userId, tab } = await params; - const targetUserId = Number(userId); + const profile = await getUserProfileServer(targetUserId); - return ; + return ; } diff --git a/src/app/(protected)/users/[userId]/layout.tsx b/src/app/(protected)/users/[userId]/layout.tsx index 136011ac..32292eee 100644 --- a/src/app/(protected)/users/[userId]/layout.tsx +++ b/src/app/(protected)/users/[userId]/layout.tsx @@ -31,7 +31,7 @@ export default async function UserLayout({ children, params }: LayoutProps) { return (
- +
{children}
diff --git a/src/entities/user/api/user-api.server.ts b/src/entities/user/api/user-api.server.ts index 0b82ab9a..978bddad 100644 --- a/src/entities/user/api/user-api.server.ts +++ b/src/entities/user/api/user-api.server.ts @@ -4,6 +4,7 @@ import { fetch as apiFetch } from "@/shared/api/server"; import { API_ENDPOINTS } from "@/shared/config/endpoints"; interface UserApiResponse { + userId: number; username: string; email: string; profileImage: string | null; @@ -30,9 +31,10 @@ export const getUserProfileServer = async (targetUserId: number) => { const { data } = response; - if (!data) return null; + if (!data) return undefined; return { + userId: data.userId, name: data.username, email: data.email, avatarUrl: data.profileImage || undefined, @@ -41,6 +43,6 @@ export const getUserProfileServer = async (targetUserId: number) => { isOwner: data.isOwner, }; } catch { - return null; + return undefined; } }; diff --git a/src/screens/user/ui/user-tab-content.tsx b/src/screens/user/ui/user-tab-content.tsx index c5231d52..3b1f5833 100644 --- a/src/screens/user/ui/user-tab-content.tsx +++ b/src/screens/user/ui/user-tab-content.tsx @@ -6,6 +6,7 @@ import dynamic from "next/dynamic"; import { notFound } from "next/navigation"; import { DASHBOARD_TABS, type TabIdType } from "@/entities/user"; +import { UserProfileType } from "@/entities/user"; import { TabLabelHeader } from "@/features/user"; import { useUserProfile } from "@/features/user/api/use-my-profile"; import { CommonItemTabSkeleton, ReviewTabSkeleton, CalendarTabSkeleton } from "@/widgets/user"; @@ -68,9 +69,10 @@ function isValidTabId(id: string): id is TabIdType { interface UserTabContentProps { tabId: string; targetUserId: number; + initialData: UserProfileType | undefined; } -export function UserTabContent({ tabId, targetUserId }: UserTabContentProps) { +export function UserTabContent({ tabId, targetUserId, initialData }: UserTabContentProps) { if (!isValidTabId(tabId)) notFound(); const currentTabId = tabId; @@ -79,7 +81,12 @@ export function UserTabContent({ tabId, targetUserId }: UserTabContentProps) { const currentTabConfig = DASHBOARD_TABS.find((t) => t.id === currentTabId); if (!currentTabConfig) notFound(); - const { data: profile, isLoading, isFetching, isPending } = useUserProfile(targetUserId); + const { + data: profile, + isLoading, + isFetching, + isPending, + } = useUserProfile(targetUserId, initialData); // 핵심: 프로필/권한이 "확정"되기 전에는 비공개 판정을 하지 말고 // 서버/클라 동일하게 스켈레톤을 렌더해서 트리를 고정한다. diff --git a/src/widgets/user/ui/user-dashboard-header.tsx b/src/widgets/user/ui/user-dashboard-header.tsx index d2b04a75..89d11151 100644 --- a/src/widgets/user/ui/user-dashboard-header.tsx +++ b/src/widgets/user/ui/user-dashboard-header.tsx @@ -2,15 +2,21 @@ import { useParams } from "next/navigation"; -import { DASHBOARD_TABS } from "@/entities/user"; +import { DASHBOARD_TABS, UserProfileType } from "@/entities/user"; import { ActivityTabs, UserProfileCard } from "@/features/user"; import { useUserProfile } from "@/features/user/api/use-my-profile"; -export function UserDashboardHeader({ targetUserId }: { targetUserId: number }) { +interface UserDashboardHeaderProps { + targetUserId: number; + initialData?: UserProfileType; // initial user profile data used before fetching +} + +export function UserDashboardHeader({ targetUserId, initialData }: UserDashboardHeaderProps) { const params = useParams(); const activeTab = (params.tab as string) || "calendar"; - const { data: profile, isLoading } = useUserProfile(targetUserId); + const { data: profile, isLoading } = useUserProfile(targetUserId, initialData); + if (isLoading) { return (