diff --git a/apps/cm/app/api/proxy/[...url]/route.ts b/apps/cm/app/api/proxy/[...url]/route.ts index 105b36fa7..c0f646cd2 100644 --- a/apps/cm/app/api/proxy/[...url]/route.ts +++ b/apps/cm/app/api/proxy/[...url]/route.ts @@ -19,6 +19,10 @@ async function handler( "Content-Type": "application/json", "X-Site-Id": SITE_ID, }; + const deviceId = request.headers.get("X-Device-Id"); + if (deviceId) { + headers["X-Device-Id"] = deviceId; + } let body = {}; const isRefreshHandler = params.url.includes("refresh"); @@ -57,40 +61,57 @@ async function handler( }); console.log(`[Proxy] Response status: ${result.status}`); - console.log(`[Proxy] Response headers:`, Object.fromEntries(result.headers.entries())); + console.log( + `[Proxy] Response headers:`, + Object.fromEntries(result.headers.entries()), + ); // 2FA 관련 엔드포인트인 경우 응답 본문도 확인 - const is2FAEndpoint = params.url.includes("2fa") || params.url.includes("auth"); + const is2FAEndpoint = + params.url.includes("2fa") || params.url.includes("auth"); // 배너 API는 인증 없이 호출 가능 const isBannerEndpoint = params.url.includes("bnrs"); let responseData = {}; - + if (is2FAEndpoint || isBannerEndpoint) { try { responseData = await result.json(); - console.log(`[Proxy] ${is2FAEndpoint ? '2FA' : 'Banner'} Response data:`, responseData); + console.log( + `[Proxy] ${is2FAEndpoint ? "2FA" : "Banner"} Response data:`, + responseData, + ); } catch { // JSON 파싱 실패 시 빈 객체 사용 console.log(`[Proxy] Failed to parse JSON response`); } } - const accessToken = result.headers.get(ACCESS_TOKEN) || result.headers.get('ss-token'); + const accessToken = + result.headers.get(ACCESS_TOKEN) || result.headers.get("ss-token"); const refreshToken = result.headers.get(REFRESH_TOKEN); const userId = result.headers.get(AUTH_USER_ID); - console.log(`[Proxy] Tokens - accessToken: ${!!accessToken}, userId: ${!!userId}, refreshToken: ${!!refreshToken}`); - console.log(`[Proxy] Available headers:`, Array.from(result.headers.keys())); + console.log( + `[Proxy] Tokens - accessToken: ${!!accessToken}, userId: ${!!userId}, refreshToken: ${!!refreshToken}`, + ); + console.log( + `[Proxy] Available headers:`, + Array.from(result.headers.keys()), + ); // 2FA 또는 배너 엔드포인트이고 실패한 경우 실제 오류 메시지 반환 if (!result.ok && (is2FAEndpoint || isBannerEndpoint)) { - console.log(`[Proxy] ${is2FAEndpoint ? '2FA' : 'Banner'} endpoint failed, returning actual error`); + console.log( + `[Proxy] ${is2FAEndpoint ? "2FA" : "Banner"} endpoint failed, returning actual error`, + ); return NextResponse.json(responseData, { status: result.status }); } // 2FA 또는 배너 엔드포인트이고 성공한 경우 (토큰이 헤더에 없을 수 있음) if (result.ok && (is2FAEndpoint || isBannerEndpoint)) { - console.log(`[Proxy] ${is2FAEndpoint ? '2FA' : 'Banner'} endpoint succeeded, returning response data`); + console.log( + `[Proxy] ${is2FAEndpoint ? "2FA" : "Banner"} endpoint succeeded, returning response data`, + ); return NextResponse.json(responseData, { status: result.status }); } diff --git a/apps/cm/package.json b/apps/cm/package.json index e9c722d39..64eb7edb4 100644 --- a/apps/cm/package.json +++ b/apps/cm/package.json @@ -47,6 +47,7 @@ "sonner": "^2.0.1", "tailwindcss-animate": "^1.0.7", "tus-js-client": "^4.3.1", + "uuid": "^14.0.1", "zod": "^3.24.2", "zustand": "^5.0.3" }, diff --git a/apps/cm/src/entities/auth/lib/auth-wrapper.tsx b/apps/cm/src/entities/auth/lib/auth-wrapper.tsx index d1a2a48c3..87f932817 100644 --- a/apps/cm/src/entities/auth/lib/auth-wrapper.tsx +++ b/apps/cm/src/entities/auth/lib/auth-wrapper.tsx @@ -18,10 +18,9 @@ import { getRemainingMs } from "@common/auth"; import { isAtLoginRedirectUrl } from "@common/network"; import { authService } from "../api/auth-service"; - import { getToken, setupRefreshTimer } from "./token-manager"; -const AuthWrapper = ({ children }: { children: React.ReactNode }) => { +export function AuthWrapper({ children }: { children: React.ReactNode }) { const router = useRouter(); const userInfo = useUserStore((state) => state.userInfo); const setUserInfo = useUserStore((state) => state.setUserInfo); @@ -31,9 +30,6 @@ const AuthWrapper = ({ children }: { children: React.ReactNode }) => { if (token) { const remainingMs = getRemainingMs(token); if (remainingMs > 0) { - console.log( - `[AuthWrapper] Refresh timer set: ${remainingMs}ms (${remainingMs / 1000}s)`, - ); setupRefreshTimer(authService.silentRefresh, remainingMs); } } @@ -79,6 +75,4 @@ const AuthWrapper = ({ children }: { children: React.ReactNode }) => { }, []); return <>{children}; -}; - -export { AuthWrapper }; +} diff --git a/apps/cm/src/entities/auth/lib/device-id.ts b/apps/cm/src/entities/auth/lib/device-id.ts new file mode 100644 index 000000000..3683c7189 --- /dev/null +++ b/apps/cm/src/entities/auth/lib/device-id.ts @@ -0,0 +1,20 @@ +import { v4 as uuidv4 } from "uuid"; + +const DEVICE_ID_KEY = "x-device-id"; + +export function getOrCreateDeviceId(): string { + if (typeof window === "undefined") return ""; + + const existing = localStorage.getItem(DEVICE_ID_KEY); + if (existing) return existing; + + const id = uuidv4(); + localStorage.setItem(DEVICE_ID_KEY, id); + return id; +} + +export function clearDeviceId(): void { + if (typeof window === "undefined") return; + + localStorage.removeItem(DEVICE_ID_KEY); +} diff --git a/apps/cm/src/pages/auth/login/ui/UserSignupModal.tsx b/apps/cm/src/pages/auth/login/ui/UserSignupModal.tsx index 8faf518f1..e4a542cfa 100644 --- a/apps/cm/src/pages/auth/login/ui/UserSignupModal.tsx +++ b/apps/cm/src/pages/auth/login/ui/UserSignupModal.tsx @@ -882,11 +882,6 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { type="submit" className="user-signup-submit-button" disabled={isLoading} - onClick={(e) => { - e.preventDefault(); - console.log("회원가입 버튼 클릭됨 - onClick"); - handleSubmit(e); - }} > {isLoading ? "가입 중..." : "회원가입"} diff --git a/apps/cm/src/shared/config/index.ts b/apps/cm/src/shared/config/index.ts index cd83447d5..ea6a6f16a 100644 --- a/apps/cm/src/shared/config/index.ts +++ b/apps/cm/src/shared/config/index.ts @@ -1,2 +1,3 @@ export * from "./api"; +export * from "./auth"; export * from "./env";