Android WebView에서 카카오 로그인 "앱으로 열기" 버튼이 안 보일 때 — User-Agent와 스킴 점검

Chrome에선 보이는 카카오톡 "앱으로 열기" 버튼이 Android WebView에선 안 보인다. 패키지 가시성(queries)·콜백 스킴·WebView User-Agent(wv 흔적)를 점검하고, 안 되면 네이티브 카카오 SDK로 우회하는 순서 정리.

TL;DR

  • Chrome 앱에선 accounts.kakao.com에 “카카오톡으로 열기” 버튼이 뜨는데, 앱 내부 WebView에선 계정 입력 화면만 나오고 그 버튼이 안 보였다.
  • 스킴 하나 빠진 문제로 보이지만, 실제로는 WebView User-Agent의 wv 흔적 때문에 카카오 페이지가 “앱 내 WebView”로 판단하고 UI를 다르게 주는 경우가 있다.
  • 점검 순서: com.kakao.talk 패키지 query → ② 콜백 스킴 등록 → ③ 카카오 인증 도메인에서 Chrome 유사 UA 사용(wv·Version/4.0·Build/... 제거) → ④ 그래도 안 되면 네이티브 카카오 SDK 로그인으로 우회.

같은 문제를 iOS(WKWebView)에서 다룬 글은 위의 “같은 주제 · 다른 플랫폼” 박스에서 볼 수 있다. iOS는 “요청 전에 UA를 확정”하는 게 핵심이었고, Android는 “UA에서 WebView 흔적을 지우는 것”이 핵심이다.

문제 상황

Android 앱에서 WebView로 카카오 로그인 페이지를 띄웠을 때, Chrome 앱에선 보이는 “카카오톡으로 열기” / “앱으로 열기” 버튼이 WebView에선 안 보이는 경우가 있다.

Chrome에서 accounts.kakao.com 로그인 페이지에 들어가면 카카오톡 앱으로 이어지는 버튼이 노출되는데, 앱 내부 WebView에선 계정 입력 로그인 화면만 나오고 카카오톡으로 전환하는 버튼이 나타나지 않는다.

처음엔 단순히 intent scheme 처리가 빠졌거나 AndroidManifest에 카카오 스킴이 누락된 문제처럼 보인다. 하지만 실제로는 WebView의 User-Agent 때문에 카카오 로그인 페이지가 “앱 내 WebView 환경”으로 판단하고 UI를 다르게 보여주는 케이스도 있다.

원인 후보 3가지

이 문제를 볼 때 확인할 지점은 크게 셋이다.

  1. 카카오톡 앱 패키지 조회 가능 여부 (Android 11+ 패키지 가시성)
  2. 카카오 로그인 콜백 스킴 등록 여부
  3. WebView User-Agent가 Chrome처럼 보이는지 여부

1·2: 패키지 가시성과 콜백 스킴

Android 11(API 30) 이상에서는 패키지 가시성 제한이 있어서, 외부 앱을 열거나 설치 여부를 확인하려면 queries 선언이 필요하다.

<queries>
    <package android:name="com.kakao.talk" />
</queries>

카카오 SDK 로그인을 쓴다면 콜백 스킴도 필요하다.

<activity
    android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data
            android:host="oauth"
            android:scheme="@string/kakao_scheme" />
    </intent-filter>
</activity>

여기서 kakao_scheme은 보통 이런 형태다.

<string name="kakao_scheme">kakao{NATIVE_APP_KEY}</string>

이게 다 돼 있는데도 버튼이 안 보인다면, 다음으로 봐야 할 게 User-Agent다.

WebView User-Agent 문제

Android WebView의 기본 User-Agent에는 보통 WebView임을 나타내는 흔적이 들어간다. 대표적으로:

  • ; wv — WebView를 가리키는 토큰
  • Version/4.0
  • Build/...

카카오 로그인 페이지는 브라우저와 WebView를 다르게 취급할 수 있다. Chrome에선 카카오톡 열기 버튼을 보여주지만, WebView로 판단되면 계정 로그인 화면만 보여줄 수 있다.

따라서 카카오 로그인 도메인에 진입할 때는 WebView 기본 UA가 아니라 Chrome에 가까운 UA로 바꿔주는 방식이 필요할 수 있다.

기존 접근

기존 코드에선 Google/Facebook OAuth URL에 대해서만 UA를 fake UA로 바꾸고 있었다. 보통 WebViewClient.shouldOverrideUrlLoading 안에서 이런 식이다.

if (isGoogleOauthUrl(url)) {
    viewSet.setUserAgentString(userAgent);
    view.loadUrl(url);
    return true;
}

if (isFacebookOauthUrl(url)) {
    viewSet.setUserAgentString(userAgent);
    view.loadUrl(url);
    return true;
}

iOS편과 같은 구조다. UA를 먼저 세팅하고 loadUrl로 다시 로드한 뒤 return true로 원래 로딩을 가로챈다. “UA 설정 → 재로드” 순서가 포인트.

여기에 카카오 계정 도메인을 추가해야 한다. 처음엔 accounts.kakao.com만 보면 될 것 같지만, 실제 인증 흐름에선 앞단에 kauth.kakao.com이 등장할 수 있다. 그래서 둘 다 포함하는 게 안전하다.

KAKAO_AUTH(Pattern.compile("(accounts|kauth)\\.kakao\\.com"));

해당 URL이면 Chrome처럼 정리한 UA를 적용한다.

if (isKakaoAuthUrl(url)) {
    String fakeUserAgent = userAgent;
    viewSet.setUserAgentString(fakeUserAgent);
    view.loadUrl(url);
    return true;
}

User-Agent를 더 Chrome답게 만들기

단순히 wv만 지우는 것으로는 부족할 수 있다. Version/4.0, Build/... 같은 흔적이 남아 있으면 여전히 WebView로 판별될 가능성이 있다.

그래서 WebView 기본 UA를 이렇게 정리했다.

String originalUA = mWebView.getSettings().getUserAgentString();

userAgent = originalUA
        .replaceAll(";\\s*wv\\)", ")")
        .replaceAll("\\sBuild/[^;)]*", "")
        .replaceAll("\\sVersion/[0-9.]+", "")
        .replaceAll(";\\s+", "; ")
        .replaceAll("\\s{2,}", " ")
        .trim();

핵심은 세 가지 제거다.

  • ; wv 제거 (WebView 표식)
  • Build/... 제거
  • Version/4.0 제거

이렇게 하면 WebView 기본 UA가 일반 Chrome 브라우저 UA에 한층 가까워진다.

주의: UA를 통째로 가짜 문자열로 박아넣기보다, 기기의 실제 UA에서 WebView 흔적만 걷어내는 편이 안전하다. OS·Chromium 버전이 올라가도 자연스럽게 따라가고, 서버가 보는 디바이스 정보도 보존된다.

Manifest 보강 (패키지 가시성)

스킴이 완전히 누락된 상태가 아니더라도, Android 11+ 패키지 가시성 이슈를 줄이려면 카카오 관련 intent query도 명시할 수 있다.

<queries>
    <package android:name="com.kakao.talk" />

    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="kakaokompassauth" />
    </intent>

    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="@string/kakao_scheme" />
    </intent>
</queries>

이 설정은 “카카오톡 앱을 열 수 있는지”, “해당 스킴을 처리할 앱이 있는지” 확인하는 과정에서 Android 시스템 제한에 걸릴 가능성을 줄여준다.

Google/Facebook 사이드 이펙트는?

카카오 대응을 넣을 때 Google/Facebook 로그인에 영향이 가는지도 확인해야 한다.

이번 변경에서 Google/Facebook의 URL 조건과 분기 자체는 건드리지 않았다.

  • Google: accounts.google.com
  • Facebook: facebook.com/.../dialog/oauth
  • Kakao: accounts.kakao.com 또는 kauth.kakao.com

URL 분기 관점에선 Google/Facebook에 직접적인 영향은 거의 없다. 다만 공통으로 쓰는 userAgent 값을 더 Chrome답게 정리했기 때문에, Google/Facebook OAuth에도 WebView 흔적이 덜 남은 UA가 적용된다. 일반적으로 OAuth 페이지에선 오히려 유리한 방향이다.

그래도 최대한 보수적으로 가고 싶다면 UA를 둘로 분리하는 방법도 있다.

private String userAgent = "";
private String browserLikeUserAgent = "";

Google/Facebook은 기존 userAgent를, 카카오만 browserLikeUserAgent를 쓰게 하면, 카카오 대응이 다른 소셜 로그인에 주는 영향 범위를 더 줄일 수 있다.

그래도 버튼이 안 뜬다면: 네이티브 SDK로 우회

여기까지 했는데도 WebView에서 카카오톡 열기 버튼이 안 보일 수 있다. 그 경우는 앱 코드의 스킴 누락보다, 카카오 페이지 자체가 WebView 환경을 강하게 감지해서 버튼을 숨기는 정책일 가능성이 있다.

이럴 땐 WebView 안의 버튼 노출에 의존하지 말고, 카카오 OAuth 진입을 감지했을 때 네이티브 카카오 SDK 로그인을 직접 호출하는 게 확실하다.

UserApiClient.getInstance().loginWithKakaoTalk(...)

또는 fallback으로:

UserApiClient.getInstance().loginWithKakaoAccount(...)

WebView에서 카카오 로그인 URL을 만났을 때, 웹 페이지를 계속 로드하는 대신 네이티브 로그인 플로우로 우회하는 구조를 고려할 수 있다. WebView UI 정책 변화에 흔들리지 않는 가장 안정적인 길이다.

정리

Android WebView에서 카카오 로그인의 “앱으로 열기” 버튼이 안 보이는 문제는 스킴 하나 빠진 문제만은 아니다. 점검 순서는 이렇게 가는 게 좋다.

  1. com.kakao.talk 패키지 query가 있는지 확인
  2. kakao{native_key}://oauth 콜백 스킴이 등록돼 있는지 확인
  3. accounts.kakao.com, kauth.kakao.com에서 Chrome 유사 User-Agent를 쓰는지 확인
  4. UA에서 wv, Version/4.0, Build/... 같은 WebView 흔적을 제거했는지 확인
  5. 그래도 안 되면 WebView 버튼에 의존하지 말고 네이티브 카카오 SDK 로그인으로 우회

결론적으로 WebView에서 외부 앱 로그인을 다룰 땐 “스킴 처리”와 “User-Agent 판별”을 함께 봐야 한다. Chrome에선 보이는데 WebView에선 안 보이는 UI라면, 서버나 페이지 스크립트가 User-Agent를 기준으로 화면을 다르게 구성하고 있을 가능성을 반드시 의심해봐야 한다.