모바일 앱에서 **OAuth(구글 로그인)**를 붙이는 일은 웹과는 차원이 다른 복잡함을 동반합니다. 웹이라면 URL 한 번 이동하면 끝이지만, 모바일은 외부 브라우저에서 인증을 마친 뒤 다시 내 앱으로 정확히 돌아오는 '딥링크(Deep Link)' 과정이 반드시 필요하기 때문입니다. 바로 이 "되돌아오는 길"을 제대로 깔지 못해 많은 분이 막힙니다.
이 글에서는 React Native(Expo) 환경에서 Supabase로 구글 로그인을 구현하고, 실제 기기 빌드 과정에서 마주칠 수 있는 치명적인 에러까지 해결하는 과정을 정리합니다.
1. 기술 스택 구성
먼저 이번 구현에 쓴 도구들과 선택 이유입니다.
영역
선택 기술
이유
Framework
Expo (React Native)
빠른 생산성 및 강력한 라이브러리 생태계
Auth / Backend
Supabase
Firebase 대비 높은 자유도와 PostgreSQL 기반의 강력한 인증 기능
Authentication
Google OAuth
가장 대중적이고 사용자 접근성이 높은 인증 방식
Browser
expo-web-browser
앱 내에서 안전한 인증 세션을 열기 위한 표준 라이브러리
Linking
expo-linking
인증 완료 후 앱으로 복귀하기 위한 딥링크 제어
핵심 흐름은 단순합니다. expo-web-browser로 인증 창을 열고, expo-linking으로 다시 앱으로 받아낸다. 나머지 설정은 모두 이 왕복을 매끄럽게 만들기 위한 준비 작업입니다.
2. 핵심 설정: 딥링크(Deep Link)
모바일 앱에는 localhost가 없습니다. 인증을 마치고 앱으로 돌아오려면, 앱만의 고유 주소(Scheme)가 필요합니다.
Redirect URLs에 day-rise://google-auth를 반드시 등록해야 합니다. 이 설정이 빠지면 인증을 마쳐도 앱으로 돌아오지 못하고, 사용자는 브라우저에 멈춰선 채 끝납니다. 가장 흔히 놓치는 지점이니 꼭 확인하세요.
3. 구글 로그인 로직 구현
단순한 링크 이동이 아니라, PKCE(Proof Key for Code Exchange) 흐름을 써서 보안성을 높인 구현입니다. 모바일처럼 클라이언트 시크릿을 안전하게 숨길 수 없는 환경에서 권장되는 방식입니다.
ts
import { supabase } from "@/lib/supabase";import * as Linking from "expo-linking";import * as WebBrowser from "expo-web-browser";async function signInWithGoogle() { // 1. 환경에 맞는 리다이렉트 URI 생성 (Deep Link) const redirectUrl = Linking.createURL("google-auth"); // 2. Supabase OAuth URL 요청 const { data, error } = await supabase.auth.signInWithOAuth({ provider: "google", options: { redirectTo: redirectUrl, skipBrowserRedirect: true, }, }); if (error) return console.log("Error:", error.message); // 3. 앱 내 브라우저에서 로그인 세션 실행 if (data?.url) { const result = await WebBrowser.openAuthSessionAsync(data.url, redirectUrl); if (result.type === "success" && result.url) { // 4. 돌아온 URL에서 인증 코드(Code)를 추출하여 세션으로 교환 const { queryParams } = Linking.parse(result.url); const code = queryParams?.code; if (code) { await supabase.auth.exchangeCodeForSession(code); } } }}
흐름을 한 줄로 요약하면 이렇습니다. 리다이렉트 URI를 만들고 → OAuth URL을 받아 브라우저를 열고 → 돌아온 URL에서 코드를 꺼내 → 세션으로 교환한다. 네 단계가 끊김 없이 이어져야 로그인이 완성됩니다.
4. 트러블슈팅: "No script url provided" 에러
빌드 후 USB를 뽑거나 네트워크를 바꿨더니 앱이 실행되지 않는 문제는, 많은 초보 개발자가 당황하는 지점입니다.
code
Error: "No script url provided. make sure the packager is running..."
원인 분석
이 에러는 앱이 디버그(Debug) 모드로 빌드됐기 때문입니다. 디버그 모드의 앱은 로직 코드를 앱 내부에 품고 있는 게 아니라, 내 컴퓨터의 Metro 서버에서 실시간으로 스트리밍 받습니다. 그러니 컴퓨터와의 연결이 끊기는 순간, 앱은 읽을 코드를 잃고 멈춰버리는 것이죠.
해결 방법: 릴리즈(Release) 모드 빌드
컴퓨터 연결 없이도 혼자 도는 '진짜 앱'을 만들려면, 자바스크립트 번들을 앱 내부에 포함시키는 릴리즈 빌드가 필요합니다.
딥링크 일관성부터 확인하세요. app.json의 scheme, 코드 안의 redirectTo, Supabase 대시보드의 주소가 셋 다 정확히 일치해야 합니다. 하나라도 어긋나면 되돌아오는 길이 끊깁니다. 다음으로 Google Cloud Console에서 Web Client ID를 생성해 Supabase에 등록했는지 점검하고, 외부 테스트를 할 때는 빌드 모드를 반드시 --configuration Release로 두세요.
6. 결론
React Native에서 Supabase와 구글 로그인을 연동하는 일은, 단순한 코드 작성을 넘어 네트워크 환경과 빌드 아키텍처에 대한 이해를 요구합니다. 딥링크의 왕복 구조와 디버그/릴리즈 빌드의 차이를 한 번 제대로 잡아두면, 이후 프로젝트는 훨씬 가볍고 빠르게 나아갑니다.