-
Notifications
You must be signed in to change notification settings - Fork 1
Fix: #78 리다이렉트 로직 수정 #79
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,86 @@ | ||||||||||
| import { clearAuthTokens, getAuthHeaders, API_BASE_URL } from "@/lib/auth"; | ||||||||||
| import { ROUTES } from "@/constants/routes"; | ||||||||||
|
|
||||||||||
| export { API_BASE_URL, getAuthHeaders }; | ||||||||||
|
|
||||||||||
| export interface ApiResponse<T> { | ||||||||||
| isSuccess: boolean; | ||||||||||
| code: string; | ||||||||||
| message: string; | ||||||||||
| result: T | null; | ||||||||||
| error: string | null; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export class UnauthorizedError extends Error { | ||||||||||
| constructor() { | ||||||||||
| super("인증이 만료되었습니다. 다시 로그인해주세요."); | ||||||||||
| this.name = "UnauthorizedError"; | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export function handleUnauthorized(): never { | ||||||||||
| clearAuthTokens(); | ||||||||||
|
|
||||||||||
| if (typeof window !== "undefined") { | ||||||||||
| const redirectPath = encodeURIComponent(window.location.pathname); | ||||||||||
| window.location.replace(`${ROUTES.LOGIN}?redirect=${redirectPath}`); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| throw new UnauthorizedError(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export async function parseApiResponse<T>( | ||||||||||
| response: Response, | ||||||||||
| fallbackMessage: string, | ||||||||||
| ): Promise<T> { | ||||||||||
| if (response.status === 401) { | ||||||||||
| handleUnauthorized(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| let data: ApiResponse<T> | null = null; | ||||||||||
|
|
||||||||||
| try { | ||||||||||
| data = (await response.json()) as ApiResponse<T>; | ||||||||||
| } catch { | ||||||||||
| throw new Error(`${fallbackMessage} 응답을 확인할 수 없습니다.`); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (!response.ok || !data.isSuccess || !data.result) { | ||||||||||
| throw new Error(data?.error || data?.message || fallbackMessage); | ||||||||||
|
Comment on lines
+48
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Two concerns on the validation check:
🔧 Proposed fix- if (!response.ok || !data.isSuccess || !data.result) {
+ if (!response.ok || !data?.isSuccess || data.result == null) {
throw new Error(data?.error || data?.message || fallbackMessage);
}🤖 Prompt for AI Agents |
||||||||||
| } | ||||||||||
|
|
||||||||||
| return data.result; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| export async function parseApiResponseAllowNull<T>( | ||||||||||
| response: Response, | ||||||||||
| fallbackMessage: string, | ||||||||||
| ): Promise<T | null> { | ||||||||||
| if (response.status === 401) { | ||||||||||
| handleUnauthorized(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| const responseText = await response.text(); | ||||||||||
|
|
||||||||||
| if (!responseText.trim()) { | ||||||||||
| if (!response.ok) { | ||||||||||
| throw new Error(fallbackMessage); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return null; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| let data: ApiResponse<T> | null = null; | ||||||||||
|
|
||||||||||
| try { | ||||||||||
| data = JSON.parse(responseText) as ApiResponse<T>; | ||||||||||
| } catch { | ||||||||||
| throw new Error(`${fallbackMessage} 응답을 확인할 수 없습니다.`); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (!response.ok || !data.isSuccess) { | ||||||||||
| throw new Error(data?.error || data?.message || fallbackMessage); | ||||||||||
|
Comment on lines
+81
to
+82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same potential null dereference as
🔧 Proposed fix- if (!response.ok || !data.isSuccess) {
+ if (!response.ok || !data?.isSuccess) {
throw new Error(data?.error || data?.message || fallbackMessage);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| } | ||||||||||
|
|
||||||||||
| return data.result; | ||||||||||
| } | ||||||||||
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.
Preserve query string and hash in the redirect path.
window.location.pathnamedrops the current?queryand#hash. Since the goal is to return the user to the exact page after login, pages that rely on query params will lose state on redirect-back.🔧 Proposed fix
if (typeof window !== "undefined") { - const redirectPath = encodeURIComponent(window.location.pathname); + const redirectPath = encodeURIComponent( + window.location.pathname + window.location.search + window.location.hash, + ); window.location.replace(`${ROUTES.LOGIN}?redirect=${redirectPath}`); }📝 Committable suggestion
🤖 Prompt for AI Agents