새 빌드를 배포하고 VPN으로 옮겼더니 같은 화면이 더 이상 로드되지 않았습니다. Android 9 이상에서는 더 흔한 설명이 더 시시합니다. 플랫폼은 API 28 이상을 대상으로 하는 앱에 대해 평문 HTTP를 거부하며, VPN은 그것이 드러날 만큼만 상황을 바꿨습니다. 보게 되는 오류는 HTTP 클라이언트에 따라 다르므로 cleartext라고 말하는 경우는 드뭅니다.

이 실패의 정체

Android 9부터는 앱이 API 수준 28 이상을 대상으로 삼는 순간 플랫폼이 기본적으로 cleartext를 차단합니다. 평문 HTTP 주소로 가는 요청은 패킷 하나도 기기를 떠나기 전에 플랫폼이 거부합니다. 어떤 오류가 보이는지는 HTTP 라이브러리에 달려 있습니다. 때로는 일반적인 네트워크 실패, 때로는 시간 초과, 때로는 cleartext를 그대로 언급하는 메시지입니다. 절대 하지 않는 일은 서버에 도달하는 것입니다. Android는 기본 동작과 도메인 단위 대안을 보안 구성 가이드에 문서화합니다.

같은 기기의 브라우저는 여전히 페이지를 로드할 수 있으며, 이것이 대부분이 놓치는 단서입니다. 브라우저는 앱의 네트워크 보안 정책을 따르지 않습니다.

VPN이 의심받는 이유

보통 세 가지가 동시에 바뀝니다. VPN이 연결된 뒤 앱이 다른 호스트 이름을 확인할 수 있고, 기기가 더 새로운 빌드를 가진 새 기기일 수 있으며, 빌드 자체가 기본 동작을 켜는 대상 API 수준으로 옮겨졌을 수도 있습니다. VPN은 보이고 대상 SDK는 보이지 않으므로 VPN이 티켓을 받습니다.

네트워크 구성을 건드리기 전에 이전 빌드와 새 빌드의 대상 수준을 비교하세요. 27 이하를 대상으로 하는 앱은 예외이며, «내 예전 폰에서는 되는데»라는 모든 보고가 이것으로 설명됩니다.

Expo 플래그가 실제로 적용되는 곳

관리형 Expo 프로젝트에서 app.json에 설정을 추가해도 Android 매니페스트는 편집되지 않습니다. 매니페스트를 만드는 것은 prebuild와 개발 빌드이기 때문입니다. 그 단계에서 설정을 기록하는 것이 구성 플러그인입니다. 지원되는 경로는 expo-build-properties이며, 앱 구성에서 android 아래에 cleartext 옵션을 두어 설정합니다.

{
  "plugins": [
    ["expo-build-properties", {
      "android": { "usesCleartextTraffic": true }
    }]
  ]
}

이 스니펫은 플러그인 옵션만 설정하고 매니페스트는 나중에 생성됩니다. prebuild나 새 개발 클라이언트 빌드를 실행한 뒤, 다시 테스트하기 전에 생성된 AndroidManifest.xml에서 android:usesCleartextTraffic을 확인하세요. JS만 다시 로드하면 예전 매니페스트가 그대로 남기 때문입니다. 플러그인 버전은 프로젝트가 실행되는 Expo SDK에 맞추세요. 플러그인 옵션은 릴리스마다 바뀌므로 다른 SDK에서 복사한 스니펫은 조용한 무효화의 확실한 원인입니다.

bare 워크플로와 더 좁은 선택

bare React Native에서는 application 요소의 android:usesCleartextTraffic, 또는 지정한 도메인에만 cleartext를 허용하는 네트워크 보안 구성이 그에 해당합니다. 선택해야 하는 것은 도메인 단위 형태입니다. 내부 API를 허용하고 나머지는 모두 HTTPS에 두기 때문입니다. 예외 목록도 함께 문서화되며, 리뷰어가 묻는 부분이 바로 그것입니다.

두 접근 방식 모두 이름 붙이지 않은 것에 대해서는 플랫폼 기본값을 바꾸지 않습니다.

먼저 기기 로그로 증명하세요

구성을 편집하기 전에 정확한 실패를 확보하세요. 연결된 기기에서 adb logcat은 플랫폼의 cleartext 거부를 보여 주고 그것을 일으킨 구성 요소를 알려 줍니다. 거부된 소켓, 확인되지 않는 이름, 실패한 TLS 핸드셰이크는 전혀 다른 곳을 가리키며, cleartext를 켜면 이유 없이 앱만 약해집니다.

그다음 운영은 정직하게 유지하세요. TLS를 제공할 수 없는 실험실 엔드포인트에는 cleartext가 허용되지만, 예약할 수 있는 가장 짧은 기간에만 허용됩니다. API가 사설 네트워크 안에서 TLS를 종료할 수 있다면 인증서를 기기의 신뢰 저장소에 넣고 cleartext는 비활성으로 두세요. 터널은 출구까지의 경로만 암호화하고 그 너머는 암호화하지 않습니다.

엔드포인트가 HTTPS로 응답하면 권한을 제거하고 다시 빌드한 뒤, 같은 세션에서 VPN과 일반 Wi-Fi 모두에서 테스트하세요. 이름 확인은 두 경로에서 다를 수 있고, 두 경로를 함께 봐야 어느 쪽이 사실을 말하는지 알 수 있습니다.

관련 글