DEEP DEBUG
웹뷰에서 카카오/구글 OAuth가 안 되는 이유, 구조부터 짚어드립니다
"PC에선 잘 되는데 앱에서만 흰 화면이 떠요"라는 문의가 정말 많아요. 웹뷰 앱 만들 때 가장 자주 부딪히는 문제 중 하나입니다. 단순히 코드 추가의 문제가 아니라 OAuth 표준과 모바일 OS의 충돌이라는 구조적 이슈예요. 본질부터 짚으면 해결도 쉬워집니다.
결론을 미리 말씀드리면, 이 문제의 근본 원인은 "웹뷰는 외부 앱(카카오톡)을 직접 실행할 권한이 없다"는 데 있습니다. PC 브라우저는 이걸 자동 처리하지만, 모바일 웹뷰는 명시적으로 코드를 추가해야 처리돼요. 그래서 PC는 되는데 앱은 안 되는 거예요.
또한 구글은 2021년부터 보안 이유로 웹뷰의 OAuth 사용을 공식적으로 제한합니다. 즉, 카카오와 구글은 다른 차원의 문제예요. 카카오는 처리 코드 추가로 해결되지만, 구글은 Chrome Custom Tabs로 우회해야 합니다. 이 차이를 모르고 같은 방식으로 접근하면 구글 로그인은 절대 작동 안 해요.
먼저 OAuth 흐름이 웹뷰에서 어떻게 깨지는지
PC 브라우저와 모바일 웹뷰의 OAuth 흐름 차이를 보면 문제의 본질이 보입니다. 같은 OAuth 표준인데 왜 모바일에서만 깨지는지 이해되실 거예요.
OAuth 흐름의 차이
PC 브라우저 ✓
1. 카카오 로그인 버튼 클릭
↓
2. 카카오 인증 페이지 호출
↓
3. 인가코드 → redirect URI 리턴
↓
로그인 성공
모바일 웹뷰 ✗
1. 카카오 로그인 버튼 클릭
↓
2. intent:// URL 호출 시도
(카카오톡 앱 실행)
✗
웹뷰는 intent URI 처리 불가
웹뷰는 외부 앱 실행 권한 없음
→ 흰 화면
핵심은 2번 단계예요. 카카오 SDK는 카카오톡 앱을 실행하기 위해 intent:// URL scheme을 호출하는데, 웹뷰는 기본적으로 이런 URL을 처리하는 권한이 없습니다. 그래서 클릭해도 아무 일도 안 일어나거나 흰 화면만 떠요.
이걸 해결하려면 웹뷰의 URL 처리 로직을 직접 오버라이드해서 "intent:// URL이 오면 외부 앱을 실행하도록" 명시적으로 코드를 추가해야 합니다. 그래서 카카오는 공식 문서에서 두 가지 작업을 안내해요.
카카오 OAuth 해결법: 두 가지 작업
카카오 측에서 명시한 해결책은 "카카오톡 실행 처리"와 "팝업 웹뷰 처리" 두 가지를 모두 추가하는 것입니다. 둘 중 하나만 하면 부분적으로만 작동해요.
작업 1: 카카오톡 앱 실행 처리 (Android)
웹뷰의 shouldOverrideUrlLoading 메서드를 오버라이드해서, intent URI가 호출되면 직접 파싱해 카카오톡 앱을 실행해야 합니다.
// Android Kotlin 예시
override fun shouldOverrideUrlLoading(
view: WebView, request: WebResourceRequest
): Boolean {
val url = request.url.toString()
if (url.startsWith("intent://")) {
val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
view.context.startActivity(intent)
return true
}
return false
}
주의: 카카오톡이 설치 안 된 기기에서는 ActivityNotFoundException이 발생합니다. try-catch로 감싸서 미설치 시 카카오 계정 로그인 페이지로 fallback 처리 필수예요. 안 그러면 앱이 죽어요.
작업 2: 팝업 웹뷰 처리 (window.open)
카카오 JavaScript SDK는 보안 향상을 위해 일부 기능에서 팝업 윈도우를 사용합니다. 웹뷰가 window.open()을 처리하지 못하면 클릭해도 아무 일도 안 일어나요. 이게 두 번째 사고 지점입니다.
// 부모 웹뷰 설정
webView.settings.run {
javaScriptEnabled = true
javaScriptCanOpenWindowsAutomatically = true
setSupportMultipleWindows(true) // 핵심!
}
// onCreateWindow에서 자식 웹뷰 생성
override fun onCreateWindow(...): Boolean {
val childWebView = WebView(view.context)
// 부모와 동일한 설정 적용
// transport.webView = childWebView
// resultMsg.sendToTarget()
return true
}
iOS WKWebView 처리
iOS는 Android와 메서드 이름만 다를 뿐 처리 원리는 동일합니다. decidePolicyFor navigationAction에서 URL scheme 분기.
// iOS Swift 예시
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url else { return }
if url.scheme == "kakaotalk" || url.scheme == "kakaolink" {
UIApplication.shared.open(url)
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
구글 OAuth는 다릅니다: Chrome Custom Tabs로 우회
카카오는 위 처리로 해결되지만, 구글은 다른 차원의 문제입니다. 구글은 2016년부터 보안 이유로 웹뷰에서의 OAuth 사용을 공식적으로 제한해왔고, 2021년부터는 점차 차단을 강화하고 있어요.
⚠️ 구글의 공식 입장
구글 측 메시지: "WebView에서 인증에 OAuth를 사용하면 앱이 보안 문제에 취약해질 수 있으며, 싱글 사인온(SSO) 세션에서 사용자의 연결이 끊겨 사용성이 저하될 수 있습니다."
구글의 권고: Chrome Custom Tabs(Android) / SFSafariViewController(iOS)로 교체하세요.
Chrome Custom Tabs는 웹뷰가 아니라 진짜 Chrome 브라우저예요. 사용자의 기존 구글 로그인 세션을 그대로 활용할 수 있고, 보안 표준도 만족합니다. 화면 전환은 거의 자연스럽게 보이고요.
Android Custom Tabs 사용 예시:
// 구글 OAuth는 웹뷰 대신 Custom Tabs로
val customTabsIntent = CustomTabsIntent.Builder()
.setShowTitle(true)
.build()
customTabsIntent.launchUrl(context,
Uri.parse("https://accounts.google.com/o/oauth2/v2/auth?..."))
실전 디버깅: 흰 화면 만났을 때 체크 순서
실제로 트러블슈팅 할 때의 체크 순서입니다. 위에서부터 차례로 확인하세요. 5분 안에 원인 90%는 잡힙니다.
STEP 1
Chrome inspect로 웹뷰 디버깅 활성화
Android는 chrome://inspect로 실제 웹뷰 콘솔 로그 확인 가능. iOS는 Safari → 개발 메뉴. 흰 화면의 99%는 콘솔에 에러 메시지가 있어요. 콘솔 안 보고 추측하면 시간만 낭비됩니다.
STEP 2
Network 탭에서 redirect 흐름 추적
로그인 클릭 → 카카오 → redirect URI까지 어디서 끊기는지 확인. intent:// 호출 시점에서 멈췄다면 shouldOverrideUrlLoading 미구현. URL이 끊긴 지점 = 문제 발생 지점.
STEP 3
redirect URI가 카카오 콘솔에 등록됐는지
"KOE006" 에러 → redirect URI 미등록. 카카오 디벨로퍼스 콘솔의 카카오 로그인 → 리다이렉트 URI에 정확한 값 등록. 콘솔 등록 값과 코드의 redirect_uri가 한 글자도 다르면 안 됩니다.
STEP 4
window.open 처리가 됐는지 확인
팝업 화면 클릭 시 화면이 안 뜨면 setSupportMultipleWindows(true) 누락. Android는 onCreateWindow 오버라이드 필수.
STEP 5
User-Agent 확인 (구글의 경우)
"disallowed_useragent" 에러 → 구글이 웹뷰를 감지하고 차단한 것. Custom Tabs로 전환 외엔 우회 방법 없음.
자주 만나는 에러 코드 빠른 참조
실전에서 자주 만나는 에러 코드와 의미를 정리했어요. 에러 메시지에 이 코드가 보이면 즉시 해결 방향을 잡을 수 있어요.
KOE006
카카오
redirect URI 불일치 → 콘솔 등록 확인
KOE101
카카오
앱 키 오류 → REST API 키 확인
KOE237
카카오
요청 수 초과 → Rate limit 정책 확인
disallowed_useragent
구글
웹뷰에서 차단 → Custom Tabs 전환 필수
redirect_uri_mismatch
구글
콘솔 redirect URI 미등록 → GCP 콘솔 확인
access_denied
공통
사용자가 동의 거부 → 정상 흐름
웹뷰 OAuth의 근본적 대안: 네이티브 SDK 직접 사용
사실 가장 깔끔한 해결책은 웹뷰에서 OAuth를 처리하지 않고, 네이티브 SDK로 처리한 토큰을 웹뷰에 전달하는 방식입니다. 구조가 살짝 복잡해지지만 한 번 만들어두면 이후 OAuth 정책 변화에 흔들리지 않아요.
흐름:
1. 웹뷰 → 네이티브 (JavaScript Bridge로 "로그인 시작" 신호)
2. 네이티브 → 카카오 / 구글 SDK로 OAuth 처리
3. 네이티브가 access_token / 사용자 정보 받음
4. 네이티브 → 웹뷰 (토큰 전달, evaluateJavaScript)
5. 웹뷰가 토큰으로 자체 서버에 로그인 처리
장점: 웹뷰 OAuth의 모든 문제(intent URL, window.open, 구글 차단)에서 자유로움. 사용자 경험도 가장 자연스러움. 카카오톡이 설치된 사용자는 SSO도 가능.
단점: JavaScript Bridge 통신 코드 추가 필요. iOS / Android 양쪽 동일하게 구현해야 함. 기존 웹 코드 일부 수정 필요.
정리하며
웹뷰 OAuth 문제의 본질은 "브라우저는 자동으로 처리하지만 웹뷰는 명시적으로 처리해야 한다"는 점입니다. 이걸 알면 모든 트러블슈팅이 쉬워져요. PC에서 잘 되니까 코드 문제가 없을 거라고 생각하면 안 돼요. 모바일 웹뷰는 PC 브라우저와 다른 환경입니다.
제가 가장 강조하고 싶은 건 이거예요. 카카오는 처리, 구글은 우회입니다. 카카오는 위에서 설명한 두 가지 처리(intent 처리, window.open 처리)로 해결되지만, 구글은 Custom Tabs 외에는 답이 없어요. 같은 방식으로 접근하면 시간만 낭비합니다.
한 가지 팁을 더 드리면, 앱 시작부터 네이티브 SDK + JavaScript Bridge 구조로 가시면 위 모든 고민이 사라집니다. 처음에는 약간 복잡해 보이지만, 1년 후 OAuth 정책 변화에 흔들리지 않는 견고한 구조가 돼요. 웹뷰 + OAuth는 어차피 임시방편이라는 점, 인지하고 시작하시는 게 좋습니다.
WebView OAuth 트러블슈팅 · 시니어 개발자 가이드