diff --git a/src/contexts/auth/auth.utils.ts b/src/contexts/auth/auth.utils.ts index fdaf3cf..71be197 100644 --- a/src/contexts/auth/auth.utils.ts +++ b/src/contexts/auth/auth.utils.ts @@ -9,9 +9,15 @@ export const showAuthToast = (title: string, description: string, variant: 'defa // 에러 메시지 처리 유틸리티 함수 export const handleNetworkError = (error: any): string => { - return error.message && error.message.includes('fetch') - ? '서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.' - : (error.message || '예상치 못한 오류가 발생했습니다.'); + if (error.message && error.message.includes('fetch')) { + return '서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.'; + } else if (error.message && error.message.includes('CORS')) { + return 'CORS 오류가 발생했습니다. 서버 설정 또는 CORS 프록시를 확인하세요.'; + } else if (error.message && error.message.includes('NetworkError')) { + return '네트워크 오류가 발생했습니다. 인터넷 연결을 확인하세요.'; + } + + return error.message || '예상치 못한 오류가 발생했습니다.'; }; // 응답 파싱 유틸리티 함수 @@ -60,23 +66,98 @@ export const hasCorsIssue = (error: any): boolean => { ); }; -// 오프라인 모드 확인 -export const isOfflineMode = (): boolean => { - return !navigator.onLine || localStorage.getItem('offline_mode') === 'true'; +// Supabase 연결 상태 확인 +export const checkSupabaseConnection = async (): Promise => { + try { + const { data, error } = await supabase.auth.getSession(); + + // 오류가 없으면 연결 성공 + if (!error) { + return true; + } + + // 오류 메시지 확인 + if (error.message && ( + error.message.includes('Failed to fetch') || + error.message.includes('Network') || + error.message.includes('CORS') + )) { + return false; + } + + // 다른 오류는 연결은 됐지만 세션/인증 관련 오류이므로 연결은 성공 + return true; + } catch (err) { + console.error('Supabase 연결 확인 중 오류:', err); + return false; + } }; -// 데모 모드 사용자 생성 (오프라인 환경용) -export const createDemoUser = (email: string, username: string) => { - // 데모용 가상 사용자 정보 - const demoUser = { - id: 'demo-' + Date.now(), - email, - user_metadata: { username }, - created_at: new Date().toISOString() - }; - - // 로컬 스토리지에 저장 - localStorage.setItem('demo_user', JSON.stringify(demoUser)); - - return demoUser; +// 서버 연결 상태 검사 +export const verifyServerConnection = async (): Promise<{ + connected: boolean; + message: string; +}> => { + try { + const start = Date.now(); + + // Supabase URL 가져오기 + const supabaseUrl = localStorage.getItem('supabase_url') || 'http://a11.ism.kr:8000'; + + // 단순 헬스 체크 요청 + const response = await fetch(`${supabaseUrl}/rest/v1/`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }); + + const elapsed = Date.now() - start; + + if (response.ok || response.status === 401) { + // 401도 서버가 응답했다는 의미이므로 연결 성공으로 간주 + return { + connected: true, + message: `서버 연결 성공 (응답 시간: ${elapsed}ms)` + }; + } else { + return { + connected: false, + message: `서버 응답 오류: ${response.status} ${response.statusText}` + }; + } + } catch (error: any) { + console.error('서버 연결 확인 중 오류:', error); + + // 오류 유형에 따른 메시지 설정 + let errorMessage = '알 수 없는 네트워크 오류'; + + if (error.message) { + if (error.message.includes('Failed to fetch')) { + errorMessage = 'CORS 정책 오류 또는 서버 연결 실패'; + } else if (error.message.includes('NetworkError')) { + errorMessage = '네트워크 연결 실패'; + } else if (error.message.includes('TypeError')) { + errorMessage = '네트워크 요청 형식 오류'; + } else { + errorMessage = error.message; + } + } + + return { + connected: false, + message: errorMessage + }; + } +}; + +// 서버 URL 검증 +export const validateServerUrl = (url: string): boolean => { + try { + // URL 유효성 검사 + new URL(url); + return true; + } catch (e) { + return false; + } }; diff --git a/src/contexts/auth/signIn.ts b/src/contexts/auth/signIn.ts index c9a32e7..579acc9 100644 --- a/src/contexts/auth/signIn.ts +++ b/src/contexts/auth/signIn.ts @@ -1,18 +1,17 @@ import { supabase } from '@/lib/supabase'; -import { handleNetworkError, parseResponse, showAuthToast, isOfflineMode, createDemoUser } from './auth.utils'; +import { handleNetworkError, parseResponse, showAuthToast, verifyServerConnection } from './auth.utils'; export const signIn = async (email: string, password: string) => { try { - // 오프라인 모드 확인 - if (isOfflineMode()) { - console.log('오프라인 모드로 로그인 시도:', email); - - // 데모 사용자 생성 (데모 모드용) - const demoUser = createDemoUser(email, email.split('@')[0]); - - showAuthToast('로그인 성공', '오프라인 모드로 로그인되었습니다.'); - return { error: null, user: demoUser }; + // 서버 연결 상태 먼저 확인 + const connectionStatus = await verifyServerConnection(); + if (!connectionStatus.connected) { + showAuthToast('서버 연결 실패', connectionStatus.message, 'destructive'); + return { + error: { message: `서버 연결에 실패했습니다: ${connectionStatus.message}` }, + user: null + }; } console.log('로그인 시도 중:', email); diff --git a/src/contexts/auth/signUp.ts b/src/contexts/auth/signUp.ts index c3f44c2..0c2f880 100644 --- a/src/contexts/auth/signUp.ts +++ b/src/contexts/auth/signUp.ts @@ -1,23 +1,12 @@ import { supabase } from '@/lib/supabase'; -import { handleNetworkError, showAuthToast, isOfflineMode, createDemoUser } from './auth.utils'; +import { handleNetworkError, showAuthToast } from './auth.utils'; export const signUp = async (email: string, password: string, username: string) => { try { - // 오프라인 모드 확인 - if (isOfflineMode()) { - console.log('오프라인 모드로 회원가입 시도:', { email, username }); - - // 데모 사용자 생성 - const demoUser = createDemoUser(email, username); - - showAuthToast('회원가입 성공', '오프라인 모드로 회원가입되었습니다.'); - return { error: null, user: demoUser }; - } - console.log('회원가입 시도:', { email, username }); - // 기본 회원가입 시도 + // 회원가입 시도 const { data, error } = await supabase.auth.signUp({ email, password, @@ -44,14 +33,17 @@ export const signUp = async (email: string, password: string, username: string) errorMessage = '서버 연결 문제: 이메일 검증에 실패했습니다.'; } else if (error.message.includes('json')) { errorMessage = '서버 응답 처리 오류: 네트워크 연결을 확인하세요.'; + } else if (error.message.includes('fetch failed')) { + errorMessage = '서버 연결에 실패했습니다. 네트워크 연결 또는 서버 상태를 확인하세요.'; + } else if (error.message.includes('Failed to fetch')) { + errorMessage = 'CORS 오류가 발생했습니다. 서버 설정을 확인하거나 네트워크 관리자에게 문의하세요.'; } showAuthToast('회원가입 실패', errorMessage, 'destructive'); return { error, user: null }; } - // 즉시 로그인이 필요한 경우 여기서 처리 - // 기본적으로는 이메일 확인 요청 + // 성공 메시지 표시 showAuthToast('회원가입 성공', '이메일 확인 후 로그인해주세요.'); // 개발 환경 또는 데모 모드에서는 즉시 로그인 허용 diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx index 2119254..7261f4f 100644 --- a/src/pages/Register.tsx +++ b/src/pages/Register.tsx @@ -1,3 +1,4 @@ + import React, { useState, useEffect } from "react"; import { Link, useNavigate } from "react-router-dom"; import { Label } from "@/components/ui/label"; @@ -6,6 +7,8 @@ import { Button } from "@/components/ui/button"; import { ArrowRight, Mail, KeyRound, User, Eye, EyeOff } from "lucide-react"; import { useToast } from "@/hooks/useToast.wrapper"; import { useAuth } from "@/contexts/auth"; +import TestConnectionSection from "@/components/auth/TestConnectionSection"; +import SupabaseConnectionStatus from "@/components/auth/SupabaseConnectionStatus"; const Register = () => { const [username, setUsername] = useState(""); @@ -14,6 +17,8 @@ const Register = () => { const [confirmPassword, setConfirmPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [registerError, setRegisterError] = useState(null); + const [testResults, setTestResults] = useState(null); const { toast } = useToast(); const navigate = useNavigate(); const { signUp, user } = useAuth(); @@ -27,6 +32,7 @@ const Register = () => { const handleRegister = async (e: React.FormEvent) => { e.preventDefault(); + setRegisterError(null); if (!username || !email || !password || !confirmPassword) { toast({ @@ -51,12 +57,15 @@ const Register = () => { try { const { error, user } = await signUp(email, password, username); - if (!error && user) { - // 데모 모드에서는 즉시 메인 페이지로 이동 - navigate("/"); + if (error) { + setRegisterError(error.message || '알 수 없는 오류가 발생했습니다.'); + } else if (user) { + // 회원가입 성공 시 로그인 페이지로 이동 + navigate("/login"); } - } catch (error) { + } catch (error: any) { console.error("회원가입 처리 중 예외 발생:", error); + setRegisterError(error.message || '예상치 못한 오류가 발생했습니다.'); } finally { setIsLoading(false); } @@ -140,6 +149,12 @@ const Register = () => { + {registerError && ( +
+

{registerError}

+
+ )} +