모바일 앱에 Outline SDK 추가

이 문서에서는 간소화된 로컬 프록시 관리를 위한 MobileProxy 라이브러리에 중점을 두고 Outline SDK를 모바일 애플리케이션에 통합하는 방법을 설명합니다.

MobileProxy는 모바일 앱에 프록시 기능을 통합하는 과정을 간소화하도록 설계된 Go 기반 라이브러리입니다. Go 모바일을 사용하여 모바일 라이브러리를 생성하므로 로컬 프록시를 통해 트래픽을 라우팅하도록 앱의 네트워킹 라이브러리를 구성할 수 있습니다.

MobileProxy가 없는 앱

MobileProxy가 없는 콘텐츠 앱

MobileProxy가 있는 앱

MobileProxy가 있는 콘텐츠 앱

1단계: MobileProxy 모바일 라이브러리 빌드

gomobile을 사용하여 Go 코드를 Android 및 iOS용 라이브러리로 컴파일합니다.

  1. Outline SDK 저장소를 클론합니다.

    git clone https://github.com/Jigsaw-Code/outline-sdk.git
    cd outline-sdk/x
    
  2. go build를 사용하여 Go 모바일 바이너리를 빌드합니다.

    go build -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind
    

    Psiphon 지원 추가

    다음 추가 단계에 따라 Psiphon 네트워크를 사용할 수 있는 지원을 추가할 수 있습니다.

    • Psiphon팀에 문의하여 네트워크에 액세스할 수 있는 구성을 받습니다. 계약이 필요할 수 있습니다.
    • 수신된 Psiphon 구성을 SmartDialer 구성의 fallback 섹션에 추가합니다.
    • -tags psiphon 플래그를 사용하여 모바일 프록시를 빌드합니다.

      go build -tags psiphon -o "$(pwd)/out/" golang.org/x/mobile/cmd/gomobile golang.org/x/mobile/cmd/gobind
      

    Psiphon 코드베이스는 GPL에 따라 라이선스가 부여되며, 이는 자체 코드에 라이선스 제한을 적용할 수 있기 때문에 -tags psiphon 플래그가 필요합니다. 해당 기관으로부터 특별 라이선스를 받는 것이 좋습니다.

  3. 모바일 라이브러리를 생성하고 프로젝트에 추가합니다.

    Android

    PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=android -androidapi=21 -o "$(pwd)/out/mobileproxy.aar" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy
    

    Android 스튜디오에서 File > Import Project…를 선택하여 생성된 out/mobileproxy.aar 번들을 가져옵니다. 자세한 내용은 Go Mobile의 Android용 빌드 및 배포를 참고하세요.

    iOS

    PATH="$(pwd)/out:$PATH" gomobile bind -ldflags='-s -w' -target=ios -iosversion=11.0 -o "out/mobileproxy.xcframework" github.com/Jigsaw-Code/outline-sdk/x/mobileproxy
    

    out/mobileproxy.xcframework 번들을 Xcode 프로젝트로 드래그합니다. 자세한 내용은 Go Mobile의 iOS용 빌드 및 배포를 참고하세요.

2단계: MobileProxy 실행

앱의 런타임 내에 MobileProxy 로컬 프록시를 초기화하고 시작합니다. 동적 전략 선택을 위해 정적 전송 구성 또는 스마트 프록시를 사용할 수 있습니다.

  • 정적 전송 구성: 로컬 주소 및 전송 구성과 함께 RunProxy 함수를 사용합니다.

    Android

    import mobileproxy.*
    
    val dialer = StreamDialer("split:3")
    
    // Use port zero to let the system pick an open port for you.
    val proxy = Mobileproxy.runProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    

    iOS

    import Mobileproxy
    
    let dialer = MobileproxyStreamDialer("split:3")
    
    // Use port zero to let the system pick an open port for you.
    let proxy = MobileproxyRunProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    
  • 스마트 프록시: 스마트 프록시는 지정된 테스트 도메인을 기반으로 DNS 및 TLS 전략을 동적으로 선택합니다. 구성 전략을 YAML 형식으로 지정해야 합니다().

    Android

    val testDomains = Mobileproxy.newListFromLines("www.youtube.com\ni.ytimg.com")
    val strategiesConfig = "..."  // Config YAML.
    val dialer = Mobileproxy.newSmartStreamDialer(testDomains, strategiesConfig, Mobileproxy.newStderrLogWriter())
    
    // Use port zero to let the system pick an open port for you.
    val proxy = Mobileproxy.runProxy("localhost:0", dialer)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    

    iOS

    import Mobileproxy
    
    var dialerError: NSError?
    let testDomains = MobileproxyNewListFromLines("www.youtube.com\ni.ytimg.com")
    let strategiesConfig = "..."  // Config YAML.
    let dialer = MobileproxyNewSmartStreamDialer(
        testDomains,
        strategiesConfig,
        MobileproxyNewStderrLogWriter(),
        &dialerError
    )
    
    var proxyError: NSError?
    // Use port zero to let the system pick an open port for you.
    MobileproxyRunProxy("localhost:0", dialer, &proxyError)
    // Configure your networking library using proxy.host() and proxy.port() or proxy.address().
    // ...
    // Stop running the proxy.
    proxy.stop()
    

3단계: HTTP 클라이언트 및 네트워킹 라이브러리 구성

로컬 프록시 주소와 포트를 사용하도록 네트워킹 라이브러리를 구성합니다.

Dart/Flutter HttpClient

HttpClient.findProxy로 프록시를 설정합니다.

HttpClient client = HttpClient();
client.findProxy = (Uri uri) {
  return "PROXY " + proxy.address();
};

OkHttp (Android)

OkHttpClient.Builder.proxy로 프록시를 설정합니다.

val proxyConfig = Proxy(Proxy.Type.HTTP, InetSocketAddress(proxy.host(), proxy.port()))
val client = OkHttpClient.Builder().proxy(proxyConfig).build()

JVM (Java, Kotlin)

시스템 속성으로 사용할 프록시를 구성합니다.

System.setProperty("http.proxyHost", proxy.host())
System.setProperty("http.proxyPort", String.valueOf(proxy.port()))
System.setProperty("https.proxyHost", proxy.host())
System.setProperty("https.proxyPort", String.valueOf(proxy.port()))

Android Web View

androidx.webview 라이브러리를 사용하여 애플리케이션의 모든 웹 뷰에 프록시 구성을 적용합니다.

ProxyController.getInstance()
    .setProxyOverride(
        ProxyConfig.Builder()
            .addProxyRule(this.proxy!!.address())
            .build(),
        {}, // execution context for the following callback - do anything needed here once the proxy is applied, like refreshing web views
        {} // callback to be called once the ProxyConfig is applied
    )

iOS Web View

iOS 17부터 WKWebsiteDataStore 속성을 사용하여 WKWebView에 프록시 구성을 추가할 수 있습니다.

let configuration = WKWebViewConfiguration()
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(proxyHost), port: NWEndpoint.Port(proxyPort)!)
let proxyConfig = ProxyConfiguration.init(httpCONNECTProxy: endpoint)
let websiteDataStore = WKWebsiteDataStore.default()
websiteDataStore.proxyConfigurations = [proxyConfig]
let webview = WKWebView(configuration: configuration)

고급: 맞춤 모바일 라이브러리 생성

고급 사용 사례의 경우 자체 모바일 라이브러리를 생성할 수 있습니다.

  1. Go 라이브러리 만들기: 필요한 SDK 기능을 래핑하는 Go 패키지를 개발합니다.
  2. 모바일 라이브러리 생성: gomobile bind를 사용하여 Android 보관 파일 (AAR) 및 Apple 프레임워크를 생성합니다. 예를 들면 다음과 같습니다.
  3. 앱에 통합: 생성된 라이브러리를 모바일 애플리케이션에 추가합니다.