2026년 7월 28일 · 읽는 데 약 8분

React Native 웹뷰에서 카카오톡 로그인 버튼이 안 보이는 조건

React Native 웹뷰에서 카카오톡 로그인 버튼이 안 보이는 조건

앱 코드를 아무리 뒤져도 이 버튼을 숨기는 조건문은 안 나옵니다. 조건문이 카카오 쪽에 있기 때문입니다. 카카오 로그인 페이지는 접속한 환경이 어떤 앱인지 보고, 지원 목록에 없으면 “카카오톡으로 로그인” 버튼을 뺀 화면을 내려줍니다. React Native 웹뷰는 그 목록에 없습니다.

판별 재료는 User-Agent, 웹뷰가 서버에 자신을 소개하는 문자열입니다. 웹뷰가 “저는 웹뷰입니다”라고 소개하는 한 버튼은 나오지 않습니다. 브라우저라고 소개하도록 바꾸면 나타나긴 하는데, 그건 절반의 해결입니다. 버튼이 보이는 것과, 눌러서 카카오톡을 오가는 것은 별개의 문제라서요. UA를 바꾼 뒤에는 카카오톡을 열고 다시 돌아오는 길도 직접 이어줘야 합니다. 확인은 10초면 됩니다.

10초 확인: 같은 주소를 크롬으로 열어봅니다

이 글이 다루는 건 웹뷰 안에서 카카오 로그인 페이지(kauth.kakao.com)를 직접 여는 웹 방식 연동입니다. 그 로그인 주소를 그대로 복사해 크롬이나 사파리에서 엽니다. 거기서 노란 버튼이 나온다면, 앱 코드보다 카카오의 노출 정책을 의심할 차례입니다. 카카오가 데브톡에서 안내한 노출 조건은 두 가지입니다. 기기의 카카오톡이 지원 버전(Android 9.1.0, iOS 9.0.0 이상)이어야 하고, 로그인 페이지를 연 앱이 Chrome·Safari 같은 기본 브라우저이거나 네이버 앱처럼 카카오가 지정한 앱이어야 합니다. 세부 목록은 데브톡 답변 시점에 따라 조금씩 다릅니다(공식 사양 문서는 따로 없습니다). 그래도 축은 같습니다. 기본 브라우저와 지정된 몇 개, 그리고 “그 외 앱의 웹뷰에서는 버튼이 표시되지 않습니다.”

이 문제가 유난히 오래 잡아먹는 이유는 에러가 없어서입니다. 버튼만 조용히 빠지고, 카카오계정 아이디와 비밀번호를 치는 로그인은 그대로 진행됩니다. 화면이 뜨고 로그인도 되니까 어딘가 내 코드가 스타일을 깨뜨렸나 싶어 CSS부터 뒤지게 됩니다. 에러 없이 돌아가는 문제가 에러 나는 문제보다 오래 걸립니다.

웹뷰는 스스로를 웹뷰라고 소개합니다

User-Agent는 HTTP 요청마다 붙어 가는 자기소개 문자열입니다. 안드로이드 웹뷰의 기본 문자열에는 wv라는 토큰이 박혀 있습니다.

Mozilla/5.0 (Linux; Android 10; K; wv) AppleWebKit/537.36
  (KHTML, like Gecko) Version/4.0 Chrome/125.0.0.0 Mobile Safari/537.36

같은 기기의 진짜 크롬에는 wv도, Version/4.0도 없습니다. 사이트가 웹뷰를 식별할 수 있도록 일부러 넣어둔 표식입니다. iOS는 반대 방향으로 티가 납니다. WKWebView의 기본 문자열에는 사파리라면 붙는 Version/…과 끝의 Safari/… 토큰이 빠져 있습니다. React Native의 WebView는 이 기본 문자열을 그대로 씁니다. 카카오 로그인 페이지 입장에서 이 웹뷰는 첫 요청부터 “기본 브라우저 아님”을 밝히고 들어오는 셈입니다.

구글 로그인의 웹뷰 문제와는 양상이 다릅니다. 구글은 웹뷰에서의 OAuth 로그인 자체를 보안 정책으로 금지해, 요청을 대개 403: disallowed_useragent 에러로 차단합니다. 에러 문자열이 있으니 검색하면 바로 원인이 나옵니다. 카카오는 막는 게 아니라 가립니다. 로그인은 되게 두고 버튼만 빼니, 검색할 에러 문자열조차 없습니다. 이게 카카오 쪽 문제가 더 오래 걸리는 이유입니다.

우회: UA를 브라우저로 소개하기 — 단, 이건 절반입니다

react-native-webview에는 웹뷰의 소개 문자열을 통째로 바꾸는 userAgent prop이 있습니다(기존 문자열 뒤에 앱 이름만 덧붙이는 applicationNameForUserAgent도 있습니다 — 통계 식별용이라면 이쪽이 낫습니다). 여기에 실제 브라우저의 문자열을 넣으면 카카오 로그인 페이지는 기본 브라우저가 왔다고 판단하고 버튼을 내려줍니다. 공식 지원 방식이 아니라, 판별을 속이는 우회입니다.

<WebView
  userAgent={
    Platform.OS === 'ios'
      ? 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1'
      : 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.7444.172 Mobile Safari/537.36'
  }
/>

이 prop 옆에는 왜 넣었는지 주석을 꼭 남겨두세요. 몇 달 뒤에 보면 웬 하드코딩 문자열인지 아무도 모릅니다. 저는 “카카오 로그인 버튼 때문”이라는 주석 한 줄 덕에, 리팩터링 때 이 코드가 지워지는 걸 막은 적이 있습니다.

이 우회에는 유지비가 있습니다. 문자열은 실제 사파리·크롬에서 채집된 예시인데, 브라우저 버전은 몇 주 단위로 올라가니 하드코딩한 UA는 낡습니다. 최신 문자열을 정리해 주는 사이트(whatismybrowser의 latest user agent 페이지 등)에서 이따금 갱신해야 합니다. 바뀐 문자열은 그 웹뷰로 여는 모든 사이트에 가서, 접속 통계가 브라우저로 잡히고 UA로 웹뷰를 감지하던 다른 분기도 함께 바뀝니다. 그리고 구글 로그인에 이 방법을 쓰는 건 접어두세요. 구글이 금지하는 건 웹뷰에서 OAuth를 수행하는 것 자체라, UA를 위장해도 웹뷰라는 사실이 바뀌지 않습니다. 정책 위반이고 언제 막혀도 이상하지 않습니다.

이 우회로 지금은 버튼이 나오지만, 노출 설정은 카카오 서버 쪽 값이라 바뀌면 그만입니다. 그리고 데브톡에는 버튼이 나온 뒤의 질문이 또 이어집니다. UA를 바꿨더니 이번에는 “버튼 클릭 시 아무 액션이 없는 상태”가 됐다는 겁니다.

나머지 절반: 버튼이 보이는 것과 눌리는 것은 다릅니다

“카카오톡으로 로그인”의 실체는 앱 전환입니다. 버튼을 누르면 페이지가 카카오톡 앱을 깨우고, 카카오톡에서 동의를 받은 뒤 다시 원래 화면으로 돌아오는 왕복입니다. 브라우저는 이 왕복을 알아서 처리해 주지만, 웹뷰에서는 그 처리를 앱이 맡아야 합니다. 필요한 일은 셋입니다. 카카오톡으로 향하는 요청을 웹뷰에서 가로채고, 웹뷰의 로딩을 멈추고, 그 주소를 운영체제에 넘겨 카카오톡을 엽니다.

플랫폼별로 보면 이렇습니다. 안드로이드에서 앱을 깨우는 주소는 intent:// 형식이라, 가로챈 뒤 주소에 담긴 실행 정보를 꺼내(파싱해) 카카오톡을 실행해야 합니다. Android 11 이상이라면 매니페스트에 카카오톡 패키지명도 등록합니다. iOS에서는 커스텀 URL 스킴 — kakaotalk://처럼 특정 앱을 여는 주소 형식 — 요청을 웹뷰가 아닌 시스템으로 넘깁니다. 카카오의 하이브리드 앱 가이드가 이 처리들을 코드 예시와 함께 나열합니다. JavaScript SDK 기준의 문서지만 웹뷰가 맡을 역할을 파악하기에는 충분하고, JS SDK의 팝업 방식 로그인까지 쓴다면 팝업 창(window.open)을 새 웹뷰로 만들고 닫는 처리도 더해집니다.

React Native에서 가로채기는 onShouldStartLoadWithRequest가 맡습니다. 카카오톡 실행에 쓰이는 스킴들을 허용 목록으로 정해두고, 그 요청이 오면 false를 돌려줘 웹뷰의 로딩을 끊은 뒤 시스템 쪽으로 넘기는 구조입니다. 흐름은 단순한데 플랫폼별 예외가 적지 않습니다. 어떤 스킴이 오는지는 연동 방식과 버전에 따라 달라 실제 요청 URL을 관찰해 목록을 잡아야 하고, 안드로이드의 intent: 주소는 Linking.openURL에 그대로 넘기는 방식을 신뢰할 수 없어 네이티브에서 파싱해야 하며, 카카오톡이 설치되지 않은 기기 처리 같은 곁가지도 있습니다. 이 연결 코드를 다 깔 게 아니라면, 다음 방법이 남습니다.

정공법: 로그인만 웹뷰 밖으로

오래 가져갈 구조라면 로그인을 웹뷰 안에서 해결하지 않는 편이 단순합니다. 카카오도 데브톡에서 UA 수정보다는 가이드를 마련하겠다는 쪽에 섰고요. 카카오 네이티브 SDK의 React Native 래퍼(커뮤니티 라이브러리 @react-native-seoul/kakao-login 등)로 앱에서 로그인해 로그인 성공을 증명하는 토큰을 받고, 그 토큰을 서버로 보내 카카오 API로 유효성을 확인한 뒤, 서버가 발급한 자체 세션으로 웹뷰를 여는 방식입니다. 앱 전환 왕복은 SDK가 통째로 책임지니 로그인 왕복에 필요하던 연결 코드는 사라지고, UA는 원래대로 정직하게 둘 수 있습니다.

정리하면 선택지는 둘입니다. 웹 로그인 페이지를 계속 쓸 거면 UA로 버튼을 띄우고 스킴 처리까지 마저 깔 것. 그게 아니면 로그인을 네이티브로 빼서 왕복 자체를 웹뷰 밖으로 옮길 것. 어느 쪽이든 진단의 출발점은 같습니다. 콘솔이 조용한 문제일수록 판별이 일어나는 곳부터 봐야 하고 — 안드로이드에서만 폰트가 다르게 나오던 문제도 그랬습니다 — 이번 판별은 카카오 서버에서 일어납니다. 버튼이 없는 화면은 고장이 아니라 대답입니다. 웹뷰가 첫 요청에서 자신을 뭐라고 소개했는지에 대한 대답.


여백 — 위 UA 문자열이 하나같이 Mozilla/5.0으로 시작하는 게 이상하지 않았나요. 크롬도 사파리도 모질라가 아닌데요. 90년대 브라우저 전쟁의 유산입니다. 당시 서버들이 넷스케이프(코드명 Mozilla)에만 고급 페이지를 내려주자 다른 브라우저들이 전부 “Mozilla인 척”을 시작했고, 그 위장이 30년째 표준처럼 굳었습니다. 서로가 서로인 척하는 문자열로 신원을 판별하는 셈이니, 애초에 튼튼할 수 없는 구조 위에 다들 분기를 쌓아온 겁니다.

참고 — 카카오톡 로그인 버튼 노출 기준 (카카오 데브톡 공식 답변), 하이브리드 앱 가이드 (Kakao Developers)

이 글이 울렸다면 눌러 주세요

댓글

  • 불러오는 중…

← 전체 글 목록