diff --git a/src/components/supabase/SupabaseSettingsForm.tsx b/src/components/supabase/SupabaseSettingsForm.tsx index 50bf3f3..aed8016 100644 --- a/src/components/supabase/SupabaseSettingsForm.tsx +++ b/src/components/supabase/SupabaseSettingsForm.tsx @@ -23,8 +23,8 @@ const SupabaseSettingsForm: React.FC = ({ onSaved }) const [formState, setFormState] = useState({ supabaseUrl: '', supabaseKey: '', - useProxy: false, - proxyType: 'corsproxy.io', + useProxy: true, // 기본값을 true로 변경 + proxyType: 'cloudflare', // 기본값을 cloudflare로 변경 isSaving: false }); @@ -37,15 +37,15 @@ const SupabaseSettingsForm: React.FC = ({ onSaved }) useEffect(() => { const savedUrl = localStorage.getItem('supabase_url'); const savedKey = localStorage.getItem('supabase_key'); - const proxyEnabled = isCorsProxyEnabled(); + const proxyEnabled = localStorage.getItem('use_cors_proxy') === 'true'; const savedProxyType = getProxyType(); setFormState(prev => ({ ...prev, supabaseUrl: savedUrl || '', supabaseKey: savedKey || '', - useProxy: proxyEnabled, - proxyType: savedProxyType + useProxy: proxyEnabled === false ? false : true, // 저장된 값이 명시적으로 false인 경우에만 false, 아니면 기본값 true + proxyType: savedProxyType || 'cloudflare' })); }, []); diff --git a/src/contexts/auth/signInUtils.ts b/src/contexts/auth/signInUtils.ts index 6965f95..688b661 100644 --- a/src/contexts/auth/signInUtils.ts +++ b/src/contexts/auth/signInUtils.ts @@ -1,7 +1,7 @@ import { supabase } from '@/lib/supabase'; import { parseResponse, showAuthToast, handleNetworkError } from '@/utils/auth'; -import { getProxyType, isCorsProxyEnabled } from '@/lib/supabase/config'; +import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config'; /** * 직접 API 호출을 통한 로그인 시도 (대체 방법) @@ -11,23 +11,27 @@ export const signInWithDirectApi = async (email: string, password: string) => { try { // API 호출 URL 및 헤더 설정 - const supabaseUrl = localStorage.getItem('supabase_url') || 'https://a11.ism.kr'; + const supabaseUrl = getOriginalSupabaseUrl(); // 원본 URL 사용 + const proxyUrl = getSupabaseUrl(); // 프록시 적용된 URL const supabaseKey = localStorage.getItem('supabase_key') || supabase.supabaseKey; // 프록시 정보 로그 const usingProxy = isCorsProxyEnabled(); const proxyType = getProxyType(); - console.log(`CORS 프록시 사용: ${usingProxy ? '예' : '아니오'}, 타입: ${proxyType}`); + console.log(`CORS 프록시 사용: ${usingProxy ? '예' : '아니오'}, 타입: ${proxyType}, 프록시 URL: ${proxyUrl}`); + + // 실제 요청에 사용할 URL 결정 (항상 프록시 URL 사용) + const useUrl = usingProxy ? proxyUrl : supabaseUrl; // URL에 auth/v1이 이미 포함되어있는지 확인 - const baseUrl = supabaseUrl.includes('/auth/v1') ? supabaseUrl : `${supabaseUrl}/auth/v1`; + const baseUrl = useUrl.includes('/auth/v1') ? useUrl : `${useUrl}/auth/v1`; - // 토큰 엔드포인트 경로 (curl 테스트와 동일한 형식으로) + // 토큰 엔드포인트 경로 const tokenUrl = `${baseUrl}/token?grant_type=password`; console.log('로그인 API 요청 URL:', tokenUrl); - // 로그인 요청 보내기 (curl 테스트와 동일한 형식으로) + // 로그인 요청 보내기 const response = await fetch(tokenUrl, { method: 'POST', headers: { diff --git a/src/contexts/auth/signUpUtils.ts b/src/contexts/auth/signUpUtils.ts index 59f4e31..d7751b1 100644 --- a/src/contexts/auth/signUpUtils.ts +++ b/src/contexts/auth/signUpUtils.ts @@ -1,6 +1,7 @@ import { supabase } from '@/lib/supabase'; import { parseResponse, showAuthToast } from '@/utils/auth'; +import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config'; /** * 직접 API 호출을 통한 회원가입 @@ -9,11 +10,21 @@ export const signUpWithDirectApi = async (email: string, password: string, usern try { console.log('직접 API 호출로 회원가입 시도 중'); - const supabaseUrl = localStorage.getItem('supabase_url') || 'https://a11.ism.kr'; + // 프록시 적용된 URL과 원본 URL 모두 가져오기 + const supabaseUrl = getOriginalSupabaseUrl(); // 원본 URL + const proxyUrl = getSupabaseUrl(); // 프록시 적용된 URL const supabaseKey = localStorage.getItem('supabase_key') || supabase.supabaseKey; + // 프록시 정보 로깅 + const usingProxy = isCorsProxyEnabled(); + const proxyType = getProxyType(); + console.log(`CORS 프록시 사용: ${usingProxy ? '예' : '아니오'}, 타입: ${proxyType}, 프록시 URL: ${proxyUrl}`); + + // 실제 요청에 사용할 URL 결정 (항상 프록시 URL 사용) + const useUrl = usingProxy ? proxyUrl : supabaseUrl; + // URL에 auth/v1이 이미 포함되어있는지 확인 - const baseUrl = supabaseUrl.includes('/auth/v1') ? supabaseUrl : `${supabaseUrl}/auth/v1`; + const baseUrl = useUrl.includes('/auth/v1') ? useUrl : `${useUrl}/auth/v1`; // 회원가입 API 엔드포인트 및 헤더 설정 const signUpUrl = `${baseUrl}/signup`; @@ -125,7 +136,23 @@ export const signUpWithDirectApi = async (email: string, password: string, usern } catch (error: any) { console.error('회원가입 API 호출 중 예외 발생:', error); - const errorMessage = error.message || '알 수 없는 오류가 발생했습니다.'; + // 프록시 설정 확인 + const usingProxy = isCorsProxyEnabled(); + const proxyType = getProxyType(); + + // 오류 메시지 설정 및 프록시 추천 + let errorMessage = error.message || '알 수 없는 오류가 발생했습니다.'; + + if (errorMessage.includes('Failed to fetch') || + errorMessage.includes('NetworkError') || + errorMessage.includes('CORS')) { + if (!usingProxy) { + errorMessage += ' (설정에서 Cloudflare CORS 프록시 활성화를 권장합니다)'; + } else if (proxyType !== 'cloudflare') { + errorMessage += ' (설정에서 Cloudflare CORS 프록시로 변경을 권장합니다)'; + } + } + showAuthToast('회원가입 오류', errorMessage, 'destructive'); return { error: { message: errorMessage }, user: null }; diff --git a/src/lib/supabase/config.ts b/src/lib/supabase/config.ts index 11945f1..f60d468 100644 --- a/src/lib/supabase/config.ts +++ b/src/lib/supabase/config.ts @@ -54,6 +54,12 @@ export const getSupabaseUrl = () => { return 'https://a11.ism.kr'; }; +// 원본 URL 반환 (프록시 없는 URL) +export const getOriginalSupabaseUrl = () => { + const storedUrl = localStorage.getItem('supabase_url'); + return storedUrl || 'https://a11.ism.kr'; +}; + export const getSupabaseKey = () => { // 로컬 스토리지에서 설정된 키를 우선 사용 const storedKey = localStorage.getItem('supabase_key'); @@ -84,7 +90,7 @@ export const isCorsProxyEnabled = () => { }; // 온프레미스 연결을 위한 설정 도우미 함수 -export const configureSupabase = (url: string, key: string, useProxy: boolean = false, proxyType: string = 'corsproxy.io') => { +export const configureSupabase = (url: string, key: string, useProxy: boolean = true, proxyType: string = 'cloudflare') => { // URL 정리 (앞뒤 공백 제거) const cleanUrl = url.trim(); @@ -108,9 +114,3 @@ export const configureSupabase = (url: string, key: string, useProxy: boolean = // 페이지 새로고침 - 새로운 설정으로 Supabase 클라이언트 초기화 window.location.reload(); }; - -// 원본 URL 반환 (프록시 없는 URL) -export const getOriginalSupabaseUrl = () => { - const storedUrl = localStorage.getItem('supabase_url'); - return storedUrl || 'https://a11.ism.kr'; -}; diff --git a/src/utils/auth/networkUtils.ts b/src/utils/auth/networkUtils.ts index f0fd3ba..ec42a82 100644 --- a/src/utils/auth/networkUtils.ts +++ b/src/utils/auth/networkUtils.ts @@ -1,6 +1,6 @@ import { supabase } from '@/lib/supabase'; -import { getProxyType, isCorsProxyEnabled } from '@/lib/supabase/config'; +import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config'; /** * CORS 문제 확인 @@ -28,8 +28,8 @@ export const verifyServerConnection = async (): Promise<{ try { const start = Date.now(); - // Supabase URL 가져오기 - const supabaseUrl = supabase.auth.url; + // Supabase URL 가져오기 (프록시 적용 URL) + const supabaseUrl = getSupabaseUrl(); if (!supabaseUrl) { return { @@ -41,7 +41,7 @@ export const verifyServerConnection = async (): Promise<{ // 프록시 설정 상태 확인 const usingProxy = isCorsProxyEnabled(); const proxyType = getProxyType(); - console.log(`연결 테스트 - CORS 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}`); + console.log(`연결 테스트 - CORS 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}, URL: ${supabaseUrl}`); // 단순 헬스 체크 요청 try { @@ -51,7 +51,7 @@ export const verifyServerConnection = async (): Promise<{ 'Content-Type': 'application/json', 'apikey': localStorage.getItem('supabase_key') || '' }, - signal: AbortSignal.timeout(5000) // 타임아웃 시간 증가 + signal: AbortSignal.timeout(8000) // 타임아웃 시간 증가 }); const elapsed = Date.now() - start; @@ -73,6 +73,15 @@ export const verifyServerConnection = async (): Promise<{ } catch (fetchError: any) { console.error('기본 연결 확인 실패, 상태 확인 시도:', fetchError); + // HTTP URL을 사용하는데 프록시가 비활성화된 경우 + const originalUrl = getOriginalSupabaseUrl(); + if (originalUrl.startsWith('http:') && !usingProxy) { + return { + connected: false, + message: 'HTTP URL에 직접 접근할 수 없습니다. CORS 프록시를 활성화하세요.' + }; + } + try { // 대체 경로로 상태 확인 const altResponse = await fetch(`${supabaseUrl}/`, { @@ -80,7 +89,7 @@ export const verifyServerConnection = async (): Promise<{ headers: { 'Content-Type': 'application/json' }, - signal: AbortSignal.timeout(5000) + signal: AbortSignal.timeout(8000) }); // 어떤 응답이라도 오면 서버가 살아있다고 간주 @@ -121,12 +130,20 @@ export const verifyServerConnection = async (): Promise<{ } } + // HTTP URL을 사용하는데 프록시가 비활성화된 경우 + const originalUrl = getOriginalSupabaseUrl(); + if (originalUrl.startsWith('http:') && !usingProxy) { + errorMessage = 'HTTP URL에 직접 접근할 수 없습니다. CORS 프록시를 활성화하세요.'; + } + // Cloudflare 프록시 추천 메시지 추가 if (errorMessage.includes('CORS') || errorMessage.includes('fetch') || errorMessage.includes('네트워크')) { if (!usingProxy) { console.log('CORS 오류 감지, Cloudflare 프록시 사용 권장'); + errorMessage += '. Cloudflare CORS 프록시 사용을 권장합니다.'; } else if (proxyType !== 'cloudflare') { console.log('CORS 오류 감지, Cloudflare 프록시로 변경 권장'); + errorMessage += '. Cloudflare CORS 프록시로 변경을 권장합니다.'; } } @@ -146,7 +163,7 @@ export const verifySupabaseConnection = async (): Promise<{ statusCode?: number; details?: string; }> => { - const supabaseUrl = localStorage.getItem('supabase_url'); + const supabaseUrl = getSupabaseUrl(); if (!supabaseUrl) { return { connected: false, @@ -154,6 +171,11 @@ export const verifySupabaseConnection = async (): Promise<{ }; } + // 프록시 정보 로깅 + const usingProxy = isCorsProxyEnabled(); + const proxyType = getProxyType(); + console.log(`강화된 연결 테스트 - 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}, URL: ${supabaseUrl}`); + // 무작위 쿼리 파라미터를 추가하여 캐시 방지 const cacheParam = `?_nocache=${Date.now()}`; @@ -167,14 +189,18 @@ export const verifySupabaseConnection = async (): Promise<{ for (const path of paths) { try { + console.log(`경로 시도: ${path}`); const response = await fetch(`${supabaseUrl}${path}${cacheParam}`, { method: 'GET', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'apikey': localStorage.getItem('supabase_key') || '' }, - signal: AbortSignal.timeout(5000) + signal: AbortSignal.timeout(8000) }); + console.log(`경로 ${path} 응답 상태:`, response.status); + // 어떤 응답이든 서버가 살아있다는 신호로 간주 return { connected: true, @@ -188,6 +214,16 @@ export const verifySupabaseConnection = async (): Promise<{ } } + // HTTP URL을 사용하는데 프록시가 비활성화된 경우 + const originalUrl = getOriginalSupabaseUrl(); + if (originalUrl.startsWith('http:') && !usingProxy) { + return { + connected: false, + message: 'HTTP URL에 직접 접근할 수 없습니다. CORS 프록시를 활성화하세요.', + details: 'CORS 제한으로 인해 HTTP URL에 직접 접근할 수 없습니다' + }; + } + // 모든 경로 시도 실패 return { connected: false,