ERR_UNKNOWN_URL_SCHEME

react native 프로젝트 내부 App.tsx에 WebView로 배포되어있는 웹 사이트를 띄운 즉 패키징 프로젝트 입니다.
다름이 아니라, 제가 카카오톡 로그인 기능을 구현하려는 와중, IOS에서는
// if (!window.Kakao) {
// console.error(‘Kakao SDK not found’);
// return;
// }

// if (!window.Kakao.isInitialized()) {
// window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID);
// }

// window.Kakao.Auth.authorize({
// redirectUri: process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI!,
// throughTalk: true,
// });

이렇게 로그인을 구현했을 때 정상적으로 동작을 하는데,
ANDROID 에서는, ERR_UNKNOWN_URL_SCHEME 라는 에러가 발생을 합니다.
혹시 도움을 좀 받을 수 있을까요?

안드로이드에서 ERR_UNKNOWN_URL_SCHEME 에러가 발생하는 것은 인텐트 스킴을 오픈하지 못하는 문제로 보입니다. 이는 웹뷰에서 카카오톡 앱을 실행하려고 할 때 발생하는 일반적인 이슈입니다.

해결 방법은 다음과 같습니다:

  1. AndroidManifest에 카카오톡 패키지명 선언: 안드로이드 앱의 AndroidManifest.xml 파일에 카카오톡 패키지명을 선언해야 합니다.
<manifest package="com.example.sample">
    <queries>
        <package android:name="com.kakao.talk" />
    </queries>
    ...
</manifest>
  1. 웹뷰의 shouldOverrideUrlLoading 메소드 구현: 웹뷰에서 인텐트 스킴 URL을 처리할 수 있도록 shouldOverrideUrlLoading 메소드를 구현해야 합니다.

리액트 네이티브 프로젝트에서는 직접 네이티브 코드를 수정할 수 없으므로, react-native-webview 라이브러리의 커스텀 안드로이드 구현을 참고하여 조치해야 합니다.

이 두 가지 방법을 통해 안드로이드에서 카카오톡 로그인 기능을 정상적으로 동작시킬 수 있을 것입니다.

추가적인 도움이 필요하시면 언제든지 문의해주세요.

[ @tim.l @woody.ho ]

답변 감사드립니다.
혹시 한 가지 궁금한 점이, 맨 처음 로그인 시에는 아이디와 비밀번호를 요구하고 그 이후에는 요구를 안하는 것이 맞나요?

네, 브라우저에 카카오계정 로그인 세션이 유지지되는 동안은 요구하지 않습니다.

카카오계정과 함께 로그아웃 또는 항상 ID/PW 입력받기 옵션도 있습니다.

export const kakaoLogin = () => {
// console.log(‘카카오 로그인 실행됨’);
if (typeof window === ‘undefined’) return;

if (!window.Kakao) {
console.error(‘Kakao SDK not found’);
return;
}

if (!window.Kakao.isInitialized()) {
window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID);
}

window.Kakao.Auth.authorize({
redirectUri: process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI!,
throughTalk: true,
});
};
제가 웹단에서 이렇게 사용 중에 있는데요,

react native
android 하위에 src > main > …
MainApplication.java 파일들과 같은 선상에 CustomWebViewClient, Manager, Package.java 파일들을 생성하여 사용중에 있는데,
카카오톡이 열리지 않네요…

인텐트 스킴 호출 할 수 있도록 하이브리드앱 처리 하셨을까요?

public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    String url = request.getUrl().toString();
    Log.d(TAG, "URL intercepted: " + url);
    return handleCustomScheme(view, url);
}

이런식으로 사용했습니다

AndroidManifest.xml에도 queries 에 package 추가헀구요…

react native 프로젝트 라고 하셨는데 해당 로직이 실행되나요?

제가 구현한 방식은 CustomWebView를 생성한 것인데,
Android에서만 react-native-webview 대신 CustomWebView를 감싼 형식으로 진행을 했습니다

Log.d(TAG, "URL intercepted: " + url); 로그가 찍히던가요?

URL intercepted: https://jangpa.co.kr/kakao/callback?error=timeout&error_description=LOGIN_TIMEOUT

URL intercepted: intent:#Intent;action=com.kakao.talk.intent.action.CAPRI_LOGGED_IN_ACTIVITY;launchFlags=0x08880000;S.com.kakao.sdk.talk.appKey=(앱키);S.com.kakao.sdk.talk.redirectUri=https://jangpa.co.kr/kakao/callback;S.com.kakao.sdk.talk.kaHeader=sdk/1.43.6%20os/javascript%20sdk_type/javascript%20lang/ko-KR%20device/Linux_aarch64%20origin/https%3A%2F%2Fjangpa.co.kr;S.com.kakao.sdk.talk.extraparams={"client_id"%3A"7724d6e88271e6c967ade56d235965ba"%2C"redirect_uri"%3A"https%3A%2F%2Fjangpa.co.kr%2Fkakao%2Fcallback"%2C"response_type"%3A"code"%2C"auth_tran_id"%3A"l89ai0g0dp7724d6e88271e6c967ade56d235965bamcydcca2"%2C"is_popup"%3Atrue};S.browser_fallback_url=https%3A%2F%2Fkauth.kakao.com%2Foauth%2Fauthorize%3Fclient_id%3D7724d6e88271e6c967ade56d235965ba%26redirect_uri%3Dhttps%253A%252F%252Fjangpa.co.kr%252Fkakao%252Fcallback%26response_type%3Dcode%26auth_tran_id%3Dl89ai0g0dp7724d6e88271e6c967ade56d235965bamcydcca2%26ka%3Dsdk%252F1.43.6%2520os%252Fjavascript%2520sdk_type%252Fjavascript%2520lang%252Fko-KR%2520device%252FLinux_aarch64%2520origin%252Fhttps%25253A%25252F%25252Fjangpa.co.kr%26is_popup%3Dfalse;end;

react-native-webview 대신 CustomWebView를 감싼 형식으로 진행

위내용에 대해 어떤 방식인지 잘 이해하지 못했는데요.
로그가 찍힌다고 하셨으니 후킹은 되는 상황으로 이해 했습니다.

앱 호출이 실패하면 S.browser_fallback_url로 이동하도록 구현 하는데요.

https://developer.chrome.com/docs/multidevice/android/intents/

intent의 S.browser_fallback_url에 대한 파싱을 구현하셨을까요?

https://developers.kakao.com/docs/latest/ko/javascript/hybrid#android-execute-kakaotalk

테스트한 기기에 카카오톡은 설치 및 로그인되어 있는 상태인가요?

넵! 카카오톡 앱은 설치되어있습니다!

import React, {forwardRef} from ‘react’;
import {Platform, requireNativeComponent, ViewStyle} from ‘react-native’;
import {WebView, WebViewProps} from ‘react-native-webview’;

interface CustomWebViewProps {
ref?: any;
source?: {
uri?: string;
html?: string;
};
style?: ViewStyle | ViewStyle;
onLoad?: () => void;
bounces?: boolean;
onMessage?: (event: {nativeEvent: {data: string}}) => void;
javaScriptEnabled?: boolean;
domStorageEnabled?: boolean;
sharedCookiesEnabled?: boolean;
allowsInlineMediaPlayback?: boolean;
mediaPlaybackRequiresUserAction?: boolean;
onNavigationStateChange?: (navState: any) => void;
allowFileAccess?: boolean;
allowUniversalAccessFromFileURLs?: boolean;
mixedContentMode?: ‘never’ | ‘always’ | ‘compatibility’;
originWhitelist?: string;
onShouldStartLoadWithRequest?: (event: {url: string}) => boolean;
injectedJavaScript?: string;
}

const CustomWebViewNative =
requireNativeComponent(‘MyCustomWebView’);

export const CustomWebView = forwardRef<any, CustomWebViewProps>(
(props, ref) => {
if (Platform.OS === ‘android’) {
const androidProps = {
ref,
source: props.source,
style: props.style,
javaScriptEnabled: props.javaScriptEnabled,
domStorageEnabled: props.domStorageEnabled,
allowFileAccess: props.allowFileAccess,
allowUniversalAccessFromFileURLs:
props.allowUniversalAccessFromFileURLs,
mixedContentMode: props.mixedContentMode,
mediaPlaybackRequiresUserAction: props.mediaPlaybackRequiresUserAction,
injectedJavaScript: props.injectedJavaScript,
};

  return <CustomWebViewNative {...androidProps} />;
}

return <WebView ref={ref} {...(props as WebViewProps)} />;

},
);

CustomWebView.displayName = ‘CustomWebView’;

export default CustomWebView;
이렇게 구현해두고,
로 사용중이였습니다

handleCustomScheme 구현부와 로그 공유해주시겠어요?

private boolean handleCustomScheme(WebView view, String url) {
    Log.d(TAG, "🔍 Handling URL: " + url);
    
    // Intent 스킴 처리 (가장 먼저 확인)
    if (url.startsWith("intent:")) {
        Log.d(TAG, "🟡 Intent scheme detected: " + url);
        return handleIntentScheme(view, url);
    }
    
    // 카카오톡 관련 URL 처리
    if (url.startsWith("kakaotalk://") || url.startsWith("kakaokompass://")) {
        Log.d(TAG, "🟢 Kakao app scheme detected: " + url);
        return launchExternalApp(view, url);
    }
    
    // 기타 커스텀 스킴 처리
    if (url.startsWith("kakao:") || url.startsWith("talk:")) {
        Log.d(TAG, "🟠 Custom kakao scheme detected: " + url);
        return launchExternalApp(view, url);
    }
    
    // 카카오 OAuth URL 처리
    if (url.contains("kauth.kakao.com")) {
        Log.d(TAG, "🔵 Kakao OAuth URL detected: " + url);
        return false; // WebView에서 처리하여 OAuth 진행
    }
    
    // HTTP/HTTPS는 WebView에서 처리
    if (url.startsWith("http://") || url.startsWith("https://")) {
        Log.d(TAG, "🌐 HTTP/HTTPS URL - loading in WebView: " + url);
        return false; // WebView에서 처리
    }
    
    Log.d(TAG, "❓ Unknown scheme, attempting external launch: " + url);
    return launchExternalApp(view, url);
}

URL intercepted: intent:#Intent;action=com.kakao.talk.intent.action.CAPRI_LOGGED_IN_ACTIVITY;launchFlags=0x08880000;S.com.kakao.sdk.talk.appKey=(앱키 주석하겠니
다);S.com.kakao.sdk.talk.redirectUri=https://jangpa.co.kr/kakao/callback;S.com.kakao.sdk.talk.kaHeader=sdk/1.43.6%20os/javascript%20sdk_type/javascript%20lang/ko-KR%20device/Linux_aarch64%20origin/https%3A%2F%2Fjangpa.co.kr;S.com.kakao.sdk.talk.extraparams={"client_id"%3A"7724d6e88271e6c967ade56d235965ba"%2C"redirect_uri"%3A"https%3A%2F%2Fjangpa.co.kr%2Fkakao%2Fcallback"%2C"response_type"%3A"code"%2C"auth_tran_id"%3A"y2be5wvurq7724d6e88271e6c967ade56d235965bamcyeqfsa"%2C"is_popup"%3Atrue};S.browser_fallback_url=https%3A%2F%2Fkauth.kakao.com%2Foauth%2Fauthorize%3Fclient_id%3D7724d6e88271e6c967ade56d235965ba%26redirect_uri%3Dhttps%253A%252F%252Fjangpa.co.kr%252Fkakao%252Fcallback%26response_type%3Dcode%26auth_tran_id%3Dy2be5wvurq7724d6e88271e6c967ade56d235965bamcyeqfsa%26ka%3Dsdk%252F1.43.6%2520os%252Fjavascript%2520sdk_type%252Fjavascript%2520lang%252Fko-KR%2520device%252FLinux_aarch64%2520origin%252Fhttps%25253A%25252F%25252Fjangpa.co.kr%26is_popup%3Dfalse;end;


handleCustomScheme 여기서 문제 있을거라 생각했는데…

Log.d(TAG, ":mag: Handling URL: " + url); 여기 수행 안되었나요?

:mag: Handling URL: intent:#Intent;action=com.kakao.talk.intent.action.CAPRI_LOGGED_IN_ACTIVITY;launchFlags=0x08880000;S.com.kakao.sdk.talk.appKey=(앱 키 주석입니다);S.com.kakao.sdk.talk.redirectUri=https://jangpa.co.kr/kakao/callback;S.com.kakao.sdk.talk.kaHeader=sdk/1.43.6%20os/javascript%20sdk_type/javascript%20lang/ko-KR%20device/Linux_aarch64%20origin/https%3A%2F%2Fjangpa.co.kr;S.com.kakao.sdk.talk.extraparams={"client_id"%3A"7724d6e88271e6c967ade56d235965ba"%2C"redirect_uri"%3A"https%3A%2F%2Fjangpa.co.kr%2Fkakao%2Fcallback"%2C"response_type"%3A"code"%2C"auth_tran_id"%3A"y2be5wvurq7724d6e88271e6c967ade56d235965bamcyeqfsa"%2C"is_popup"%3Atrue};S.browser_fallback_url=https%3A%2F%2Fkauth.kakao.com%2Foauth%2Fauthorize%3Fclient_id%3D7724d6e88271e6c967ade56d235965ba%26redirect_uri%3Dhttps%253A%252F%252Fjangpa.co.kr%252Fkakao%252Fcallback%26response_type%3Dcode%26auth_tran_id%3Dy2be5wvurq7724d6e88271e6c967ade56d235965bamcyeqfsa%26ka%3Dsdk%252F1.43.6%2520os%252Fjavascript%2520sdk_type%252Fjavascript%2520lang%252Fko-KR%2520device%252FLinux_aarch64%2520origin%252Fhttps%25253A%25252F%25252Fjangpa.co.kr%26is_popup%3Dfalse;end;

그다음 아래 로직 수행 안되었나요?

// Intent 스킴 처리 (가장 먼저 확인)
if (url.startsWith(“intent:”)) {
Log.d(TAG, ":yellow_circle: Intent scheme detected: " + url);
return handleIntentScheme(view, url);
}