diff --git a/src/contexts/auth/auth.utils.ts b/src/contexts/auth/auth.utils.ts new file mode 100644 index 0000000..e3fd3d4 --- /dev/null +++ b/src/contexts/auth/auth.utils.ts @@ -0,0 +1,32 @@ + +import { toast } from '@/hooks/useToast.wrapper'; +import { supabase } from '@/lib/supabase'; + +// 토스트 메시지 표시 유틸리티 함수 +export const showAuthToast = (title: string, description: string, variant: 'default' | 'destructive' = 'default') => { + toast({ title, description, variant }); +}; + +// 에러 메시지 처리 유틸리티 함수 +export const handleNetworkError = (error: any): string => { + return error.message && error.message.includes('fetch') + ? '서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.' + : (error.message || '예상치 못한 오류가 발생했습니다.'); +}; + +// 응답 파싱 유틸리티 함수 +export const parseResponse = async (response: Response) => { + try { + const responseText = await response.text(); + console.log('응답 원본:', responseText); + + if (!responseText || responseText.trim() === '') { + throw new Error('서버가 빈 응답을 반환했습니다'); + } + + return JSON.parse(responseText); + } catch (parseError) { + console.error('응답 파싱 오류:', parseError); + throw parseError; + } +}; diff --git a/src/contexts/auth/authActions.ts b/src/contexts/auth/authActions.ts index 3405e58..d9adb89 100644 --- a/src/contexts/auth/authActions.ts +++ b/src/contexts/auth/authActions.ts @@ -1,206 +1,5 @@ -import { supabase } from '@/lib/supabase'; -import { toast } from '@/hooks/useToast.wrapper'; -export const signIn = async (email: string, password: string) => { - try { - console.log('로그인 시도 중:', email); - - // 응답 예외 처리를 위한 래핑 - const response = await fetch(`${supabase.auth.url}/token?grant_type=password`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'apikey': supabase.supabaseKey, - 'Authorization': `Bearer ${supabase.supabaseKey}`, - 'X-Client-Info': 'supabase-js/2.x' - }, - body: JSON.stringify({ email, password }) - }).catch(err => { - console.error('로그인 요청 중 fetch 오류:', err); - throw new Error('서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.'); - }); - - // 응답 상태 확인 및 로깅 - console.log('로그인 응답 상태:', response.status); - - // 빈 응답 또는 JSON 파싱 문제 처리 - let responseData; - try { - // 응답 본문이 비어있는지 먼저 확인 - const responseText = await response.text(); - console.log('로그인 응답 원본:', responseText); - - if (!responseText || responseText.trim() === '') { - throw new Error('서버가 빈 응답을 반환했습니다'); - } - - // 응답이 있으면 JSON으로 파싱 - responseData = JSON.parse(responseText); - } catch (parseError) { - console.error('로그인 응답 파싱 오류:', parseError); - return { - error: { - message: '서버 응답을 처리할 수 없습니다. CORS 프록시 설정을 확인하세요.' - }, - user: null - }; - } - - // 응답 처리 - if (response.ok && responseData?.access_token) { - // 로그인 성공 시 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); - - toast({ - title: '로그인 성공', - description: '환영합니다!', - }); - - return { error: null, user: userData.user }; - } else { - // 오류 응답 처리 - console.error('로그인 오류 응답:', responseData); - - const errorMessage = responseData?.error_description || - responseData?.error || - '로그인에 실패했습니다. 이메일과 비밀번호를 확인하세요.'; - - return { error: { message: errorMessage }, user: null }; - } - } catch (error: any) { - console.error('로그인 중 예외 발생:', error); - - // 네트워크 오류 확인 - const errorMessage = error.message && error.message.includes('fetch') - ? '서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.' - : (error.message || '예상치 못한 오류가 발생했습니다.'); - - return { error: { message: errorMessage }, user: null }; - } -}; - -export const signUp = async (email: string, password: string, username: string) => { - try { - console.log('회원가입 시도:', { email, username }); - - const { data, error } = await supabase.auth.signUp({ - email, - password, - options: { - data: { - username, - }, - }, - }); - - if (error) { - console.error('회원가입 오류:', error); - toast({ - title: '회원가입 실패', - description: error.message, - variant: 'destructive', - }); - return { error, user: null }; - } - - toast({ - title: '회원가입 성공', - description: '이메일 확인 후 로그인해주세요.', - }); - - return { error: null, user: data.user }; - } catch (error: any) { - console.error('회원가입 중 예외 발생:', error); - - // 네트워크 오류 확인 - const errorMessage = error.message && error.message.includes('fetch') - ? '서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.' - : '예상치 못한 오류가 발생했습니다.'; - - toast({ - title: '회원가입 오류', - description: errorMessage, - variant: 'destructive', - }); - return { error, user: null }; - } -}; - -export const signOut = async (): Promise => { - try { - const { error } = await supabase.auth.signOut(); - - if (error) { - console.error('로그아웃 오류:', error); - toast({ - title: '로그아웃 실패', - description: error.message, - variant: 'destructive', - }); - } else { - toast({ - title: '로그아웃 성공', - description: '다음에 또 만나요!', - }); - } - } catch (error: any) { - console.error('로그아웃 중 예외 발생:', error); - - // 네트워크 오류 확인 - const errorMessage = error.message && error.message.includes('fetch') - ? '서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.' - : '예상치 못한 오류가 발생했습니다.'; - - toast({ - title: '로그아웃 오류', - description: errorMessage, - variant: 'destructive', - }); - } -}; - -export const resetPassword = async (email: string) => { - try { - const { error } = await supabase.auth.resetPasswordForEmail(email, { - redirectTo: window.location.origin + '/reset-password', - }); - - if (error) { - console.error('비밀번호 재설정 오류:', error); - toast({ - title: '비밀번호 재설정 실패', - description: error.message, - variant: 'destructive', - }); - return { error }; - } - - toast({ - title: '비밀번호 재설정 이메일 전송됨', - description: '이메일을 확인하여 비밀번호를 재설정해주세요.', - }); - return { error: null }; - } catch (error: any) { - console.error('비밀번호 재설정 중 예외 발생:', error); - - // 네트워크 오류 확인 - const errorMessage = error.message && error.message.includes('fetch') - ? '서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.' - : '예상치 못한 오류가 발생했습니다.'; - - toast({ - title: '비밀번호 재설정 오류', - description: errorMessage, - variant: 'destructive', - }); - return { error }; - } -}; +export { signIn } from './signIn'; +export { signUp } from './signUp'; +export { signOut } from './signOut'; +export { resetPassword } from './resetPassword'; diff --git a/src/contexts/auth/resetPassword.ts b/src/contexts/auth/resetPassword.ts new file mode 100644 index 0000000..34d8ba6 --- /dev/null +++ b/src/contexts/auth/resetPassword.ts @@ -0,0 +1,28 @@ + +import { supabase } from '@/lib/supabase'; +import { handleNetworkError, showAuthToast } from './auth.utils'; + +export const resetPassword = async (email: string) => { + try { + const { error } = await supabase.auth.resetPasswordForEmail(email, { + redirectTo: window.location.origin + '/reset-password', + }); + + if (error) { + console.error('비밀번호 재설정 오류:', error); + showAuthToast('비밀번호 재설정 실패', error.message, 'destructive'); + return { error }; + } + + showAuthToast('비밀번호 재설정 이메일 전송됨', '이메일을 확인하여 비밀번호를 재설정해주세요.'); + return { error: null }; + } catch (error: any) { + console.error('비밀번호 재설정 중 예외 발생:', error); + + // 네트워크 오류 확인 + const errorMessage = handleNetworkError(error); + + showAuthToast('비밀번호 재설정 오류', errorMessage, 'destructive'); + return { error }; + } +}; diff --git a/src/contexts/auth/signIn.ts b/src/contexts/auth/signIn.ts new file mode 100644 index 0000000..d0a33fb --- /dev/null +++ b/src/contexts/auth/signIn.ts @@ -0,0 +1,75 @@ + +import { supabase } from '@/lib/supabase'; +import { handleNetworkError, parseResponse, showAuthToast } from './auth.utils'; + +export const signIn = async (email: string, password: string) => { + try { + console.log('로그인 시도 중:', email); + + // 응답 예외 처리를 위한 래핑 + const response = await fetch(`${supabase.auth.url}/token?grant_type=password`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'apikey': supabase.supabaseKey, + 'Authorization': `Bearer ${supabase.supabaseKey}`, + 'X-Client-Info': 'supabase-js/2.x' + }, + body: JSON.stringify({ email, password }) + }).catch(err => { + console.error('로그인 요청 중 fetch 오류:', err); + throw new Error('서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.'); + }); + + // 응답 상태 확인 및 로깅 + console.log('로그인 응답 상태:', response.status); + + // 빈 응답 또는 JSON 파싱 문제 처리 + let responseData; + try { + responseData = await parseResponse(response); + } catch (parseError) { + console.error('로그인 응답 파싱 오류:', parseError); + return { + error: { + message: '서버 응답을 처리할 수 없습니다. CORS 프록시 설정을 확인하세요.' + }, + user: null + }; + } + + // 응답 처리 + if (response.ok && responseData?.access_token) { + // 로그인 성공 시 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 }; + } else { + // 오류 응답 처리 + console.error('로그인 오류 응답:', responseData); + + const errorMessage = responseData?.error_description || + responseData?.error || + '로그인에 실패했습니다. 이메일과 비밀번호를 확인하세요.'; + + return { error: { message: errorMessage }, user: null }; + } + } catch (error: any) { + console.error('로그인 중 예외 발생:', error); + + // 네트워크 오류 확인 + const errorMessage = handleNetworkError(error); + + return { error: { message: errorMessage }, user: null }; + } +}; diff --git a/src/contexts/auth/signOut.ts b/src/contexts/auth/signOut.ts new file mode 100644 index 0000000..cf22ad1 --- /dev/null +++ b/src/contexts/auth/signOut.ts @@ -0,0 +1,25 @@ + +import { supabase } from '@/lib/supabase'; +import { showAuthToast } from './auth.utils'; + +export const signOut = async (): Promise => { + try { + const { error } = await supabase.auth.signOut(); + + if (error) { + console.error('로그아웃 오류:', error); + showAuthToast('로그아웃 실패', error.message, 'destructive'); + } else { + showAuthToast('로그아웃 성공', '다음에 또 만나요!'); + } + } catch (error: any) { + console.error('로그아웃 중 예외 발생:', error); + + // 네트워크 오류 확인 + const errorMessage = error.message && error.message.includes('fetch') + ? '서버 연결에 실패했습니다. 네트워크 연결을 확인해주세요.' + : '예상치 못한 오류가 발생했습니다.'; + + showAuthToast('로그아웃 오류', errorMessage, 'destructive'); + } +}; diff --git a/src/contexts/auth/signUp.ts b/src/contexts/auth/signUp.ts new file mode 100644 index 0000000..7d2f67d --- /dev/null +++ b/src/contexts/auth/signUp.ts @@ -0,0 +1,37 @@ + +import { supabase } from '@/lib/supabase'; +import { handleNetworkError, showAuthToast } from './auth.utils'; + +export const signUp = async (email: string, password: string, username: string) => { + try { + console.log('회원가입 시도:', { email, username }); + + const { data, error } = await supabase.auth.signUp({ + email, + password, + options: { + data: { + username, + }, + }, + }); + + if (error) { + console.error('회원가입 오류:', error); + showAuthToast('회원가입 실패', error.message, 'destructive'); + return { error, user: null }; + } + + showAuthToast('회원가입 성공', '이메일 확인 후 로그인해주세요.'); + + return { error: null, user: data.user }; + } catch (error: any) { + console.error('회원가입 중 예외 발생:', error); + + // 네트워크 오류 확인 + const errorMessage = handleNetworkError(error); + + showAuthToast('회원가입 오류', errorMessage, 'destructive'); + return { error, user: null }; + } +};