본문 바로가기
프로그래밍/프론트엔드

[React] 사용자 이탈을 0%로: 인앱 브라우저 감지 및 리다이렉트 구현 여정

by me_in_sk 2026. 1. 20.
반응형

카카오톡, 에브리타임 등 인앱 브라우저에서 Google OAuth 로그인이 차단되는 문제를 분석하고, 앱별 URL 스킴을 활용한 외부 브라우저 리다이렉트 솔루션을 구현한 과정을 공유합니다.

 

📑 바로가기


🎯 문제 정의: 왜 카카오톡에서 구글 로그인이 안 될까?

대학 커뮤니티 에브리타임이나 카카오톡에서 공유된 링크를 클릭하면, 해당 앱 내부의 인앱 브라우저(In-App Browser)에서 웹페이지가 열립니다. 문제는 이 환경에서 Google OAuth 로그인이 완전히 차단된다는 것입니다.

사용자가 "Google로 계속하기" 버튼을 누르면, 다음과 같은 에러 메시지가 표시됩니다:

"403 disallowed_useragent"
이 브라우저 또는 앱이 안전하지 않을 수 있습니다.

강냉봇 서비스에서 이 문제는 심각했습니다. 대학생들은 주로 에브리타임이나 카카오톡을 통해 서비스 링크를 공유하는데, 해당 경로로 접근한 사용자들이 로그인 자체를 할 수 없었기 때문입니다.


🔍 원인 분석: Google의 보안 정책

2021년, Google의 결정

Google은 2021년 9월부터 임베디드 웹뷰(WebView)에서의 OAuth 로그인을 전면 차단했습니다. 이는 보안상의 이유 때문입니다.

🔒 WebView가 위험한 이유

  • 키로깅 위험: 호스트 앱이 JavaScript를 주입하여 사용자 입력(비밀번호 등)을 가로챌 수 있음
  • 중간자 공격: 앱이 Google과 사용자 사이의 통신을 변조할 수 있음
  • 세션 탈취: 인증 후 발급된 토큰을 악의적인 앱이 가로챌 수 있음
  • SSO 불가: 시스템 브라우저의 기존 로그인 세션을 활용할 수 없음

인앱 브라우저 감지 방법

각 앱의 인앱 브라우저는 고유한 User-Agent 문자열을 가지고 있습니다. 이를 분석하여 어떤 앱에서 접근했는지 식별할 수 있습니다.

로그인 페이지 브라우저 감지 예시
로그인 페이지 브라우저 감지 예시
// browserDetect.ts - User-Agent 기반 인앱 브라우저 감지
const inAppBrowserPatterns = [
  { pattern: /KAKAOTALK/i, name: 'KakaoTalk' },
  { pattern: /everytime/i, name: 'Everytime' },
  { pattern: /NAVER\(inapp|NAVER\//i, name: 'Naver' },
  { pattern: /Instagram/i, name: 'Instagram' },
  { pattern: /FBAN|FBAV|FB_IAB/i, name: 'Facebook' },
  { pattern: /Line\//i, name: 'LINE' },
  { pattern: /Twitter|X-Twitter/i, name: 'X (Twitter)' },
  { pattern: /; wv\)/i, name: 'Android WebView' },
];

export const detectInAppBrowser = () => {
  const ua = navigator.userAgent;
  
  for (const { pattern, name } of inAppBrowserPatterns) {
    if (pattern.test(ua)) {
      return { isInAppBrowser: true, browserName: name };
    }
  }
  
  // iOS WebView: Safari 식별자가 없는 경우
  if (/iPhone|iPad/.test(ua) && !/Safari\//.test(ua)) {
    return { isInAppBrowser: true, browserName: 'iOS WebView' };
  }
  
  return { isInAppBrowser: false, browserName: null };
};

🔥 기술적 도전과제

1. 플랫폼별 외부 브라우저 열기 방법의 차이

가장 큰 난관은 Android와 iOS의 동작 방식이 완전히 다르다는 점이었습니다. 더 나아가, 같은 iOS라도 앱마다 지원하는 URL 스킴이 달랐습니다.

 플랫폼  방법  문제점
 Android  Intent 스킴  Chrome 미설치 시 실패
 iOS Safari  window.open()  인앱 브라우저에서 무시됨
 iOS 카카오톡  전용 URL 스킴  문서화되지 않음

2. iOS의 근본적 제한

iOS에서는 JavaScript로 Safari를 강제로 실행하는 것이 불가능합니다. Android처럼 Intent 스킴으로 외부 앱을 호출할 수 있는 표준화된 방법이 없기 때문입니다.

window.open(url, '_blank')는 iOS 인앱 브라우저에서
같은 WebView 내에서 열리거나 완전히 무시됩니다.

3. 사용자 경험 고려

기술적으로 불가능한 경우에도 사용자가 당황하지 않도록, 명확한 안내와 대안(링크 복사, 게스트 모드 등)을 제공해야 했습니다.


💡 해결 과정

해결책 1: 앱별 URL 스킴 활용

조사 결과, 일부 앱들은 외부 브라우저로 열기 위한 자체 URL 스킴을 제공하고 있었습니다. 특히 카카오톡의 경우 공식 스킴이 존재했습니다.

🔗 발견한 앱별 URL 스킴

  • 카카오톡: kakaotalk://web/openExternal?url={encodedUrl}
  • 라인: line://nv/article?url={encodedUrl}
  • 네이버: naversearchapp://open?url={encodedUrl}
플랫폼별 분기 처리 흐름도
플랫폼별 분기 처리 흐름도

💻 플랫폼별 분기 처리 구현

// browserDetect.ts - 외부 브라우저 열기 로직
export const openInExternalBrowser = (
  url?: string,
  browserInfo?: InAppBrowserInfo
): boolean => {
  const targetUrl = url || window.location.href;
  const info = browserInfo || detectInAppBrowser();

  // Android: Intent 스킴 사용 (시스템 기본 브라우저)
  if (isAndroid()) {
    const intentUrl = `intent://${targetUrl.replace(/^https?:\/\//, '')}` +
      `#Intent;scheme=https;action=android.intent.action.VIEW;` +
      `S.browser_fallback_url=${encodeURIComponent(targetUrl)};end`;
    window.location.href = intentUrl;
    return true;
  }

  // iOS: 앱별 URL 스킴 분기
  if (isIOS()) {
    const encodedUrl = encodeURIComponent(targetUrl);

    switch (info.browserName) {
      case 'KakaoTalk':
        window.location.href = `kakaotalk://web/openExternal?url=${encodedUrl}`;
        return true;
      case 'LINE':
        window.location.href = `line://nv/article?url=${encodedUrl}`;
        return true;
      case 'Naver':
        window.location.href = `naversearchapp://open?url=${encodedUrl}`;
        return true;
      default:
        // 지원되지 않는 앱 - 수동 안내 필요
        return false;
    }
  }

  window.open(targetUrl, '_blank');
  return true;
};

해결책 2: Android 시스템 기본 브라우저 활용

처음에는 Chrome을 명시적으로 지정했으나, Chrome이 "사용 중지"된 경우 Intent가 조용히 실패하는 문제를 발견했습니다. 패키지를 지정하지 않고 action=android.intent.action.VIEW를 사용하면 시스템 기본 브라우저로 열립니다.

// 시스템 기본 브라우저로 열기 (Chrome 비활성화 시에도 동작)
const intentUrl = `intent://example.com#Intent;` +
  `scheme=https;` +
  `action=android.intent.action.VIEW;` +  // Chrome 대신 시스템 기본 브라우저
  `S.browser_fallback_url=${encodeURIComponent(targetUrl)};` +
  `end`;

해결책 3: 인앱 브라우저 감지 시 대체 UI

인앱 브라우저가 감지되면, 기존 로그인 버튼 대신 경고 메시지와 함께 외부 브라우저로 유도하는 UI를 표시합니다.

조건부 렌더링 흐름도
조건부 렌더링 흐름도

💻 핵심 구현 코드

// LoginPage.tsx
const [inAppBrowserInfo] = useState(() => detectInAppBrowser());

return inAppBrowserInfo.isInAppBrowser ? (
  // 인앱 브라우저 경고 UI
  <InAppBrowserWarning
    browserName={inAppBrowserInfo.browserName}
    onOpenExternal={handleOpenExternal}
    onCopyLink={handleCopyLink}
    onGuestMode={handleGuestMode}
  />
) : (
  // 일반 Google 로그인 버튼
  <GoogleLoginButton onClick={handleGoogleLogin} />
);

해결책 4: 다국어 지원

강냉봇은 4개 국어를 지원하기 때문에, 인앱 브라우저 관련 메시지도 모든 언어로 번역했습니다.

// ko.json
"inAppBrowser": {
  "title": "외부 브라우저에서 열어주세요",
  "description": "{{browserName}}에서는 보안 정책으로 인해 Google 로그인이 제한됩니다.",
  "openExternal": "외부 브라우저에서 열기",
  "copyLink": "링크 복사",
  "copied": "링크가 복사되었습니다!",
  "manualGuide": "이 앱에서는 자동 열기가 지원되지 않습니다. 우측 상단 메뉴에서 'Safari로 열기'를 선택해주세요."
}

 


📈 개선 결과

 시나리오  기존  개선 후
 카카오톡 → 로그인  ❌ 403 에러  ✅ Safari 자동 연결
 Android 인앱 브라우저  ❌ 403 에러  ✅ Chrome 자동 연결
 인스타그램/에타 (iOS)  ❌ 403 에러  ⚠️ 수동 안내 + 복사 기능
 사용자 이탈률  높음  대폭 감소

배운 점

  1. 플랫폼 제약의 이해: Google의 WebView 차단 정책은 우회할 수 없습니다. 이를 받아들이고 대안을 찾는 것이 중요합니다.
  2. 앱별 URL 스킴 활용: 공식 문서에 없더라도, 커뮤니티 조사를 통해 앱별 숨겨진 기능을 발견할 수 있습니다.
  3. Graceful Degradation: 기술적으로 완벽한 해결이 불가능할 때, 사용자에게 명확한 안내와 대안을 제공하는 것이 핵심입니다.
  4. 접근성 고려: 경고 UI에 role="alert", 아이콘에 aria-hidden을 적용하여 스크린 리더 호환성을 높였습니다.

🚀 향후 계획: 근본적인 해결책

현재 구현은 웹 환경의 제약 내에서 최선의 우회 방법을 적용한 것입니다. 하지만 인앱 브라우저 문제를 근본적으로 해결하기 위해서는 네이티브 앱 개발이 필요합니다.

📱 React Native 도입 계획

  • 네이티브 OAuth 연동: Chrome Custom Tabs (Android) / SFSafariViewController (iOS)를 통해 Google이 권장하는 방식으로 안전한 로그인 구현
  • 인앱 브라우저 우회 불필요: 앱 자체에서 시스템 브라우저를 호출하므로 WebView 제한에서 완전히 자유로움
  • 일관된 사용자 경험: 카카오톡, 에브리타임 등 어떤 경로로 접근해도 동일한 네이티브 로그인 플로우 제공

React Native를 통해 기존 React 코드를 최대한 재사용하면서, 네이티브 수준의 인증 경험을 제공할 예정입니다.


🛠 기술 스택 요약

 영역  기술  활용 내용
 브라우저 감지  User-Agent 분석  15개+ 인앱 브라우저 패턴 감지
 외부 브라우저  URL Scheme  카카오톡, 라인, 네이버 등 앱별 분기
 Android  Intent 스킴  Chrome + Fallback URL
 다국어  i18next  4개 국어(한/영/중/일) 완벽 지원
 접근성  ARIA 속성  role="alert", aria-hidden 적용

인앱 브라우저의 벽을 넘어, 모든 사용자에게 원활한 로그인 경험을 제공하기 위한 여정.

📂 프로젝트 저장소: KangNaengBot-FE

🌐 배포 URL: KangNaengBot


◆ 함께 보면 좋은 글

반응형

댓글