From 30d214b3671a61149667216fe37c132f8d04c7a0 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 13:50:46 +0000 Subject: [PATCH] Refactor signIn and signUp Refactor signIn.ts and signUp.ts files to improve maintainability. --- src/contexts/auth/auth.utils.ts | 4 + src/contexts/auth/signIn.ts | 118 +--------------------- src/contexts/auth/signInUtils.ts | 121 ++++++++++++++++++++++ src/contexts/auth/signUp.ts | 142 +------------------------- src/contexts/auth/signUpUtils.ts | 144 +++++++++++++++++++++++++++ src/utils/auth/handleNetworkError.ts | 21 ++++ src/utils/auth/index.ts | 2 + 7 files changed, 296 insertions(+), 256 deletions(-) create mode 100644 src/contexts/auth/auth.utils.ts create mode 100644 src/contexts/auth/signInUtils.ts create mode 100644 src/contexts/auth/signUpUtils.ts create mode 100644 src/utils/auth/handleNetworkError.ts diff --git a/src/contexts/auth/auth.utils.ts b/src/contexts/auth/auth.utils.ts new file mode 100644 index 0000000..8e75979 --- /dev/null +++ b/src/contexts/auth/auth.utils.ts @@ -0,0 +1,4 @@ + +import { verifyServerConnection } from '@/utils/auth/networkUtils'; + +export { verifyServerConnection }; diff --git a/src/contexts/auth/signIn.ts b/src/contexts/auth/signIn.ts index db25272..4a009ea 100644 --- a/src/contexts/auth/signIn.ts +++ b/src/contexts/auth/signIn.ts @@ -1,3 +1,4 @@ + import { supabase } from '@/lib/supabase'; import { handleNetworkError, @@ -5,6 +6,7 @@ import { showAuthToast, verifyServerConnection } from '@/utils/auth'; +import { signInWithDirectApi } from './signInUtils'; export const signIn = async (email: string, password: string) => { try { @@ -67,119 +69,3 @@ export const signIn = async (email: string, password: string) => { return { error: { message: errorMessage }, user: null }; } }; - -// 직접 API 호출을 통한 로그인 시도 (대체 방법) -const signInWithDirectApi = async (email: string, password: string) => { - console.log('직접 API 호출로 로그인 시도'); - - try { - // 로그인 API 엔드포인트 URL과 헤더 준비 - const tokenUrl = `${supabase.auth.url}/token?grant_type=password`; - const headers = { - 'Content-Type': 'application/json', - 'apikey': supabase.supabaseKey, - 'Authorization': `Bearer ${supabase.supabaseKey}`, - 'X-Client-Info': 'supabase-js/2.x' - }; - - console.log('로그인 API 요청 URL:', tokenUrl); - - // 로그인 요청 보내기 - const response = await fetch(tokenUrl, { - method: 'POST', - headers, - body: JSON.stringify({ email, password }) - }); - - // 응답 상태 확인 및 로깅 - console.log('로그인 응답 상태:', response.status); - - // HTTP 상태 코드 확인 - if (response.status === 401) { - showAuthToast('로그인 실패', '이메일 또는 비밀번호가 올바르지 않습니다.', 'destructive'); - return { - error: { message: '인증 실패: 이메일 또는 비밀번호가 올바르지 않습니다.' }, - user: null - }; - } - - if (response.status === 404) { - showAuthToast('로그인 실패', '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.', 'destructive'); - return { - error: { message: '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.' }, - user: null - }; - } - - // 응답 처리 - const responseData = await parseResponse(response); - - // 빈 응답이나 파싱 실패 시 - if (responseData.status && !responseData.success && !responseData.error) { - if (response.status >= 200 && response.status < 300) { - // 성공 상태 코드이지만 응답 내용 없음 - showAuthToast('로그인 상태 확인 필요', '서버가 성공 응답을 보냈지만 내용이 없습니다. 세션 상태를 확인하세요.'); - - // 사용자 세션 확인 시도 - try { - const { data: userData } = await supabase.auth.getUser(); - if (userData.user) { - showAuthToast('로그인 성공', '환영합니다!'); - return { error: null, user: userData.user }; - } - } catch (sessionCheckError) { - console.error('세션 확인 오류:', sessionCheckError); - } - } - } - - // 오류 응답 확인 - if (responseData?.error) { - const errorMessage = responseData.error_description || responseData.error; - showAuthToast('로그인 실패', errorMessage, 'destructive'); - return { error: { message: errorMessage }, user: null }; - } - - // 로그인 성공 응답 처리 - if (response.ok && responseData?.access_token) { - try { - // 로그인 성공 시 Supabase 세션 설정 - await supabase.auth.setSession({ - access_token: responseData.access_token, - refresh_token: responseData.refresh_token - }); - - // 사용자 정보 가져오기 - const { data: userData } = await supabase.auth.getUser(); - - console.log('로그인 성공:', userData); - - showAuthToast('로그인 성공', '환영합니다!'); - - return { error: null, user: userData.user }; - } catch (sessionError) { - console.error('세션 설정 오류:', sessionError); - showAuthToast('로그인 후처리 오류', '로그인에 성공했지만 세션 설정에 실패했습니다.', 'destructive'); - return { error: { message: '세션 설정 오류' }, user: null }; - } - } else { - // 오류 응답이나 예상치 못한 응답 형식 처리 - console.error('로그인 오류 응답:', responseData); - - const errorMessage = responseData?.error_description || - responseData?.error || - responseData?.message || - '로그인에 실패했습니다. 이메일과 비밀번호를 확인하세요.'; - - showAuthToast('로그인 실패', errorMessage, 'destructive'); - return { error: { message: errorMessage }, user: null }; - } - } catch (fetchError) { - console.error('로그인 요청 중 fetch 오류:', fetchError); - - const errorMessage = handleNetworkError(fetchError); - showAuthToast('로그인 요청 실패', errorMessage, 'destructive'); - - return { error: { message: errorMessage }, user: null }; - } -}; diff --git a/src/contexts/auth/signInUtils.ts b/src/contexts/auth/signInUtils.ts new file mode 100644 index 0000000..5765212 --- /dev/null +++ b/src/contexts/auth/signInUtils.ts @@ -0,0 +1,121 @@ + +import { supabase } from '@/lib/supabase'; +import { parseResponse, showAuthToast, handleNetworkError } from '@/utils/auth'; + +/** + * 직접 API 호출을 통한 로그인 시도 (대체 방법) + */ +export const signInWithDirectApi = async (email: string, password: string) => { + console.log('직접 API 호출로 로그인 시도'); + + try { + // 로그인 API 엔드포인트 URL과 헤더 준비 + const tokenUrl = `${supabase.auth.url}/token?grant_type=password`; + const headers = { + 'Content-Type': 'application/json', + 'apikey': supabase.supabaseKey, + 'Authorization': `Bearer ${supabase.supabaseKey}`, + 'X-Client-Info': 'supabase-js/2.x' + }; + + console.log('로그인 API 요청 URL:', tokenUrl); + + // 로그인 요청 보내기 + const response = await fetch(tokenUrl, { + method: 'POST', + headers, + body: JSON.stringify({ email, password }) + }); + + // 응답 상태 확인 및 로깅 + console.log('로그인 응답 상태:', response.status); + + // HTTP 상태 코드 확인 + if (response.status === 401) { + showAuthToast('로그인 실패', '이메일 또는 비밀번호가 올바르지 않습니다.', 'destructive'); + return { + error: { message: '인증 실패: 이메일 또는 비밀번호가 올바르지 않습니다.' }, + user: null + }; + } + + if (response.status === 404) { + showAuthToast('로그인 실패', '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.', 'destructive'); + return { + error: { message: '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.' }, + user: null + }; + } + + // 응답 처리 + const responseData = await parseResponse(response); + + // 빈 응답이나 파싱 실패 시 + if (responseData.status && !responseData.success && !responseData.error) { + if (response.status >= 200 && response.status < 300) { + // 성공 상태 코드이지만 응답 내용 없음 + showAuthToast('로그인 상태 확인 필요', '서버가 성공 응답을 보냈지만 내용이 없습니다. 세션 상태를 확인하세요.'); + + // 사용자 세션 확인 시도 + try { + const { data: userData } = await supabase.auth.getUser(); + if (userData.user) { + showAuthToast('로그인 성공', '환영합니다!'); + return { error: null, user: userData.user }; + } + } catch (sessionCheckError) { + console.error('세션 확인 오류:', sessionCheckError); + } + } + } + + // 오류 응답 확인 + if (responseData?.error) { + const errorMessage = responseData.error_description || responseData.error; + showAuthToast('로그인 실패', errorMessage, 'destructive'); + return { error: { message: errorMessage }, user: null }; + } + + // 로그인 성공 응답 처리 + if (response.ok && responseData?.access_token) { + try { + // 로그인 성공 시 Supabase 세션 설정 + await supabase.auth.setSession({ + access_token: responseData.access_token, + refresh_token: responseData.refresh_token + }); + + // 사용자 정보 가져오기 + const { data: userData } = await supabase.auth.getUser(); + + console.log('로그인 성공:', userData); + + showAuthToast('로그인 성공', '환영합니다!'); + + return { error: null, user: userData.user }; + } catch (sessionError) { + console.error('세션 설정 오류:', sessionError); + showAuthToast('로그인 후처리 오류', '로그인에 성공했지만 세션 설정에 실패했습니다.', 'destructive'); + return { error: { message: '세션 설정 오류' }, user: null }; + } + } else { + // 오류 응답이나 예상치 못한 응답 형식 처리 + console.error('로그인 오류 응답:', responseData); + + const errorMessage = responseData?.error_description || + responseData?.error || + responseData?.message || + '로그인에 실패했습니다. 이메일과 비밀번호를 확인하세요.'; + + showAuthToast('로그인 실패', errorMessage, 'destructive'); + return { error: { message: errorMessage }, user: null }; + } + } catch (fetchError) { + console.error('로그인 요청 중 fetch 오류:', fetchError); + + const errorMessage = handleNetworkError(fetchError); + showAuthToast('로그인 요청 실패', errorMessage, 'destructive'); + + return { error: { message: errorMessage }, user: null }; + } +}; diff --git a/src/contexts/auth/signUp.ts b/src/contexts/auth/signUp.ts index 802befc..68ecb84 100644 --- a/src/contexts/auth/signUp.ts +++ b/src/contexts/auth/signUp.ts @@ -1,9 +1,10 @@ + import { supabase } from '@/lib/supabase'; import { handleNetworkError, - parseResponse, showAuthToast } from '@/utils/auth'; +import { signUpWithDirectApi } from './signUpUtils'; export const signUp = async (email: string, password: string, username: string) => { try { @@ -107,142 +108,3 @@ export const signUp = async (email: string, password: string, username: string) return { error, user: null }; } }; - -// 직접 API 호출을 통한 회원가입 시도 (대체 방법) -const signUpWithDirectApi = async (email: string, password: string, username: string) => { - console.log('직접 API 호출로 회원가입 시도'); - - try { - // 요청 URL과 헤더 준비 - const signUpUrl = `${supabase.auth.url}/signup`; - const headers = { - 'Content-Type': 'application/json', - 'apikey': supabase.supabaseKey, - 'Authorization': `Bearer ${supabase.supabaseKey}`, - }; - - console.log('회원가입 API 요청 URL:', signUpUrl); - - // 회원가입 요청 보내기 - const response = await fetch(signUpUrl, { - method: 'POST', - headers, - body: JSON.stringify({ - email, - password, - data: { username } - }) - }); - - console.log('회원가입 API 응답 상태:', response.status); - - // HTTP 상태 코드 확인 - if (response.status === 401) { - showAuthToast('회원가입 실패', '인증 오류: API 키가 올바르지 않거나 서버 설정 문제가 있습니다.', 'destructive'); - return { - error: { message: '인증 오류: API 키가 올바르지 않거나 서버 설정 문제가 있습니다.' }, - user: null - }; - } - - if (response.status === 404) { - showAuthToast('회원가입 실패', '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.', 'destructive'); - return { - error: { message: '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.' }, - user: null - }; - } - - if (response.status === 500) { - showAuthToast('회원가입 실패', '서버 내부 오류가 발생했습니다.', 'destructive'); - return { - error: { message: '서버 내부 오류가 발생했습니다.' }, - user: null - }; - } - - // 응답 본문이 비어 있는지 확인 - const responseText = await response.text(); - if (!responseText || responseText.trim() === '') { - if (response.ok) { - // 응답이 비어 있지만 상태 코드가 성공인 경우 - showAuthToast('회원가입 가능성 있음', '서버가 빈 응답을 반환했지만 상태 코드는 성공입니다. 로그인을 시도해보세요.'); - return { - error: null, - user: { email, username } - }; - } else { - // 응답이 비어 있고 상태 코드가 실패인 경우 - showAuthToast('회원가입 실패', `서버가 빈 응답을 반환했습니다 (${response.status}). 서버 설정을 확인하세요.`, 'destructive'); - return { - error: { - message: `서버가 빈 응답을 반환했습니다 (${response.status}). 서버 설정을 확인하세요.` - }, - user: null - }; - } - } - - // 응답 JSON 파싱 시도 - let result; - try { - result = JSON.parse(responseText); - } catch (jsonError) { - console.error('JSON 파싱 오류:', jsonError, '원본 텍스트:', responseText); - - if (response.ok) { - // 파싱 오류지만 상태 코드가 성공인 경우 - showAuthToast('회원가입 가능성 있음', '서버 응답을 해석할 수 없지만 상태 코드는 성공입니다. 로그인을 시도해보세요.'); - return { - error: null, - user: { email, username } - }; - } else { - // 파싱 오류이고 상태 코드도 실패인 경우 - showAuthToast('회원가입 실패', `서버 응답을 해석할 수 없습니다 (${response.status}). 서버 설정을 확인하세요.`, 'destructive'); - return { - error: { - message: `서버 응답을 해석할 수 없습니다 (${response.status}). 서버 설정을 확인하세요.` - }, - user: null - }; - } - } - - // 응답 분석 및 결과 반환 - if (result.error) { - showAuthToast('회원가입 실패', result.error_description || result.error || '회원가입에 실패했습니다.', 'destructive'); - return { - error: { message: result.error_description || result.error }, - user: null - }; - } - - if (result.id || result.user || result.data?.user) { - showAuthToast('회원가입 성공', '이메일 확인 후 로그인해주세요.'); - return { - error: null, - user: result.user || result.data?.user || { id: result.id, email, username } - }; - } - - // 알 수 없는 응답 형식 - console.warn('알 수 없는 회원가입 API 응답 형식:', result); - showAuthToast('회원가입 결과 불명확', '로그인을 시도해보세요.', 'destructive'); - return { - error: { message: '서버 응답을 해석할 수 없습니다.' }, - user: null - }; - } catch (error: any) { - console.error('직접 API 호출 회원가입 중 오류:', error); - - // 네트워크 오류 확인 - const errorMessage = handleNetworkError(error); - - showAuthToast('회원가입 API 호출 실패', errorMessage, 'destructive'); - return { - error: { message: errorMessage }, - user: null - }; - } -}; diff --git a/src/contexts/auth/signUpUtils.ts b/src/contexts/auth/signUpUtils.ts new file mode 100644 index 0000000..57e95fb --- /dev/null +++ b/src/contexts/auth/signUpUtils.ts @@ -0,0 +1,144 @@ + +import { supabase } from '@/lib/supabase'; +import { parseResponse, showAuthToast, handleNetworkError } from '@/utils/auth'; + +/** + * 직접 API 호출을 통한 회원가입 시도 (대체 방법) + */ +export const signUpWithDirectApi = async (email: string, password: string, username: string) => { + console.log('직접 API 호출로 회원가입 시도'); + + try { + // 요청 URL과 헤더 준비 + const signUpUrl = `${supabase.auth.url}/signup`; + const headers = { + 'Content-Type': 'application/json', + 'apikey': supabase.supabaseKey, + 'Authorization': `Bearer ${supabase.supabaseKey}`, + }; + + console.log('회원가입 API 요청 URL:', signUpUrl); + + // 회원가입 요청 보내기 + const response = await fetch(signUpUrl, { + method: 'POST', + headers, + body: JSON.stringify({ + email, + password, + data: { username } + }) + }); + + console.log('회원가입 API 응답 상태:', response.status); + + // HTTP 상태 코드 확인 + if (response.status === 401) { + showAuthToast('회원가입 실패', '인증 오류: API 키가 올바르지 않거나 서버 설정 문제가 있습니다.', 'destructive'); + return { + error: { message: '인증 오류: API 키가 올바르지 않거나 서버 설정 문제가 있습니다.' }, + user: null + }; + } + + if (response.status === 404) { + showAuthToast('회원가입 실패', '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.', 'destructive'); + return { + error: { message: '서버 경로를 찾을 수 없습니다. Supabase URL을 확인하세요.' }, + user: null + }; + } + + if (response.status === 500) { + showAuthToast('회원가입 실패', '서버 내부 오류가 발생했습니다.', 'destructive'); + return { + error: { message: '서버 내부 오류가 발생했습니다.' }, + user: null + }; + } + + // 응답 본문이 비어 있는지 확인 + const responseText = await response.text(); + if (!responseText || responseText.trim() === '') { + if (response.ok) { + // 응답이 비어 있지만 상태 코드가 성공인 경우 + showAuthToast('회원가입 가능성 있음', '서버가 빈 응답을 반환했지만 상태 코드는 성공입니다. 로그인을 시도해보세요.'); + return { + error: null, + user: { email, username } + }; + } else { + // 응답이 비어 있고 상태 코드가 실패인 경우 + showAuthToast('회원가입 실패', `서버가 빈 응답을 반환했습니다 (${response.status}). 서버 설정을 확인하세요.`, 'destructive'); + return { + error: { + message: `서버가 빈 응답을 반환했습니다 (${response.status}). 서버 설정을 확인하세요.` + }, + user: null + }; + } + } + + // 응답 JSON 파싱 시도 + let result; + try { + result = JSON.parse(responseText); + } catch (jsonError) { + console.error('JSON 파싱 오류:', jsonError, '원본 텍스트:', responseText); + + if (response.ok) { + // 파싱 오류지만 상태 코드가 성공인 경우 + showAuthToast('회원가입 가능성 있음', '서버 응답을 해석할 수 없지만 상태 코드는 성공입니다. 로그인을 시도해보세요.'); + return { + error: null, + user: { email, username } + }; + } else { + // 파싱 오류이고 상태 코드도 실패인 경우 + showAuthToast('회원가입 실패', `서버 응답을 해석할 수 없습니다 (${response.status}). 서버 설정을 확인하세요.`, 'destructive'); + return { + error: { + message: `서버 응답을 해석할 수 없습니다 (${response.status}). 서버 설정을 확인하세요.` + }, + user: null + }; + } + } + + // 응답 분석 및 결과 반환 + if (result.error) { + showAuthToast('회원가입 실패', result.error_description || result.error || '회원가입에 실패했습니다.', 'destructive'); + return { + error: { message: result.error_description || result.error }, + user: null + }; + } + + if (result.id || result.user || result.data?.user) { + showAuthToast('회원가입 성공', '이메일 확인 후 로그인해주세요.'); + return { + error: null, + user: result.user || result.data?.user || { id: result.id, email, username } + }; + } + + // 알 수 없는 응답 형식 + console.warn('알 수 없는 회원가입 API 응답 형식:', result); + showAuthToast('회원가입 결과 불명확', '로그인을 시도해보세요.', 'destructive'); + return { + error: { message: '서버 응답을 해석할 수 없습니다.' }, + user: null + }; + } catch (error: any) { + console.error('직접 API 호출 회원가입 중 오류:', error); + + // 네트워크 오류 확인 + const errorMessage = handleNetworkError(error); + + showAuthToast('회원가입 API 호출 실패', errorMessage, 'destructive'); + return { + error: { message: errorMessage }, + user: null + }; + } +}; diff --git a/src/utils/auth/handleNetworkError.ts b/src/utils/auth/handleNetworkError.ts new file mode 100644 index 0000000..2aa6aa9 --- /dev/null +++ b/src/utils/auth/handleNetworkError.ts @@ -0,0 +1,21 @@ + +/** + * 네트워크 오류 메시지 처리 유틸리티 함수 + */ +export const handleNetworkError = (error: any): string => { + 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 '네트워크 오류가 발생했습니다. 인터넷 연결을 확인하세요.'; + } else if (error.message && error.message.includes('json')) { + return '서버 응답 형식 오류: 서버가 올바른 JSON 응답을 반환하지 않았습니다.'; + } else if (error.message && error.message.includes('timeout')) { + return '서버 응답 시간 초과: 서버가 응답하지 않습니다.'; + } else if (error.message && error.message.includes('aborted')) { + return '요청이 중단되었습니다.'; + } + + return error.message || '예상치 못한 오류가 발생했습니다.'; +}; diff --git a/src/utils/auth/index.ts b/src/utils/auth/index.ts index 4615586..1b3a09c 100644 --- a/src/utils/auth/index.ts +++ b/src/utils/auth/index.ts @@ -4,3 +4,5 @@ export * from './toastUtils'; export * from './networkUtils'; export * from './responseUtils'; export * from './validationUtils'; +export * from './handleNetworkError'; +