-
Notifications
You must be signed in to change notification settings - Fork 0
기능추가 소셜로그인 구글 로그인 #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
The head ref may contain hidden characters: "\uAE30\uB2A5\uCD94\uAC00_\uC18C\uC15C\uB85C\uADF8\uC778_\uAD6C\uAE00_\uB85C\uADF8\uC778"
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,7 @@ | ||
| EXPO_PUBLIC_KAKAO_REST_API_KEY=fcc79e9199b5dbcaedfc00bb30b3d4af | ||
| EXPO_PUBLIC_SERVER_BASE_URL=https://api.dailysnap.app | ||
| EXPO_PUBLIC_NAVER_CLIENT_ID=tCdEIPDTDBnfdVRdRgoO | ||
| EXPO_PUBLIC_NAVER_CLIENT_SECRET=IX3znje6Hl | ||
| EXPO_PUBLIC_NAVER_REDIRECT_URI=https://dailysnap.xyz/api/auth/naver | ||
| EXPO_PUBLIC_GOOGLE_CLIENT_ID=735338487068-kh9kfnrrus8fdmucfek7996ml15jiell.apps.googleusercontent.com.apps.googleusercontent.com | ||
| EXPO_PUBLIC_GOOGLE_REDIRECT_URI=https://dailysnap.xyz/auth/google/callback | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,25 +6,26 @@ import { View, ActivityIndicator } from "react-native"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default function App() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const router = useRouter(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { userInfo } = useAuth(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // const { userInfo } = useAuth(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const timer = setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (userInfo) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 로그인된 사용자면, 홈 네비게이션으로 이동 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| router.replace("/(tabs)/home"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 로그인되지 않았으면 로그인 페이지로 이동 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| router.replace("/login"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| router.replace("/(tabs)/home"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // if (userInfo) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // // 로그인된 사용자면, 홈 네비게이션으로 이동 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // router.replace("/(tabs)/home"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // // 로그인되지 않았으면 로그인 페이지로 이동 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // router.replace("/login"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, 100); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => clearTimeout(timer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [userInfo, router]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [router]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 인증 우회에 대한 우려사항 사용자 인증 로직이 주석 처리되어 모든 사용자가 로그인 없이 홈으로 이동합니다. 이는 의도된 변경인지 확인이 필요합니다. 만약 개발 중 임시 변경이라면, 배포 전 반드시 인증 로직을 복원해야 합니다. 인증이 필요한 앱이라면 다음과 같이 복원하세요: - // const { userInfo } = useAuth();
+ const { userInfo } = useAuth();
- router.replace("/(tabs)/home");
- // if (userInfo) {
- // // 로그인된 사용자면, 홈 네비게이션으로 이동
- // router.replace("/(tabs)/home");
- // } else {
- // // 로그인되지 않았으면 로그인 페이지로 이동
- // router.replace("/login");
- // }
+ if (userInfo) {
+ // 로그인된 사용자면, 홈 네비게이션으로 이동
+ router.replace("/(tabs)/home");
+ } else {
+ // 로그인되지 않았으면 로그인 페이지로 이동
+ router.replace("/login");
+ }
- }, [router]);
+ }, [router, userInfo]);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 로딩 화면 표시. TODO: 추후에 디자인 된 로딩스피너로 수정 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ActivityIndicator size="large" color="#195B35" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </View> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,92 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as WebBrowser from "expo-web-browser"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import axios from "axios"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const GOOGLE_CLIENT_ID = process.env.EXPO_PUBLIC_GOOGLE_CLIENT_ID ?? ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const GOOGLE_REDIRECT_URI = process.env.EXPO_PUBLIC_GOOGLE_REDIRECT_URI ?? ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const SERVER_BASE_URL = process.env.EXPO_PUBLIC_SERVER_BASE_URL ?? ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 환경변수 검증 추가 필요 환경변수들이 빈 문자열로 초기화되어 있지만, 실제 런타임에서 이 값들이 유효한지 검증하는 로직이 없습니다. 다음과 같이 환경변수 검증을 추가하는 것을 권장합니다: const GOOGLE_CLIENT_ID = process.env.EXPO_PUBLIC_GOOGLE_CLIENT_ID ?? "";
const GOOGLE_REDIRECT_URI = process.env.EXPO_PUBLIC_GOOGLE_REDIRECT_URI ?? "";
const SERVER_BASE_URL = process.env.EXPO_PUBLIC_SERVER_BASE_URL ?? "";
+// 환경변수 검증
+if (!GOOGLE_CLIENT_ID || !GOOGLE_REDIRECT_URI || !SERVER_BASE_URL) {
+ throw new Error("구글 로그인에 필요한 환경변수가 설정되지 않았습니다.");
+}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 브라우저 로그인 결과 처리를 위한 타입 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface GoogleAuthResult { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "success" | "error" | "cancel"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params?: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| code?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error?: Error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 구글 로그인 인증 브라우저를 열고 인증 코드를 받아오는 함수 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function signInWithGoogle(): Promise<GoogleAuthResult> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // OAuth URL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const authUrl = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `https://accounts.google.com/o/oauth2/v2/auth?` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `client_id=${GOOGLE_CLIENT_ID}` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `&redirect_uri=${encodeURIComponent(GOOGLE_REDIRECT_URI)}` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `&response_type=code` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `&scope=${encodeURIComponent("profile email")}` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `&prompt=select_account`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 기본 브라우저로 구글 로그인 페이지 열기. 구글로그인은 Webview보다 WebBroswer 권장. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = await WebBrowser.openAuthSessionAsync(authUrl, GOOGLE_REDIRECT_URI); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 브라우저 결과 처리 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (result.type === "success") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // URL에서 code 파라미터 추출 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = new URL(result.url); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const code = url.searchParams.get("code"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (code) return { type: "success", params: { code } }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const error = url.searchParams.get("error"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params: { error: error || "인증 코드를 받아오지 못했습니다." }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { type: "cancel" }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Google Auth Error:", error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: error instanceof Error ? error : new Error("알 수 없는 오류가 발생했습니다."), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 백엔드와 통신해 사용자 정보 받아오는 함수 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function exchangeGoogleCodeForUserInfo(code: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await axios.post(`${SERVER_BASE_URL}/api/auth/google`, { code }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response.data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Google Token Exchange Error:", error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw error; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 백엔드 API 에러 처리 개선 필요 현재 백엔드 API 호출에서 HTTP 상태 코드별 에러 처리가 부족합니다. export async function exchangeGoogleCodeForUserInfo(code: string) {
try {
const response = await axios.post(`${SERVER_BASE_URL}/api/auth/google`, { code });
+
+ if (response.status !== 200) {
+ throw new Error(`서버 응답 오류: ${response.status}`);
+ }
+
return response.data;
} catch (error) {
console.error("Google Token Exchange Error:", error);
+
+ if (axios.isAxiosError(error)) {
+ if (error.response?.status === 401) {
+ throw new Error("인증에 실패했습니다.");
+ } else if (error.response?.status === 500) {
+ throw new Error("서버 오류가 발생했습니다.");
+ }
+ }
+
throw error;
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 구글 로그인 전체 플로우 처리 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function handleGoogleSignIn() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const authResult = await signInWithGoogle(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (authResult.type === "success" && authResult.params?.code) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userInfo = await exchangeGoogleCodeForUserInfo(authResult.params.code); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { success: true, user: userInfo }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (authResult.type === "cancel") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { success: false, error: "로그인이 취소되었습니다." }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: authResult.params?.error || "로그인 중 오류가 발생했습니다.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Google Sign In Error:", error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,44 @@ | ||
| import React from "react"; | ||
| import { TouchableOpacity, Text } from "react-native"; | ||
| import React, { useState } from "react"; | ||
| import { TouchableOpacity, Text, Alert, ActivityIndicator } from "react-native"; | ||
| import type { SocialLoginProps } from "../model/types"; | ||
| import { handleGoogleSignIn } from "../api/googleAuth"; | ||
|
|
||
| export const GoogleLoginButton: React.FC<SocialLoginProps> = ({ onLoginSuccess, onLoginError }) => { | ||
| const [isLoading, setIsLoading] = useState(false); | ||
|
|
||
| const handleLogin = async () => { | ||
| try { | ||
| setIsLoading(true); | ||
|
|
||
| const result = await handleGoogleSignIn(); | ||
|
|
||
| if (result.success && result.user) { | ||
| onLoginSuccess(result.user); | ||
| } else { | ||
| onLoginError(result.error || "구글 로그인 중 오류가 발생했습니다."); | ||
| Alert.alert("로그인 실패", result.error || "구글 로그인 중 오류가 발생했습니다."); | ||
| } | ||
| } catch (error) { | ||
| const errorMessage = | ||
| error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다."; | ||
| onLoginError(errorMessage); | ||
| Alert.alert("로그인 실패", errorMessage); | ||
| } finally { | ||
| setIsLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <TouchableOpacity> | ||
| <Text className="">구글로 시작하기</Text> | ||
| <TouchableOpacity | ||
| className="w-full bg-white border border-gray-300 py-4 rounded-xl items-center justify-center flex-row" | ||
| onPress={handleLogin} | ||
| disabled={isLoading} | ||
| > | ||
| {isLoading ? ( | ||
| <ActivityIndicator size="small" color="#4285F4" /> | ||
| ) : ( | ||
| <Text className="text-black font-semibold text-base">구글로 시작하기</Text> | ||
| )} | ||
| </TouchableOpacity> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,47 @@ | ||
| import React from "react"; | ||
| import { TouchableOpacity, Text } from "react-native"; | ||
| import React, { useState } from "react"; | ||
| import { TouchableOpacity, Text, Modal } from "react-native"; | ||
| import { NaverLoginWebView } from "./NaverLoginWebView"; | ||
| import type { SocialLoginProps } from "../model/types"; | ||
|
|
||
| export const NaverLoginButton: React.FC<SocialLoginProps> = ({ onLoginSuccess, onLoginError }) => { | ||
| const [showWebView, setShowWebView] = useState(false); | ||
|
|
||
| const handleNaverLogin = () => { | ||
| setShowWebView(true); | ||
| }; | ||
|
|
||
| const handleLoginSuccess = (userInfo: any) => { | ||
| console.log("네이버 로그인 성공:", userInfo); | ||
| setShowWebView(false); | ||
| onLoginSuccess(userInfo); | ||
| }; | ||
|
|
||
| const handleLoginError = (error: any) => { | ||
| console.error("네이버 로그인 실패:", error); | ||
| setShowWebView(false); | ||
| onLoginError(typeof error === "string" ? error : "네이버 로그인 중 오류가 발생했습니다."); | ||
| }; | ||
|
|
||
| const handleClose = () => { | ||
| setShowWebView(false); | ||
| }; | ||
|
|
||
| return ( | ||
| <TouchableOpacity> | ||
| <Text>네이버로 시작하기</Text> | ||
| </TouchableOpacity> | ||
| <> | ||
| <TouchableOpacity | ||
| onPress={handleNaverLogin} | ||
| className="w-full bg-[#03C75A] py-4 rounded-xl items-center justify-center" | ||
| > | ||
| <Text className="text-white font-semibold text-base">네이버로 시작하기</Text> | ||
| </TouchableOpacity> | ||
|
|
||
| <Modal visible={showWebView} animationType="slide" presentationStyle="pageSheet"> | ||
| <NaverLoginWebView | ||
| onLoginSuccess={handleLoginSuccess} | ||
| onLoginError={handleLoginError} | ||
| onClose={handleClose} | ||
| /> | ||
| </Modal> | ||
| </> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Google 클라이언트 ID 수정 필요
Google 클라이언트 ID에
.apps.googleusercontent.com이 중복으로 포함되어 있습니다. 올바른 형식으로 수정해야 합니다.다음과 같이 수정하세요:
📝 Committable suggestion
🤖 Prompt for AI Agents