Refactor signUpUtils.ts file

The signUpUtils.ts file was refactored into smaller components to improve code readability and maintainability.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-15 16:19:27 +00:00
parent 5d1ff46c3e
commit a735dee195
3 changed files with 180 additions and 116 deletions

View File

@@ -0,0 +1,77 @@
import { parseResponse, showAuthToast } from '@/utils/auth';
import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config';
import { handleNetworkError } from '@/utils/auth/handleNetworkError';
/**
* 직접 API 호출을 통한 회원가입 요청 전송
*/
export const sendSignUpApiRequest = async (
email: string,
password: string,
username: string,
redirectUrl: string,
supabaseKey: string
) => {
try {
// 프록시 적용된 URL과 원본 URL 모두 가져오기
const supabaseUrl = getOriginalSupabaseUrl(); // 원본 URL
const proxyUrl = getSupabaseUrl(); // 프록시 적용된 URL
// 프록시 정보 로깅
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 = useUrl.includes('/auth/v1') ? useUrl : `${useUrl}/auth/v1`;
// 회원가입 API 엔드포인트 및 헤더 설정
const signUpUrl = `${baseUrl}/signup`;
const headers = {
'Content-Type': 'application/json',
'apikey': supabaseKey
};
console.log('회원가입 API 요청 URL:', signUpUrl);
// 회원가입 요청 전송
const response = await fetch(signUpUrl, {
method: 'POST',
headers,
body: JSON.stringify({
email,
password,
data: { username }, // 사용자 메타데이터에 username 추가
redirect_to: redirectUrl // 리디렉션 URL 추가
}),
signal: AbortSignal.timeout(15000) // 타임아웃 시간 증가
});
console.log('회원가입 응답 상태:', response.status);
return response;
} catch (error) {
throw error; // 상위 함수에서 처리하도록 오류 전파
}
};
/**
* 응답 상태 코드 기반 에러 메시지 생성
*/
export const getStatusErrorMessage = (status: number): string => {
switch (status) {
case 400:
return '잘못된 요청 형식입니다. 입력 데이터를 확인하세요.';
case 401:
return '회원가입 권한이 없습니다. Supabase 설정 또는 권한을 확인하세요.';
case 404:
return '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.';
case 500:
return '서버 내부 오류가 발생했습니다. 나중에 다시 시도하세요.';
default:
return `회원가입 처리 중 오류가 발생했습니다 (${status}). 나중에 다시 시도하세요.`;
}
};

View File

@@ -0,0 +1,54 @@
import { showAuthToast } from '@/utils/auth';
import { getProxyType, isCorsProxyEnabled } from '@/lib/supabase/config';
/**
* 회원가입 API 호출 중 발생한 예외 처리
*/
export const handleSignUpApiError = (error: any) => {
console.error('회원가입 API 호출 중 예외 발생:', error);
// 프록시 설정 확인
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
// 오류 메시지 설정 및 프록시 추천
let errorMessage = error.message || '알 수 없는 오류가 발생했습니다.';
// 타임아웃 오류 감지
if (errorMessage.includes('timed out') || errorMessage.includes('timeout')) {
errorMessage = '서버 응답 시간이 초과되었습니다. 네트워크 상태를 확인하고 다시 시도하세요.';
}
// CORS 또는 네트워크 오류 감지
else 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 };
};
/**
* 응답 데이터에서 특정 에러 처리
*/
export const handleResponseError = (responseData: any) => {
if (responseData && responseData.error) {
const errorMessage = responseData.error_description || responseData.error || '회원가입 실패';
if (responseData.error === 'user_already_registered') {
showAuthToast('회원가입 실패', '이미 등록된 이메일 주소입니다.', 'destructive');
return { error: { message: '이미 등록된 이메일 주소입니다.' }, user: null };
}
showAuthToast('회원가입 실패', errorMessage, 'destructive');
return { error: { message: errorMessage }, user: null };
}
return null;
};

View File

@@ -1,7 +1,8 @@
import { supabase } from '@/lib/supabase';
import { parseResponse, showAuthToast } from '@/utils/auth';
import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config';
import { sendSignUpApiRequest, getStatusErrorMessage } from './signUpApiCalls';
import { handleSignUpApiError, handleResponseError } from './signUpErrorHandlers';
/**
* 직접 API 호출을 통한 회원가입
@@ -10,9 +11,7 @@ export const signUpWithDirectApi = async (email: string, password: string, usern
try {
console.log('직접 API 호출로 회원가입 시도 중');
// 프록시 적용된 URL과 원본 URL 모두 가져오기
const supabaseUrl = getOriginalSupabaseUrl(); // 원본 URL
const proxyUrl = getSupabaseUrl(); // 프록시 적용된 URL
// Supabase 키 가져오기
const supabaseKey = localStorage.getItem('supabase_key') || supabase.supabaseKey;
// Supabase 키 유효성 검사
@@ -28,40 +27,8 @@ export const signUpWithDirectApi = async (email: string, password: string, usern
const finalRedirectUrl = redirectUrl || `${window.location.origin}/login`;
console.log('이메일 인증 리디렉션 URL (API):', finalRedirectUrl);
// 프록시 정보 로깅
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 = useUrl.includes('/auth/v1') ? useUrl : `${useUrl}/auth/v1`;
// 회원가입 API 엔드포인트 및 헤더 설정
const signUpUrl = `${baseUrl}/signup`;
const headers = {
'Content-Type': 'application/json',
'apikey': supabaseKey
};
console.log('회원가입 API 요청 URL:', signUpUrl);
// 회원가입 요청 전송
const response = await fetch(signUpUrl, {
method: 'POST',
headers,
body: JSON.stringify({
email,
password,
data: { username }, // 사용자 메타데이터에 username 추가
redirect_to: finalRedirectUrl // 리디렉션 URL 추가
}),
signal: AbortSignal.timeout(15000) // 타임아웃 시간 증가
});
console.log('회원가입 응답 상태:', response.status);
// API 요청 전송
const response = await sendSignUpApiRequest(email, password, username, finalRedirectUrl, supabaseKey);
// 401 오류 처리 (권한 없음)
if (response.status === 401) {
@@ -113,24 +80,51 @@ export const signUpWithDirectApi = async (email: string, password: string, usern
}
}
if (responseData && responseData.error) {
const errorMessage = responseData.error_description || responseData.error || '회원가입 실패';
// 응답 에러 처리
const errorResult = handleResponseError(responseData);
if (errorResult) return errorResult;
if (responseData.error === 'user_already_registered') {
showAuthToast('회원가입 실패', '이미 등록된 이메일 주소입니다.', 'destructive');
return { error: { message: '이미 등록된 이메일 주소입니다.' }, user: null };
// 응답 상태 코드가 성공(2xx)이면서 사용자 데이터가 있는 경우
if (response.ok && responseData && responseData.id) {
return processSuccessfulSignup(responseData, email, password);
}
// 응답이 성공(2xx)이지만, 사용자 정보가 없는 경우 또는 응답 본문이 비어있는 경우
else if (response.ok) {
// 응답 본문이 비어 있는 경우는 서버가 성공을 반환했지만 데이터가 없는 경우 (일부 Supabase 버전에서 발생)
showAuthToast('회원가입 요청 완료', '회원가입 요청이 처리되었습니다. 이메일을 확인하거나 로그인을 시도해보세요.', 'default');
return {
error: null,
user: { email },
message: '회원가입 처리 완료'
};
}
// 401 오류인 경우 인증 실패로 처리
else if (response.status === 401) {
const errorMessage = '회원가입 권한이 없습니다. Supabase 설정 또는 권한을 확인하세요.';
showAuthToast('회원가입 실패', errorMessage, 'destructive');
return { error: { message: errorMessage }, user: null, redirectToSettings: true };
}
// 다른 모든 오류 상태
else {
// 응답 상태 코드에 따른 오류 메시지
const errorMessage = getStatusErrorMessage(response.status);
showAuthToast('회원가입 실패', errorMessage, 'destructive');
return { error: { message: errorMessage }, user: null };
}
} catch (error: any) {
return handleSignUpApiError(error);
}
};
// 응답 상태 코드가 성공(2xx)이면서 사용자 데이터가 있는 경우
if (response.ok && responseData && responseData.id) {
/**
* 성공적인 회원가입 응답 처리
*/
const processSuccessfulSignup = async (responseData: any, email: string, password: string) => {
const user = {
id: responseData.id,
email: responseData.email,
user_metadata: responseData.user_metadata || { username },
user_metadata: responseData.user_metadata || { username: responseData.user_metadata?.username || '' },
app_metadata: responseData.app_metadata || {},
created_at: responseData.created_at,
};
@@ -158,65 +152,4 @@ export const signUpWithDirectApi = async (email: string, password: string, usern
return { error: null, user };
}
}
// 응답이 성공(2xx)이지만 사용자 정보가 없는 경우 또는 응답 본문이 비어있는 경우
else if (response.ok) {
// 응답 본문이 비어 있는 경우는 서버가 성공을 반환했지만 데이터가 없는 경우 (일부 Supabase 버전에서 발생)
showAuthToast('회원가입 요청 완료', '회원가입 요청이 처리되었습니다. 이메일을 확인하거나 로그인을 시도해보세요.', 'default');
return {
error: null,
user: { email },
message: '회원가입 처리 완료'
};
}
// 401 오류인 경우 인증 실패로 처리
else if (response.status === 401) {
const errorMessage = '회원가입 권한이 없습니다. Supabase 설정 또는 권한을 확인하세요.';
showAuthToast('회원가입 실패', errorMessage, 'destructive');
return { error: { message: errorMessage }, user: null, redirectToSettings: true };
}
// 다른 모든 오류 상태
else {
// 응답 상태 코드에 따른 오류 메시지
let errorMessage;
if (response.status === 400) {
errorMessage = '잘못된 요청 형식입니다. 입력 데이터를 확인하세요.';
} else if (response.status === 500) {
errorMessage = '서버 내부 오류가 발생했습니다. 나중에 다시 시도하세요.';
} else {
errorMessage = `회원가입 처리 중 오류가 발생했습니다 (${response.status}). 나중에 다시 시도하세요.`;
}
showAuthToast('회원가입 실패', errorMessage, 'destructive');
return { error: { message: errorMessage }, user: null };
}
} catch (error: any) {
console.error('회원가입 API 호출 중 예외 발생:', error);
// 프록시 설정 확인
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
// 오류 메시지 설정 및 프록시 추천
let errorMessage = error.message || '알 수 없는 오류가 발생했습니다.';
// 타임아웃 오류 감지
if (errorMessage.includes('timed out') || errorMessage.includes('timeout')) {
errorMessage = '서버 응답 시간이 초과되었습니다. 네트워크 상태를 확인하고 다시 시도하세요.';
}
// CORS 또는 네트워크 오류 감지
else 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 };
}
};