Refactor networkUtils.ts

The file src/utils/auth/networkUtils.ts was too long, so it has been refactored into smaller files.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-16 10:06:21 +00:00
parent 74e7ea19fd
commit 776d0442aa
6 changed files with 278 additions and 246 deletions

View File

@@ -5,3 +5,6 @@ export * from './networkUtils';
export * from './responseUtils';
export * from './validationUtils';
export * from './handleNetworkError';
// 새로운 네트워크 모듈도 직접 내보냅니다 (선택적)
export * from './network';

View File

@@ -0,0 +1,138 @@
import { getSupabaseUrl, isCorsProxyEnabled, getProxyType, getOriginalSupabaseUrl } from '@/lib/supabase/config';
import { logProxyInfo, handleHttpUrlWithoutProxy } from './corsUtils';
/**
* 서버 연결 상태 검사
*/
export const verifyServerConnection = async (): Promise<{
connected: boolean;
message: string;
statusCode?: number;
}> => {
try {
const start = Date.now();
// Supabase URL 가져오기 (프록시 적용 URL)
const supabaseUrl = getSupabaseUrl();
if (!supabaseUrl) {
return {
connected: false,
message: 'Supabase URL이 설정되지 않았습니다. 설정 페이지에서 구성하세요.'
};
}
// 프록시 설정 상태 확인
logProxyInfo();
// 단순 헬스 체크 요청 - 무작위 쿼리 파라미터 추가
const cacheParam = `?_nocache=${Date.now()}`;
try {
const response = await fetch(`${supabaseUrl}/auth/v1/${cacheParam}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'apikey': localStorage.getItem('supabase_key') || ''
},
signal: AbortSignal.timeout(15000) // 타임아웃 시간 증가
});
const elapsed = Date.now() - start;
// 200, 401, 404 응답도 서버가 살아있다는 신호로 간주
if (response.ok || response.status === 401 || response.status === 404) {
return {
connected: true,
message: `서버 연결 성공 (응답 시간: ${elapsed}ms)`,
statusCode: response.status
};
} else {
return {
connected: false,
message: `서버 응답 오류: ${response.status} ${response.statusText}`,
statusCode: response.status
};
}
} catch (fetchError: any) {
console.error('기본 연결 확인 실패, 상태 확인 시도:', fetchError);
// HTTP URL을 사용하는데 프록시가 비활성화된 경우 처리
if (handleHttpUrlWithoutProxy()) {
return {
connected: false,
message: 'HTTP URL에 직접 접근할 수 없어 CORS 프록시를 자동으로 활성화했습니다. 페이지를 새로고침하고 다시 시도하세요.'
};
}
try {
// 대체 경로로 상태 확인 - 무작위 쿼리 파라미터 추가
const altResponse = await fetch(`${supabaseUrl}/${cacheParam}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
signal: AbortSignal.timeout(15000) // 타임아웃 시간 증가
});
// 어떤 응답이라도 오면 서버가 살아있다고 간주
const elapsed = Date.now() - start;
return {
connected: true,
message: `서버 연결 성공 (기본 경로, 응답 시간: ${elapsed}ms)`,
statusCode: altResponse.status
};
} catch (altError) {
console.error('기본 경로 확인도 실패:', altError);
throw fetchError; // 원래 에러를 던짐
}
}
} catch (error: any) {
console.error('서버 연결 확인 중 오류:', error);
// 프록시 설정 확인
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
// 오류 유형에 따른 메시지 설정
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 if (error.message.includes('timeout') || error.message.includes('timed out')) {
errorMessage = '서버 응답 시간 초과';
} else if (error.message.includes('aborted')) {
errorMessage = '요청이 중단됨';
} else {
errorMessage = error.message;
}
}
// HTTP URL을 사용하는데 프록시가 비활성화된 경우 처리
if (handleHttpUrlWithoutProxy()) {
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 프록시로 변경을 권장합니다.';
}
}
return {
connected: false,
message: errorMessage
};
}
};

View File

@@ -0,0 +1,45 @@
import { isCorsProxyEnabled, getProxyType, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config';
/**
* CORS 문제 확인
*/
export const hasCorsIssue = (error: any): boolean => {
if (!error) return false;
const errorMessage = error.message || '';
return (
errorMessage.includes('Failed to fetch') ||
errorMessage.includes('CORS') ||
errorMessage.includes('Network') ||
errorMessage.includes('프록시')
);
};
/**
* HTTP URL이 프록시 없이 사용되고 있는지 확인하고 처리
*/
export const handleHttpUrlWithoutProxy = (): boolean => {
// HTTP URL을 사용하는데 프록시가 비활성화된 경우
const originalUrl = getOriginalSupabaseUrl();
const usingProxy = isCorsProxyEnabled();
if (originalUrl.startsWith('http:') && !usingProxy) {
// 자동으로 프록시 활성화
localStorage.setItem('use_cors_proxy', 'true');
localStorage.setItem('proxy_type', 'cloudflare');
return true;
}
return false;
};
/**
* 프록시 정보 로깅
*/
export const logProxyInfo = (): void => {
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
const supabaseUrl = getSupabaseUrl();
console.log(`연결 테스트 - CORS 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}, URL: ${supabaseUrl}`);
};

View File

@@ -0,0 +1,80 @@
import { getSupabaseUrl, isCorsProxyEnabled, getProxyType, getOriginalSupabaseUrl } from '@/lib/supabase/config';
import { logProxyInfo, handleHttpUrlWithoutProxy } from './corsUtils';
/**
* 강화된 서버 연결 검사: 다양한 경로로 시도
*/
export const verifySupabaseConnection = async (): Promise<{
connected: boolean;
message: string;
statusCode?: number;
details?: string;
}> => {
const supabaseUrl = getSupabaseUrl();
if (!supabaseUrl) {
return {
connected: false,
message: 'Supabase URL이 설정되지 않았습니다'
};
}
// 프록시 정보 로깅
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
console.log(`강화된 연결 테스트 - 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}, URL: ${supabaseUrl}`);
// 무작위 쿼리 파라미터를 추가하여 캐시 방지
const cacheParam = `?_nocache=${Date.now()}`;
// 다양한 경로를 순차적으로 시도
const paths = [
'/auth/v1/',
'/',
'/rest/v1/',
'/storage/v1/'
];
for (const path of paths) {
try {
console.log(`경로 시도: ${path}`);
const response = await fetch(`${supabaseUrl}${path}${cacheParam}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'apikey': localStorage.getItem('supabase_key') || ''
},
signal: AbortSignal.timeout(15000) // 타임아웃 시간 증가
});
console.log(`경로 ${path} 응답 상태:`, response.status);
// 어떤 응답이든 서버가 살아있다는 신호로 간주
return {
connected: true,
message: `서버 연결 성공 (${path})`,
statusCode: response.status,
details: `${response.status} ${response.statusText}`
};
} catch (error) {
console.warn(`${path} 경로 연결 실패:`, error);
// 계속 다음 경로 시도
}
}
// HTTP URL을 사용하는데 프록시가 비활성화된 경우 처리
if (handleHttpUrlWithoutProxy()) {
return {
connected: false,
message: 'HTTP URL에 직접 접근할 수 없어 CORS 프록시를 자동으로 활성화했습니다. 페이지를 새로고침하고 다시 시도하세요.',
details: 'CORS 제한으로 인해 HTTP URL에 직접 접근할 수 없습니다'
};
}
// 모든 경로 시도 실패
return {
connected: false,
message: '모든 Supabase 경로에 대한 연결 시도 실패',
details: '네트워크 연결 또는 서버 주소를 확인하세요'
};
};

View File

@@ -0,0 +1,5 @@
// 네트워크 유틸리티 모듈
export { hasCorsIssue, handleHttpUrlWithoutProxy, logProxyInfo } from './corsUtils';
export { verifyServerConnection } from './basicConnectionCheck';
export { verifySupabaseConnection } from './enhancedConnectionCheck';

View File

@@ -1,247 +1,8 @@
import { supabase } from '@/lib/supabase';
import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config';
/**
* CORS 문제 확인
*/
export const hasCorsIssue = (error: any): boolean => {
if (!error) return false;
const errorMessage = error.message || '';
return (
errorMessage.includes('Failed to fetch') ||
errorMessage.includes('CORS') ||
errorMessage.includes('Network') ||
errorMessage.includes('프록시')
);
};
/**
* 서버 연결 상태 검사
*/
export const verifyServerConnection = async (): Promise<{
connected: boolean;
message: string;
statusCode?: number;
}> => {
try {
const start = Date.now();
// Supabase URL 가져오기 (프록시 적용 URL)
const supabaseUrl = getSupabaseUrl();
if (!supabaseUrl) {
return {
connected: false,
message: 'Supabase URL이 설정되지 않았습니다. 설정 페이지에서 구성하세요.'
};
}
// 프록시 설정 상태 확인
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
console.log(`연결 테스트 - CORS 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}, URL: ${supabaseUrl}`);
// 단순 헬스 체크 요청 - 무작위 쿼리 파라미터 추가
const cacheParam = `?_nocache=${Date.now()}`;
try {
const response = await fetch(`${supabaseUrl}/auth/v1/${cacheParam}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'apikey': localStorage.getItem('supabase_key') || ''
},
signal: AbortSignal.timeout(15000) // 타임아웃 시간 증가
});
const elapsed = Date.now() - start;
// 200, 401, 404 응답도 서버가 살아있다는 신호로 간주
if (response.ok || response.status === 401 || response.status === 404) {
return {
connected: true,
message: `서버 연결 성공 (응답 시간: ${elapsed}ms)`,
statusCode: response.status
};
} else {
return {
connected: false,
message: `서버 응답 오류: ${response.status} ${response.statusText}`,
statusCode: response.status
};
}
} catch (fetchError: any) {
console.error('기본 연결 확인 실패, 상태 확인 시도:', fetchError);
// HTTP URL을 사용하는데 프록시가 비활성화된 경우
const originalUrl = getOriginalSupabaseUrl();
if (originalUrl.startsWith('http:') && !usingProxy) {
// 자동으로 프록시 활성화
localStorage.setItem('use_cors_proxy', 'true');
localStorage.setItem('proxy_type', 'cloudflare');
return {
connected: false,
message: 'HTTP URL에 직접 접근할 수 없어 CORS 프록시를 자동으로 활성화했습니다. 페이지를 새로고침하고 다시 시도하세요.'
};
}
try {
// 대체 경로로 상태 확인 - 무작위 쿼리 파라미터 추가
const altResponse = await fetch(`${supabaseUrl}/${cacheParam}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
signal: AbortSignal.timeout(15000) // 타임아웃 시간 증가
});
// 어떤 응답이라도 오면 서버가 살아있다고 간주
const elapsed = Date.now() - start;
return {
connected: true,
message: `서버 연결 성공 (기본 경로, 응답 시간: ${elapsed}ms)`,
statusCode: altResponse.status
};
} catch (altError) {
console.error('기본 경로 확인도 실패:', altError);
throw fetchError; // 원래 에러를 던짐
}
}
} catch (error: any) {
console.error('서버 연결 확인 중 오류:', error);
// 프록시 설정 확인
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
// 오류 유형에 따른 메시지 설정
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 if (error.message.includes('timeout') || error.message.includes('timed out')) {
errorMessage = '서버 응답 시간 초과';
} else if (error.message.includes('aborted')) {
errorMessage = '요청이 중단됨';
} else {
errorMessage = error.message;
}
}
// HTTP URL을 사용하는데 프록시가 비활성화된 경우
const originalUrl = getOriginalSupabaseUrl();
if (originalUrl.startsWith('http:') && !usingProxy) {
// 자동으로 프록시 활성화
localStorage.setItem('use_cors_proxy', 'true');
localStorage.setItem('proxy_type', 'cloudflare');
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 프록시로 변경을 권장합니다.';
}
}
return {
connected: false,
message: errorMessage
};
}
};
/**
* 강화된 서버 연결 검사: 다양한 경로로 시도
*/
export const verifySupabaseConnection = async (): Promise<{
connected: boolean;
message: string;
statusCode?: number;
details?: string;
}> => {
const supabaseUrl = getSupabaseUrl();
if (!supabaseUrl) {
return {
connected: false,
message: 'Supabase URL이 설정되지 않았습니다'
};
}
// 프록시 정보 로깅
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
console.log(`강화된 연결 테스트 - 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}, URL: ${supabaseUrl}`);
// 무작위 쿼리 파라미터를 추가하여 캐시 방지
const cacheParam = `?_nocache=${Date.now()}`;
// 다양한 경로를 순차적으로 시도
const paths = [
'/auth/v1/',
'/',
'/rest/v1/',
'/storage/v1/'
];
for (const path of paths) {
try {
console.log(`경로 시도: ${path}`);
const response = await fetch(`${supabaseUrl}${path}${cacheParam}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'apikey': localStorage.getItem('supabase_key') || ''
},
signal: AbortSignal.timeout(15000) // 타임아웃 시간 증가
});
console.log(`경로 ${path} 응답 상태:`, response.status);
// 어떤 응답이든 서버가 살아있다는 신호로 간주
return {
connected: true,
message: `서버 연결 성공 (${path})`,
statusCode: response.status,
details: `${response.status} ${response.statusText}`
};
} catch (error) {
console.warn(`${path} 경로 연결 실패:`, error);
// 계속 다음 경로 시도
}
}
// HTTP URL을 사용하는데 프록시가 비활성화된 경우
const originalUrl = getOriginalSupabaseUrl();
if (originalUrl.startsWith('http:') && !usingProxy) {
// 자동으로 프록시 활성화
localStorage.setItem('use_cors_proxy', 'true');
localStorage.setItem('proxy_type', 'cloudflare');
return {
connected: false,
message: 'HTTP URL에 직접 접근할 수 없어 CORS 프록시를 자동으로 활성화했습니다. 페이지를 새로고침하고 다시 시도하세요.',
details: 'CORS 제한으로 인해 HTTP URL에 직접 접근할 수 없습니다'
};
}
// 모든 경로 시도 실패
return {
connected: false,
message: '모든 Supabase 경로에 대한 연결 시도 실패',
details: '네트워크 연결 또는 서버 주소를 확인하세요'
};
};
// 이 파일은 이전 버전과의 호환성을 위해 유지됩니다.
// 새 코드에서는 utils/auth/network 모듈을 직접 사용하세요.
export {
hasCorsIssue,
verifyServerConnection,
verifySupabaseConnection
} from './network';