iOS 시뮬레이터 및 Android 에뮬레이터에서 로컬 웹 서비스에 연결

Download Sample 샘플 다운로드

많은 모바일 애플리케이션이 웹 서비스를 사용합니다. 개발 단계에서는 로컬로 웹 서비스를 배포하고 iOS 시뮬레이터 또는 Android Emulator에서 실행되는 모바일 애플리케이션에서 웹 서비스를 사용하는 것이 일반적입니다. 따라서 웹 서비스를 호스트된 엔드포인트에 배포하지 않아도 되며, 모바일 애플리케이션 및 웹 서비스를 둘 다 로컬로 실행하기 때문에 디버깅도 간편해집니다.

iOS 시뮬레이터 또는 Android Emulator에서 실행되는 모바일 애플리케이션은 다음과 같이 로컬로 실행되고 HTTP를 통해 노출되는 ASP.NET Core 웹 서비스를 사용할 수 있습니다.

  • iOS 시뮬레이터에서 실행 중인 애플리케이션은 머신 IP 주소 또는 localhost 호스트 이름을 통해 로컬 HTTP 웹 서비스에 연결할 수 있습니다. 예를 들어, /api/todoitems/ 상대 URI를 통해 GET 작업을 노출하는 로컬 HTTP 웹 서비스가 있는 경우, iOS 시뮬레이터에서 실행 중인 애플리케이션은 http://localhost:<port>/api/todoitems/로 GET 요청을 전송하여 해당 작업을 사용할 수 있습니다.
  • Android Emulator에서 실행 중인 애플리케이션은 호스트 루프백 인터페이스의 별칭(개발 머신의 127.0.0.1)인 10.0.2.2 주소를 통해 로컬 HTTP 웹 서비스에 연결할 수 있습니다. 예를 들어, /api/todoitems/ 상대 URI를 통해 GET 작업을 노출하는 로컬 HTTP 웹 서비스가 있는 경우, Android Emulator에서 실행 중인 애플리케이션은 http://10.0.2.2:<port>/api/todoitems/로 GET 요청을 전송하여 해당 작업을 사용할 수 있습니다.

그러나 iOS 시뮬레이터 또는 Android Emulator에서 실행 중인 애플리케이션이 HTTPS를 통해 노출되는 로컬 웹 서비스를 사용하려면 추가 작업이 필요합니다. 이 시나리오에서 해당 프로세스는 다음과 같습니다.

  1. 머신에 자체 서명된 개발 인증서를 만듭니다. 자세한 내용은 개발 인증서 만들기를 참조하세요.
  2. 디버그 빌드를 위해 적절한 HttpClient 네트워크 스택을 사용하도록 프로젝트를 구성합니다. 자세한 내용은 프로젝트 구성을 참조하세요.
  3. 로컬 머신의 주소를 지정합니다. 자세한 내용은 로컬 머신 주소 지정을 참조하세요.
  4. 로컬 개발 인증서 보안 검사를 무시합니다. 자세한 내용은 인증서 보안 검사 무시를 참조하세요.

각 항목을 차례차례 설명하겠습니다.

개발 인증서 만들기

.NET Core SDK를 설치하면 로컬 사용자 인증서 저장소에 ASP.NET Core HTTPS 개발 인증서가 설치됩니다. 그러나 인증서가 설치되었지만 신뢰할 수는 없습니다. 인증서를 신뢰하려면 다음 일회성 단계를 수행하여 dotnet dev-certs 도구를 실행합니다.

dotnet dev-certs https --trust

다음 명령은 dev-certs 도구에 대한 도움말을 제공합니다.

dotnet dev-certs https --help

또는 HTTPS를 사용하는 ASP.NET Core 2.1(이상) 프로젝트를 실행할 때 Visual Studio는 개발 인증서가 누락되었는지 검색하고, 누락된 경우 설치한 후 신뢰할 것을 요구합니다.

참고 항목

ASP.NET Core HTTPS 개발 인증서가 자체 서명됩니다.

머신에서 로컬 HTTPS를 사용하도록 설정하는 방법에 대한 자세한 내용은 로컬 HTTPS 사용을 참조하세요.

프로젝트 구성

iOS 및 Android에서 실행되는 Xamarin 애플리케이션은 HttpClient 클래스에서 사용되는 네트워킹 스택을 관리형 네트워크 스택 또는 네이티브 네트워크 스택 중에서 선택할 수 있도록 합니다. 관리형 스택은 기존 .NET 코드와의 높은 수준의 호환성을 제공하지만 TLS 1.0으로 제한되며 속도가 더 느려지고 실행 파일 크기가 더 커질 수 있습니다. 네이티브 스택은 더 빠르고 더 나은 보안을 제공할 수 있지만 HttpClient 클래스의 모든 기능을 제공하지는 못할 수 있습니다.

iOS

iOS에서 실행되는 Xamarin 애플리케이션은 관리형 네트워크 스택이나 네이티브 CFNetwork 또는 NSUrlSession 네트워크 스택을 사용할 수 있습니다. 기본적으로 새 iOS 플랫폼 프로젝트는 TLS 1.2를 지원하려는 경우에는 NSUrlSession 네트워크 스택을 사용하고, 성능을 높이고 실행 파일 크기를 줄이려는 경우에는 네이티브 API를 사용합니다. 자세한 내용은 iOS/macOS용 HttpClient 및 SSL/TLS 구현 선택기를 참조하세요.

Android

Android에서 실행되는 Xamarin 애플리케이션은 관리형 HttpClient 네트워크 스택이나 네이티브 AndroidClientHandler 네트워크 스택을 사용할 수 있습니다. 기본적으로 새 Android 플랫폼 프로젝트는 TLS 1.2를 지원하려는 경우에는 AndroidClientHandler 네트워크 스택을 사용하고, 성능을 높이고 실행 파일 크기를 줄이려는 경우에는 네이티브 API를 사용합니다. Android 네트워크 스택에 대한 자세한 내용은 Android용 HttpClient 스택 및 SSL/TLS 구현 선택기를 참조하세요.

로컬 머신 주소 지정

iOS 시뮬레이터 및 Android Emulator는 로컬 머신에서 실행되는 보안 웹 서비스에 액세스할 수 있도록 합니다. 그러나 각 경우의 로컬 머신 주소가 다릅니다.

iOS

iOS 시뮬레이터는 호스트 머신 네트워크를 사용합니다. 따라서 시뮬레이터에서 실행 중인 애플리케이션은 머신 IP 주소 또는 localhost 호스트 이름을 통해 로컬 머신에서 실행되는 웹 서비스에 연결할 수 있습니다. 예를 들어, /api/todoitems/ 상대 URI를 통해 GET 작업을 노출하는 로컬 보안 웹 서비스가 있는 경우, iOS 시뮬레이터에서 실행 중인 애플리케이션은 https://localhost:<port>/api/todoitems/로 GET 요청을 전송하여 해당 작업을 사용할 수 있습니다.

참고 항목

Windows의 iOS 시뮬레이터에서 모바일 애플리케이션을 실행하는 경우 애플리케이션이 Windows용 원격 iOS 시뮬레이터에 표시됩니다. 그러나 애플리케이션은 페어링된 Mac에서 실행됩니다. 따라서 Mac에서 실행되는 iOS 애플리케이션의 경우 Windows에서 실행되는 웹 서비스에 localhost 방식으로 액세스할 수 없습니다.

Android

Android Emulator의 각 인스턴스는 개발 머신 네트워크 인터페이스에서 격리되며 가상 라우터 뒤에서 실행됩니다. 따라서 에뮬레이트된 디바이스에서는 개발 머신 또는 네트워크의 다른 에뮬레이터 인스턴스를 볼 수 없습니다.

그러나 각 에뮬레이터용 가상 라우터는 10.0.2.2 주소가 호스트 루프백 인터페이스의 별칭인 미리 할당된 주소(개발 머신의 127.0.0.1)를 포함하는 특정 네트워크 공간을 관리합니다. 따라서 /api/todoitems/ 상대 URI를 통해 GET 작업을 노출하는 로컬 보안 웹 서비스가 있는 경우, Android Emulator에서 실행 중인 애플리케이션은 https://10.0.2.2:<port>/api/todoitems/로 GET 요청을 전송하여 해당 작업을 사용할 수 있습니다.

운영 체제 검색

DeviceInfo 클래스를 사용하여 애플리케이션이 실행되는 플랫폼을 검색할 수 있습니다. 로컬 보안 웹 서비스에 액세스할 수 있도록 하는 해당 호스트 이름을 다음과 같이 설정할 수 있습니다.

public static string BaseAddress =
    DeviceInfo.Platform == DevicePlatform.Android ? "https://10.0.2.2:5001" : "https://localhost:5001";
public static string TodoItemsUrl = $"{BaseAddress}/api/todoitems/";

DeviceInfo 클래스에 대한 자세한 내용은 Xamarin.Essentials: 디바이스 정보를 참조하세요.

인증서 보안 검사 무시

iOS 시뮬레이터 또는 Android Emulator에서 실행되는 애플리케이션에서 로컬 보안 웹 서비스를 호출하려고 하면 각 플랫폼에서 관리형 네트워크 스택을 사용하더라도 HttpRequestException이 throw됩니다. 로컬 HTTPS 개발 인증서가 자체 서명되며, 자체 서명된 인증서는 iOS 또는 Android에서 신뢰되지 않기 때문입니다. 따라서 애플리케이션이 로컬 보안 웹 서비스를 사용하는 경우 SSL 오류를 무시해야 합니다. 이는 iOS 및 Android에서 관리형 및 네이티브 네트워크 스택을 모두 사용할 때 HttpClientHandler 개체의 ServerCertificateCustomValidationCallback 속성을 로컬 HTTPS 개발 인증서의 인증서 보안 검사 결과를 무시하는 콜백으로 설정하여 수행할 수 있습니다.

// This method must be in a class in a platform project, even if
// the HttpClient object is constructed in a shared project.
public HttpClientHandler GetInsecureHandler()
{
    HttpClientHandler handler = new HttpClientHandler();
    handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
    {
        if (cert.Issuer.Equals("CN=localhost"))
            return true;
        return errors == System.Net.Security.SslPolicyErrors.None;
    };
    return handler;
}

이 코드 예제에서 유효성 검사를 진행한 인증서가 localhost 인증서가 아닌 경우 서버 인증서 유효성 검사 결과가 반환됩니다. 이 인증서의 경우 유효성 검사 결과가 무시되고 인증서가 유효하다는 것을 나타내는 true가 반환됩니다. 결과 HttpClient 개체는 디버그 빌드를 위해 HttpClientHandler 생성자에 인수로 전달되어야 합니다.

#if DEBUG
    HttpClientHandler insecureHandler = GetInsecureHandler();
    HttpClient client = new HttpClient(insecureHandler);
#else
    HttpClient client = new HttpClient();
#endif

HTTP 일반 텍스트 트래픽 사용

필요에 따라 일반 텍스트 HTTP 트래픽을 허용하도록 iOS 및 Android 프로젝트를 구성할 수 있습니다. HTTP 트래픽을 허용하도록 백 엔드 서비스가 구성된 경우 기준 URL에서 HTTP를 지정한 다음 일반 텍스트 트래픽을 허용하도록 프로젝트를 구성할 수 있습니다.

public static string BaseAddress =
    DeviceInfo.Platform == DevicePlatform.Android ? "http://10.0.2.2:5000" : "http://localhost:5000";
public static string TodoItemsUrl = $"{BaseAddress}/api/todoitems/";

iOS ATS 옵트아웃

iOS에서 일반 텍스트 로컬 트래픽을 사용하도록 설정하려면 Info.plist 파일에 다음을 추가하여 ATS를 옵트아웃해야 합니다.

<key>NSAppTransportSecurity</key>    
<dict>
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>

Android 네트워크 보안 구성

Android에서 일반 텍스트 로컬 트래픽을 사용하도록 설정하려면 Resources/xml 폴더에 network_security_config.xml이라는 새 XML 파일을 추가하여 네트워크 보안 구성을 만들어야 합니다. XML 파일은 다음 구성을 지정해야 합니다.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="true">10.0.2.2</domain>
  </domain-config>
</network-security-config>

그런 다음 Android 매니페스트의 애플리케이션 노드에서 networkSecurityConfig 속성을 구성합니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest>
    <application android:networkSecurityConfig="@xml/network_security_config">
        ...
    </application>
</manifest>

빌드 작업이 AndroidResource로 설정되어 있는지 확인합니다. 그렇지 않으면 빌드 시 XML 파일을 찾을 수 없습니다.