-
Notifications
You must be signed in to change notification settings - Fork 0
[Fix] axios 인터셉터 status 처리, redirect 로직 수정 #181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 📋 변경 사항 분석걸어보기
변경사항
시퀀스 다이어그램sequenceDiagram
participant Client as 클라이언트
participant API as API 핸들러
participant Env as 환경 감지
participant Nav as next/navigation
participant Router as 라우터/서버
Client->>API: API 요청 (401/404 응답)
API->>API: errorResponse 처리
alt 상태 = 401
API->>Env: 환경 확인 (서버/클라이언트)
rect rgb(200, 220, 255)
Note over Env,Router: 서버 사이드
Env->>Nav: redirect 동적 임포트
Nav->>Router: /login으로 리다이렉트
end
rect rgb(255, 240, 200)
Note over Env,Client: 클라이언트 사이드
Env->>Nav: useRouter 동적 임포트
Nav->>Client: error & path 쿼리 파라미터<br/>함께 /login으로 리다이렉트
end
else 상태 = 404
API->>Nav: notFound 동적 임포트
Nav->>Client: 404 페이지 표시
end
예상 코드 리뷰 노력🎯 3 (중간 복잡도) | ⏱️ ~20분
제안된 검토자
시🐰
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎨 Storybook Report✅ 변경 사항이 없습니다 모든 Story가 이전 빌드와 동일합니다.
|
🎭 Playwright Report✨ E2E Test가 성공적으로 완료되었습니다. Test 요약 내용을 확인해주세요.
📊 Test Summary
📜 Test Details✅ Passed Tests (9)
|
📊 Coverage Report
➡️ #181을 main에 병합하면 coverage가 Coverage 요약@@ Coverage Diff @@
## main #181 +/- ##
===========================================
Coverage 35.57% 35.57% 0.00%
===========================================
Files 149 149 0
Lines 6144 6144 0
Branches 246 246 0
===========================================
Hits 2186 2186 0
Misses 3958 3958 0 영향받은 파일✅ 이 PR로 영향받은 파일이 없습니다 수정된 모든 파일이 현재 coverage를 유지했습니다. |
🚀 PR Preview Report✨ Build가 성공적으로 완료되었습니다. Preview에서 변경사항을 확인하세요.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/api/core/index.ts
Outdated
| const { status } = errorResponse; | ||
| const isServer = typeof window === 'undefined'; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HTTP status 분기 기준이 payload(errorResponse.status)라 오동작 여지 있음
Axios의 실제 상태코드(error.response?.status)를 1순위로 쓰고, payload status는 fallback으로 두는 편이 안전합니다.
- const { status } = errorResponse;
+ const status = error.response?.status ?? errorResponse.status;
const isServer = typeof window === 'undefined';📝 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.
| const { status } = errorResponse; | |
| const isServer = typeof window === 'undefined'; | |
| const status = error.response?.status ?? errorResponse.status; | |
| const isServer = typeof window === 'undefined'; |
🤖 Prompt for AI Agents
In src/api/core/index.ts around lines 47-49, the code currently uses payload
status (errorResponse.status) as the primary HTTP status which can misbehave;
change the logic to prefer Axios' actual HTTP status (error.response?.status)
first and only fall back to errorResponse.status when that is undefined or null,
ensuring you safely access error.response and handle undefined values before
assigning to the status variable so downstream branches use the real HTTP
status.
| if (status === 401) { | ||
| if (isServer) { | ||
| const { redirect } = await import('next/navigation'); | ||
| redirect('/login'); | ||
| } else { | ||
| const currentPath = window.location.pathname + window.location.search; | ||
| window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
401 클라이언트 리다이렉트는 /login에서 루프 가능성 가드 필요
로그인 페이지에서 401이 다시 터지면 /login?...로 계속 이동할 수 있어, 최소한 현재 경로가 /login일 때는 리다이렉트를 스킵하는 방어 로직을 권장합니다.
} else {
+ if (window.location.pathname === '/login') {
+ throw errorResponse;
+ }
const currentPath = window.location.pathname + window.location.search;
window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`;
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/api/core/index.ts around lines 50-58, the redirect-on-401 logic needs a
guard to avoid redirect loops back to /login; before calling redirect('/login')
on the server, obtain the current request path from the server request/context
and skip the redirect if the path is '/login' (or startsWith '/login');
similarly on the client, check window.location.pathname (or pathname+search) and
only set window.location.href to the login URL when the current path is not
'/login' (or doesn’t start with '/login') so repeated 401s on the login page
won’t trigger continuous redirects.
| if (status === 401) { | ||
| if (isServer) { | ||
| const { redirect } = await import('next/navigation'); | ||
| redirect('/login'); | ||
| } else { | ||
| const currentPath = window.location.pathname + window.location.search; | ||
| window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`; | ||
| } | ||
| } | ||
| if (status === 404) { | ||
| const { notFound } = await import('next/navigation'); | ||
| notFound(); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Next.js notFound() function server-only api client-side compatibility
💡 Result:
Short answer: notFound() is a server-only API — it is designed to be called from Server Components, route handlers, or Server Functions (files/functions marked to run on the server). Calling notFound() from a Client Component will not work and will produce errors; instead you must either (a) perform the check on the server and call notFound() there, or (b) call a server action (a function marked "use server") which calls notFound(), or (c) handle navigation on the client (e.g. router.replace to a 404 route or your custom not-found UI). [1][2][3]
Pointers and sources:
- notFound() docs (server-only behavior & usage examples). [1]
- Server Functions / "use server" — create server functions that client code can call to run server-only logic (including calling notFound()). [2]
- Explanation of server-only vs client bundles and the server-only package (prevents importing server-only code into client). Useful when you want to ensure not to leak server-only code into the client. [3]
If you want, tell me which component/file you’re trying to call notFound() from and I’ll give a concrete refactor (server function pattern or client navigation) with code.
🌐 Web query:
Next.js app router next/navigation notFound() can be called from client component
💡 Result:
Short answer: No — notFound() is a server-side App‑Router function and cannot be directly invoked inside a Client Component. Use a server-side route/handler or a Server Action to call notFound(), or navigate the client to a 404 route (e.g. router.push('/404')) from the client instead. [1][2]
References
- notFound() docs (throws to terminate server rendering / used inside route segments). [1]
- redirect() docs (explicitly shows client usage for redirect to contrast behavior). [2]
Sources
- Next.js docs — notFound. [1]
- Next.js docs — redirect (Client Component example). [2]
🌐 Web query:
Next.js "use server" directive notFound() next/navigation client-side runtime error
💡 Result:
Likely cause
- You're calling notFound() or using the "use server" directive from code that Next.js is treating as a Client Component (or importing a "use server" file into something that runs on the client). notFound() and Server Functions must run in server context; "use server" files must only export async server functions and cannot be shipped to the browser. [1][2]
How to fix (short)
- Ensure the module that calls notFound() is a Server Component (no "use client" at top). notFound() is server-only. [1]
- If you need a Server Action callable from the client, put it in its own file with 'use server' at the very top and export an async function; import that server file only into Server Components and pass the function to Client Components via props. Do NOT put 'use server' inside a file that Next treats as a client component. [2][3]
- Verify exported values in 'use server' files are async functions (not objects/values). If any export is non-async, you’ll get the invalid "use server" value error. [3]
- Check your import graph: a server-only file must not be imported (directly or transitively) into a client component. Move shared code into server-safe or client-safe modules accordingly. [2][3]
If you want, paste the minimal file(s) (page/component + any imports) and I’ll point exactly which file should be server vs client and what to change.
Sources
- notFound() docs. [1]
- 'use server' directive docs + Server Actions guidance. [2]
- Invalid "use server" value / common errors. [3]
References
[1] Next.js — Functions: notFound.
[2] Next.js — Directives: 'use server'.
[3] Next.js — Invalid "use server" Value.
클라이언트에서 notFound() 호출은 런타임 에러 발생(서버 전용 API)
현재 코드(59-62줄)는 클라이언트에서도 next/navigation의 notFound()를 호출합니다. notFound()는 Next.js 서버 전용 API로, 클라이언트 측에서 실행되면 런타임 크래시가 발생합니다. 401 처리와 동일하게 isServer로 분기하여 서버에서는 notFound() 호출, 클라이언트에서는 /404로 이동하도록 처리해야 합니다.
- if (status === 404) {
- const { notFound } = await import('next/navigation');
- notFound();
- }
+ if (status === 404) {
+ if (isServer) {
+ const { notFound } = await import('next/navigation');
+ notFound();
+ } else {
+ window.location.href = '/404';
+ }
+ }📝 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.
| if (status === 401) { | |
| if (isServer) { | |
| const { redirect } = await import('next/navigation'); | |
| redirect('/login'); | |
| } else { | |
| const currentPath = window.location.pathname + window.location.search; | |
| window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`; | |
| } | |
| } | |
| if (status === 404) { | |
| const { notFound } = await import('next/navigation'); | |
| notFound(); | |
| } | |
| if (status === 401) { | |
| if (isServer) { | |
| const { redirect } = await import('next/navigation'); | |
| redirect('/login'); | |
| } else { | |
| const currentPath = window.location.pathname + window.location.search; | |
| window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`; | |
| } | |
| } | |
| if (status === 404) { | |
| if (isServer) { | |
| const { notFound } = await import('next/navigation'); | |
| notFound(); | |
| } else { | |
| window.location.href = '/404'; | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/api/core/index.ts around lines 50 to 63, the code calls next/navigation's
notFound() unconditionally which will crash on the client; update the 404
handling to mirror the 401 branch by checking isServer: if isServer, import and
call notFound(); else, compute current path if needed and set
window.location.href to '/404' (or '/404?path=...' if you want to preserve path)
so client-side navigation doesn't call the server-only API.
📝 변경 사항
💡 Axios 응답 인터셉터 로직 수정
Note
Todo:
🔗 관련 이슈
Closes #
🧪 테스트 방법
📸 스크린샷 (선택)
📋 체크리스트
💬 추가 코멘트
CodeRabbit Review는 자동으로 실행되지 않습니다.
Review를 실행하려면 comment에 아래와 같이 작성해주세요
Summary by CodeRabbit
릴리즈 노트
✏️ Tip: You can customize this high-level summary in your review settings.