diff --git a/next.config.ts b/next.config.ts index b83282b2..4fa6e946 100644 --- a/next.config.ts +++ b/next.config.ts @@ -4,6 +4,7 @@ import createNextIntlPlugin from 'next-intl/plugin'; const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts'); const nextConfig: NextConfig = { + reactStrictMode: true, // ๐Ÿงช TEST: Strict Mode ๋น„ํ™œ์„ฑํ™”๋กœ ์ค‘๋ณต ์š”์ฒญ ํ…Œ์ŠคํŠธ turbopack: {}, // โœ… CRITICAL: Next.js 15 + next-intl compatibility typescript: { // โš ๏ธ WARNING: This allows production builds to complete even with TypeScript errors diff --git a/src/components/auth/LoginPage.tsx b/src/components/auth/LoginPage.tsx index 1384b618..18604814 100644 --- a/src/components/auth/LoginPage.tsx +++ b/src/components/auth/LoginPage.tsx @@ -28,6 +28,7 @@ export function LoginPage() { const [rememberMe, setRememberMe] = useState(false); const [error, setError] = useState(""); const [isChecking, setIsChecking] = useState(true); + const [isLoggingIn, setIsLoggingIn] = useState(false); // โœ… ๋กœ๊ทธ์ธ ์ง„ํ–‰ ์ค‘ ์ƒํƒœ // ์ด๋ฏธ ๋กœ๊ทธ์ธ๋œ ์ƒํƒœ์ธ์ง€ ํ™•์ธ (ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ, ๋’ค๋กœ๊ฐ€๊ธฐ ์‹œ) useEffect(() => { @@ -53,6 +54,12 @@ export function LoginPage() { }, [router]); const handleLogin = async () => { + // โœ… ์ค‘๋ณต ์š”์ฒญ ๋ฐฉ์ง€ + if (isLoggingIn) { + console.warn('โš ๏ธ ๋กœ๊ทธ์ธ ์ง„ํ–‰ ์ค‘ - ์ค‘๋ณต ์š”์ฒญ ์ฐจ๋‹จ'); + return; + } + setError(""); // Validation @@ -61,6 +68,8 @@ export function LoginPage() { return; } + setIsLoggingIn(true); // โœ… ๋กœ๊ทธ์ธ ์‹œ์ž‘ + try { // ๐Ÿ”ต Next.js ํ”„๋ก์‹œ โ†’ PHP /api/v1/login (ํ† ํฐ์„ HttpOnly ์ฟ ํ‚ค๋กœ ์ €์žฅ) const response = await fetch('/api/auth/login', { @@ -122,6 +131,8 @@ export function LoginPage() { } else { setError(error.message || t('invalidCredentials')); } + } finally { + setIsLoggingIn(false); // โœ… ๋กœ๊ทธ์ธ ์ข…๋ฃŒ (์„ฑ๊ณต/์‹คํŒจ ์ƒ๊ด€์—†์ด) } }; @@ -249,10 +260,20 @@ export function LoginPage() {