Skip to content

Commit 736a2b9

Browse files
committed
fix(users): SSR에 데이터 주입해서 레이아웃시프팅 해결 시도
1 parent 4d1fc6e commit 736a2b9

5 files changed

Lines changed: 26 additions & 10 deletions

File tree

src/app/(protected)/users/[userId]/[tab]/page.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getUserProfileServer } from "@/entities/user/api/user-api.server";
12
import { UserTabContent } from "@/screens/user";
23

34
interface PageProps {
@@ -9,8 +10,8 @@ interface PageProps {
910

1011
export default async function Page({ params }: PageProps) {
1112
const { userId, tab } = await params;
12-
1313
const targetUserId = Number(userId);
14+
const profile = await getUserProfileServer(targetUserId);
1415

15-
return <UserTabContent tabId={tab} targetUserId={targetUserId} />;
16+
return <UserTabContent tabId={tab} targetUserId={targetUserId} initialData={profile} />;
1617
}

src/app/(protected)/users/[userId]/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default async function UserLayout({ children, params }: LayoutProps) {
3131
return (
3232
<HydrationBoundary state={dehydrate(queryClient)}>
3333
<main className="bg-background mx-auto flex min-h-screen w-full max-w-7xl flex-col items-center gap-4 px-4 py-6 2xl:px-0">
34-
<UserDashboardHeader targetUserId={targetUserId} />
34+
<UserDashboardHeader targetUserId={targetUserId} initialData={profile} />
3535
<div className="w-full">{children}</div>
3636
</main>
3737
</HydrationBoundary>

src/entities/user/api/user-api.server.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { fetch as apiFetch } from "@/shared/api/server";
44
import { API_ENDPOINTS } from "@/shared/config/endpoints";
55

66
interface UserApiResponse {
7+
userId: number;
78
username: string;
89
email: string;
910
profileImage: string | null;
@@ -30,9 +31,10 @@ export const getUserProfileServer = async (targetUserId: number) => {
3031

3132
const { data } = response;
3233

33-
if (!data) return null;
34+
if (!data) return undefined;
3435

3536
return {
37+
userId: data.userId,
3638
name: data.username,
3739
email: data.email,
3840
avatarUrl: data.profileImage || undefined,
@@ -41,6 +43,6 @@ export const getUserProfileServer = async (targetUserId: number) => {
4143
isOwner: data.isOwner,
4244
};
4345
} catch {
44-
return null;
46+
return undefined;
4547
}
4648
};

src/screens/user/ui/user-tab-content.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import dynamic from "next/dynamic";
66
import { notFound } from "next/navigation";
77

88
import { DASHBOARD_TABS, type TabIdType } from "@/entities/user";
9+
import { UserProfileType } from "@/entities/user";
910
import { TabLabelHeader } from "@/features/user";
1011
import { useUserProfile } from "@/features/user/api/use-my-profile";
1112
import { CommonItemTabSkeleton, ReviewTabSkeleton, CalendarTabSkeleton } from "@/widgets/user";
@@ -68,9 +69,10 @@ function isValidTabId(id: string): id is TabIdType {
6869
interface UserTabContentProps {
6970
tabId: string;
7071
targetUserId: number;
72+
initialData: UserProfileType | undefined;
7173
}
7274

73-
export function UserTabContent({ tabId, targetUserId }: UserTabContentProps) {
75+
export function UserTabContent({ tabId, targetUserId, initialData }: UserTabContentProps) {
7476
if (!isValidTabId(tabId)) notFound();
7577

7678
const currentTabId = tabId;
@@ -79,7 +81,12 @@ export function UserTabContent({ tabId, targetUserId }: UserTabContentProps) {
7981
const currentTabConfig = DASHBOARD_TABS.find((t) => t.id === currentTabId);
8082
if (!currentTabConfig) notFound();
8183

82-
const { data: profile, isLoading, isFetching, isPending } = useUserProfile(targetUserId);
84+
const {
85+
data: profile,
86+
isLoading,
87+
isFetching,
88+
isPending,
89+
} = useUserProfile(targetUserId, initialData);
8390

8491
// 핵심: 프로필/권한이 "확정"되기 전에는 비공개 판정을 하지 말고
8592
// 서버/클라 동일하게 스켈레톤을 렌더해서 트리를 고정한다.

src/widgets/user/ui/user-dashboard-header.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@
22

33
import { useParams } from "next/navigation";
44

5-
import { DASHBOARD_TABS } from "@/entities/user";
5+
import { DASHBOARD_TABS, UserProfileType } from "@/entities/user";
66
import { ActivityTabs, UserProfileCard } from "@/features/user";
77
import { useUserProfile } from "@/features/user/api/use-my-profile";
88

9-
export function UserDashboardHeader({ targetUserId }: { targetUserId: number }) {
9+
interface UserDashboardHeaderProps {
10+
targetUserId: number;
11+
initialData: UserProfileType; // 추가
12+
}
13+
14+
export function UserDashboardHeader({ targetUserId, initialData }: UserDashboardHeaderProps) {
1015
const params = useParams();
1116
const activeTab = (params.tab as string) || "calendar";
1217

13-
const { data: profile, isLoading } = useUserProfile(targetUserId);
18+
const { data: profile, isLoading } = useUserProfile(targetUserId, initialData);
19+
1420
if (isLoading) {
1521
return (
1622
<div className="flex flex-col gap-4" data-testid="user-dashboard-header-skeleton">

0 commit comments

Comments
 (0)