Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions src/app/(user)/mypage/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { redirect } from 'next/navigation';

import { dehydrate, HydrationBoundary } from '@tanstack/react-query';

import { API } from '@/api';
Expand All @@ -10,17 +8,15 @@ interface Props {
children: React.ReactNode;
}

export const dynamic = 'force-dynamic';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이게 뭔가용?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next.js는 기본적으로 빌드할 때 SSG가 되므로 prerendering을 하는데, users/me api가 기본적으로 token이 필요한 api라 빌드 시점에 렌더링이 되지 않는 문제가 있어요

그래서 이걸 추가해주면 prerendering을 하지 않게 됩니다!

단점은 SEO 적용이 어렵다는 점이 있는데, 어차피 권한이 필요한 페이지라 상관이 없어서 추가했어요 :)


const MyPageLayout = async ({ children }: Props) => {
const queryClient = getQueryClient();

try {
await queryClient.fetchQuery({
queryKey: userKeys.me(),
queryFn: () => API.userService.getMe(),
});
} catch {
redirect('/login');
}
await queryClient.fetchQuery({
queryKey: userKeys.me(),
queryFn: () => API.userService.getMe(),
});
Comment on lines +16 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

API 실패 시 에러 처리가 누락되었습니다.

이전에 존재하던 try/catch 에러 처리 로직이 제거되었습니다. 현재 코드에서는 getMe() API 호출이 실패할 경우 (네트워크 오류, 인증 실패 등) 처리되지 않은 예외가 발생하여 전체 페이지가 에러 상태가 됩니다.

사용자 경험 향상을 위해 에러 처리를 추가하거나, 최소한 적절한 에러 바운더리를 설정하는 것을 권장합니다.

🔎 에러 처리 추가 예시
 const MyPageLayout = async ({ children }: Props) => {
   const queryClient = getQueryClient();
 
-  await queryClient.fetchQuery({
-    queryKey: userKeys.me(),
-    queryFn: () => API.userService.getMe(),
-  });
+  try {
+    await queryClient.fetchQuery({
+      queryKey: userKeys.me(),
+      queryFn: () => API.userService.getMe(),
+    });
+  } catch (error) {
+    // 적절한 에러 처리 (예: 로그인 페이지로 리다이렉트)
+    redirect('/login');
+  }
 
   return <HydrationBoundary state={dehydrate(queryClient)}>{children}</HydrationBoundary>;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await queryClient.fetchQuery({
queryKey: userKeys.me(),
queryFn: () => API.userService.getMe(),
});
try {
await queryClient.fetchQuery({
queryKey: userKeys.me(),
queryFn: () => API.userService.getMe(),
});
} catch (error) {
// 적절한 에러 처리 (예: 로그인 페이지로 리다이렉트)
redirect('/login');
}
🤖 Prompt for AI Agents
In src/app/(user)/mypage/layout.tsx around lines 16 to 19, the call to
queryClient.fetchQuery for userKeys.me() lacks error handling so any API failure
will throw and crash the page; wrap the fetchQuery call in a try/catch (or use
.catch) to handle network/auth errors, log the error, and provide a fallback
path such as returning a safe default user state, redirecting to a login/error
page, or throwing a controlled error for an ErrorBoundary so the page doesn't
crash unexpectedly; ensure the chosen branch preserves type expectations and
downstream rendering (e.g., return null or a default object) and surface
user-friendly behavior.


return <HydrationBoundary state={dehydrate(queryClient)}>{children}</HydrationBoundary>;
};
Expand Down
17 changes: 7 additions & 10 deletions src/app/(user)/profile/[userId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { redirect } from 'next/navigation';
import { notFound, redirect } from 'next/navigation';

import { dehydrate, HydrationBoundary } from '@tanstack/react-query';

Expand All @@ -15,22 +15,19 @@ const ProfileLayout = async ({ children, params }: Props) => {
const { userId: id } = await params;
const userId = Number(id);

const { cookies } = await import('next/headers');
const cookieStore = await cookies();
const myId = Number(cookieStore.get('userId')?.value);

// 본인 id와 같은지 확인 후 같으면 mypage로 리다이렉트
if (userId === myId) {
redirect('/mypage');
}
// userId가 숫자가 아닌 경우 notFound redirect 처리
if (isNaN(userId)) notFound();

const queryClient = getQueryClient();

await queryClient.fetchQuery({
const user = await queryClient.fetchQuery({
queryKey: userKeys.item(userId),
queryFn: () => API.userService.getUser({ userId }),
});

// isFollow가 null이면 본인 페이지 이므로 mypage로 redirect 처리
if (user.isFollow === null) redirect('/mypage');
Comment on lines +23 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the getUser API implementation and schema
find . -type f -name "*.ts" -o -name "*.tsx" | head -20

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 692


🏁 Script executed:

# Search for API service definition to understand getUser response
rg "getUser" --type ts --type tsx -A 5 | head -50

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 98


🏁 Script executed:

# Look for userService implementation
rg "userService\|API\.user" --type ts --type tsx -B 2 -A 2 | head -50

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 98


🏁 Script executed:

# Check the mypage layout for error handling pattern mentioned in review
find . -type f -name "*mypage*" -o -name "*layout.tsx" | grep -i mypage

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 132


🏁 Script executed:

# Search for isFollow field usage in the codebase
rg "isFollow" --type ts --type tsx -B 3 -A 3

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 98


🏁 Script executed:

# Search for getUser without type restriction
rg "getUser" -A 5 -B 2 | head -60

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 2794


🏁 Script executed:

# Look for isFollow field in codebase
rg "isFollow" -A 3 -B 3 | head -80

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 4480


🏁 Script executed:

# Check user service implementation
cat src/api/service/user-service/index.ts

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 2135


🏁 Script executed:

# Check mypage layout for error handling
cat src/app/\(user\)/mypage/layout.tsx

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 706


🏁 Script executed:

# Check the profile layout file
cat src/app/\(user\)/profile/\[userId\]/layout.tsx

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 1054


fetchQuery 호출 시 에러 처리가 필요합니다.

현재 코드에서 API.userService.getUser() 호출이 실패하면 처리되지 않은 예외가 발생합니다. API 호출 실패에 대한 에러 처리(에러 바운더리 또는 try-catch)를 추가하세요.

참고: isFollow: null이 본인 프로필을 나타내는 API 계약은 타입 정의와 모의 데이터로 확인되었으므로 현재 로직은 올바릅니다.

🤖 Prompt for AI Agents
In src/app/(user)/profile/[userId]/layout.tsx around lines 23-29, wrap the
queryClient.fetchQuery call in a try-catch and handle API failures instead of
letting exceptions escape: call fetchQuery inside try, keep the existing
isFollow===null check and redirect('/mypage') as before, and in the catch either
redirect to a safe error route (e.g. redirect('/error')) or rethrow/throw a
Next.js Response to trigger the app error boundary, while optionally logging the
caught error for diagnostics.


return <HydrationBoundary state={dehydrate(queryClient)}>{children}</HydrationBoundary>;
};

Expand Down
2 changes: 1 addition & 1 deletion src/mock/service/user/user-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const mockUserItems: User[] = [
createdAt: '2025-12-07T17:00:00+09:00',
groupJoinedCnt: 5,
groupCreatedCnt: 3,
isFollow: true,
isFollow: null,
},
{
userId: 2,
Expand Down
2 changes: 1 addition & 1 deletion src/types/service/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface User {
groupCreatedCnt: number;
isNotificationEnabled: boolean;
createdAt: string;
isFollow: boolean;
isFollow: boolean | null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for all isFollow usage in the codebase
rg -nP --type=ts --type=tsx '\.isFollow\b' -g '!*.test.*' -g '!*.spec.*' -g '!node_modules'

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 98


🏁 Script executed:

# Check the type definition file and related files
cat -n src/types/service/user.ts

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 1424


🏁 Script executed:

# Search for isFollow in condition checks to see how it's being handled
rg -nP --type=ts --type=tsx 'isFollow\s*(===|!==|==|!=|\?|!)' -g '!*.test.*' -g '!*.spec.*' -g '!node_modules'

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 98


🏁 Script executed:

# Search for isFollow usage without specifying tsx
rg -nP '\.isFollow\b' -g '!*.test.*' -g '!*.spec.*' -g '!node_modules' | head -50

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 840


🏁 Script executed:

# Search for isFollow in condition checks
rg -nP 'isFollow\s*(===|!==|==|!=|\?|!)' -g '!*.test.*' -g '!*.spec.*' -g '!node_modules'

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 616


🏁 Script executed:

# Look at profile-related files that handle isFollow
fd -e ts -e js -e jsx -e tsx 'profile' | grep -v node_modules | grep -v test | head -20

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 53


🏁 Script executed:

# Check the mock handler mentioned in learnings
cat -n src/mock/service/user/user-handler.ts | head -50

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 1748


🏁 Script executed:

# Check the profile-info component in detail to understand the logic
cat -n src/components/pages/user/profile/profile-info/index.tsx | sed -n '25,40p'

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 589


🏁 Script executed:

# Verify if there are any other isFollow usages we missed
rg 'isFollow' -g '!*.test.*' -g '!*.spec.*' -g '!node_modules' --count-matches

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 279


🏁 Script executed:

# Check the actual user mock data to see what values are set
fd 'user-mock' | xargs cat -n | head -60

Repository: WeGo-Together/WeGo_FrontEnd

Length of output: 2063


프로필 정보 컴포넌트에 명시적 null 방어 로직 추가 필요

isFollow 타입 변경이 코드베이스 전체에 통합되어 있으며, mock 핸들러와 라우트 가드에서 null 값을 적절히 처리하고 있습니다. 다만 src/components/pages/user/profile/profile-info/index.tsx의 31-32번 라인에서 !user.isFollowuser.isFollow 조건을 사용할 때 명시적인 null 체크가 없습니다.

현재는 layout.tsx의 null 체크 리다이렉트(if (user.isFollow === null) redirect('/mypage'))로 보호되고 있지만, 향후 리팩토링 시 이 가드가 제거되면 null 값이 통과되어 자신의 프로필에서도 "팔로우 하기" 버튼이 표시될 수 있습니다. 방어적 프로그래밍을 위해 profile-info 컴포넌트에도 user.isFollow !== null 체크를 추가하는 것을 권장합니다.

🤖 Prompt for AI Agents
In src/components/pages/user/profile/profile-info/index.tsx around lines 31-32,
the component uses `!user.isFollow` and `user.isFollow` without guarding against
`null`; add an explicit null check (e.g., `user.isFollow !== null && ...`) to
both conditional branches so the UI only treats boolean values and ignores
`null`, preventing the "Follow" button from appearing when `isFollow` is `null`.

}

export interface GetUserPathParams {
Expand Down