> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 사용자 지정 HTTP 스택 디버깅

> 사용자 지정 HTTP 스택 디버깅

xCurl 대신 타이틀이 사용자 지정 HTTP 스택을 직접 사용하고 Fiddler와 같은 프록시를 통해 트래픽을 디버깅해야 하는 경우 이 문서를 사용합니다.

## 이 지침을 사용하는 경우

이 지침은 다음 중 하나 이상이 적용되는 경우에 가장 유용합니다.

* 타이틀이 프록시 설정을 직접 검사하거나 적용해야 합니다.
* 디버깅 워크플로우가 Fiddler 또는 다른 개발 프록시에 의존합니다.
* HTTP 스택이 Schannel을 보안 공급자로 사용하지 않으며 프록시 인증서를 명시적으로 로드해야 합니다.

일반적인 보안 지침은 [Microsoft Game Development Kit 타이틀용 보안 웹 요청 및 WebSockets 모범 사례](https://learn.microsoft.com/gaming/gdk/docs/gdk-dev/game-principles/security/communication-security/communication-security-impl/gc-secure-webrequest-impl)를 참조하세요.

## XBOX 및 PC의 프록시 설정

XBOX에서 프록시 설정을 처리할 때 빌드 및 대상 환경에서 해당 경로를 사용할 수 있는 경우 `WinHttpProxySettingsTypeXBox`와 함께 `WinHttpGetProxySettingsEx`를 선호합니다. XBOX에서 이 API는 2026년 4월 Microsoft Game Development Kit (GDK)부터 프록시 설정을 읽기 위한 미래 지향적인 API입니다.

XBOX에서 타이틀이 이전 GDK를 대상으로 하는 경우 XBOX 전용 프록시 설정 경로를 사용할 수 없습니다. 이 경우 HTTP 스택에서 프록시 주소와 포트를 수동으로 설정합니다. 이 문서 뒷부분에 설명된 인증서 로드 경로는 XBOX 전용 프록시 주소 API를 사용할 수 없더라도 이러한 이전 GDK에서도 여전히 적용됩니다.

PC 타이틀도 WinHTTP를 통해 프록시 설정을 쿼리할 수 있지만 XBOX 흐름을 직접 복사하지 마세요. PC에서 `WinHttpGetProxySettingsEx`는 비동기이므로 `WinHttpGetProxySettingsResultEx`를 호출하고 반환된 프록시 값을 HTTP 스택에 복사하기 전에 작업이 완료될 때까지 기다립니다.

libcurl을 사용하는 예제는 XBOX 프록시 설정 API로 프록시 주소를 확인한 다음 스택에 직접 전달하는 것입니다. 세션은 `WINHTTP_FLAG_ASYNC`로 열리고 상태 콜백이 등록되어 코드가 동기 완료와 `ERROR_IO_PENDING` 반환 모두를 처리합니다. 다음 예제는 호출 흐름을 보여줍니다.

```cpp theme={null}
// WinHttpCreateProxyResolver is not declared in WinHttp.h when using the GDK.
WINHTTPAPI DWORD WINAPI WinHttpCreateProxyResolver(HINTERNET hSession, HINTERNET* phResolver);

std::string ResolveXboxProxy()
{
    HINTERNET session = WinHttpOpen(
        L"CustomHttp/1.0",
        WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
        WINHTTP_NO_PROXY_NAME,
        WINHTTP_NO_PROXY_BYPASS,
        WINHTTP_FLAG_ASYNC);
    if (!session)
        return {};

    struct ProxyContext
    {
        HANDLE event;
        DWORD  error;
    };

    ProxyContext ctx{ CreateEventW(nullptr, TRUE, FALSE, nullptr), ERROR_SUCCESS };

    WinHttpSetStatusCallback(session,
        [](HINTERNET, DWORD_PTR context, DWORD status, LPVOID info, DWORD)
        {
            auto* c = reinterpret_cast<ProxyContext*>(context);
            if (status == WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE)
            {
                c->error = ERROR_SUCCESS;
                SetEvent(c->event);
            }
            else if (status == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR)
            {
                c->error = reinterpret_cast<WINHTTP_ASYNC_RESULT*>(info)->dwError;
                SetEvent(c->event);
            }
        },
        WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS,
        0);

    HINTERNET resolver = nullptr;
    if (WinHttpCreateProxyResolver(session, &resolver) != ERROR_SUCCESS)
    {
        CloseHandle(ctx.event);
        WinHttpCloseHandle(session);
        return {};
    }

    DWORD result = WinHttpGetProxySettingsEx(
        resolver,
        WinHttpProxySettingsTypeXBox,
        nullptr,
        reinterpret_cast<DWORD_PTR>(&ctx));

    if (result == ERROR_IO_PENDING)
    {
        // Wait for the async callback to fire (5-second timeout).
        WaitForSingleObject(ctx.event, 5000);
        result = ctx.error;
    }

    std::string proxyAddress;
    if (result == ERROR_SUCCESS)
    {
        WINHTTP_PROXY_SETTINGS_EX proxySettings{};
        if (WinHttpGetProxySettingsResultEx(resolver, &proxySettings) == ERROR_SUCCESS)
        {
            PCWSTR proxy =
                proxySettings.pcwszSecureProxy != nullptr
                    ? proxySettings.pcwszSecureProxy
                    : proxySettings.pcwszProxy;

            if (proxy != nullptr)
            {
                // Example function for conversion.
                proxyAddress = Utf16ToUtf8(std::wstring(proxy));
            }

            WinHttpFreeProxySettingsEx(WinHttpProxySettingsTypeXBox, &proxySettings);
        }
    }

    CloseHandle(ctx.event);
    WinHttpCloseHandle(resolver);
    WinHttpCloseHandle(session);
    return proxyAddress;
}

std::string proxyAddress = ResolveXboxProxy();
if (!proxyAddress.empty())
{
    curl_easy_setopt(curlHandle, CURLOPT_PROXY, proxyAddress.c_str());
}
```

## TLS 공급자 동작

### Schannel 기반 스택

스택이 Schannel 기반 TLS 동작에 의존하는 경우 콘솔은 구성한 프록시 인증서를 자동으로 적용합니다. 이 동작은 Schannel을 TLS 공급자로 사용하는 libcurl 빌드에도 적용됩니다.

### 비 Schannel 스택

스택이 OpenSSL 또는 다른 비 Schannel TLS 공급자에 의존하는 경우 타이틀은 활성 디버깅 도구에서 필요한 인증서를 명시적으로 로드할 책임이 있습니다. 릴리스 코드에 이 인증서 로드 경로를 유지하세요. 콘솔에서 타이틀은 필요할 때 프록시된 트래픽을 진단할 수 있도록 `RETAIL` 빌드에서도 관련 프록시 디버깅 경로를 사용할 수 있게 유지해야 합니다.

신뢰, 프록시 및 보안 기본값 지침은 [Microsoft Game Development Kit 타이틀용 보안 웹 요청 및 WebSockets 모범 사례](https://learn.microsoft.com/gaming/gdk/docs/gdk-dev/game-principles/security/communication-security/communication-security-impl/gc-secure-webrequest-impl)를 참조하세요.

## 프록시 루트 인증서 찾기

비 Schannel 스택의 경우 디버깅 도구에 해당하는 인증서를 명시적으로 로드하고 이 경로를 릴리스 코드에 유지합니다. 인증서 위치와 주제는 도구에 따라 다릅니다. 예를 들어 [XBOX Multiplayer Analysis Tool (XMAT)](https://aka.ms/XMAT)은 `CurrentUser\Root`에서 `Xbox Multiplayer Analysis Tool Root Cert Authority` 인증서를 사용합니다. 이 예제는 해당 인증서를 조회하고 DER 인코딩된 인증서 바이트를 타이틀 소유 버퍼로 복사합니다.

```cpp theme={null}
#include <wincrypt.h>
#include <vector>

HRESULT ExtractXmatRootCertificateDer(_Out_ std::vector<uint8_t>& encodedCertificate)
{
    encodedCertificate.clear();

    constexpr DWORD certificateStoreLocation = CERT_SYSTEM_STORE_CURRENT_USER;
    constexpr wchar_t certificateStoreName[] = L"Root";
    constexpr wchar_t certificateSubjectName[] =
        L"Xbox Multiplayer Analysis Tool Root Cert Authority";

    HCERTSTORE certificateStore = CertOpenStore(
        CERT_STORE_PROV_SYSTEM_W,
        0,
        0,
        certificateStoreLocation |
            CERT_STORE_OPEN_EXISTING_FLAG |
            CERT_STORE_READONLY_FLAG,
        certificateStoreName);
    if (certificateStore == nullptr)
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    PCCERT_CONTEXT certificate = CertFindCertificateInStore(
        certificateStore,
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        0,
        CERT_FIND_SUBJECT_STR_W,
        certificateSubjectName,
        nullptr);
    if (certificate == nullptr)
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        CertCloseStore(certificateStore, 0);
        return hr;
    }

    encodedCertificate.assign(
        certificate->pbCertEncoded,
        certificate->pbCertEncoded + certificate->cbCertEncoded);

    CertFreeCertificateContext(certificate);
    CertCloseStore(certificateStore, 0);
    return S_OK;
}
```

이 단계 후에 TLS 공급자가 사용하는 인증서 로드 경로로 `encodedCertificate`를 전달합니다.

## 제안된 디버깅 워크플로우

1. [XBOX 개발 킷의 Fiddler](/build/console-features/networking/tools/fiddler-setup-networking)에 설명된 대로 XBOX Device Portal에서 콘솔 프록시 설정을 구성합니다.
2. 2026년 4월 GDK 이상으로 XBOX를 대상으로 하고 타이틀이 프록시 설정을 직접 검사해야 하는 경우 `WinHttpProxySettingsTypeXBox`와 함께 `WinHttpGetProxySettingsEx`를 사용합니다.
3. PC 코드 경로가 WinHTTP를 통해 프록시 설정을 쿼리하는 경우, `WinHttpGetProxySettingsResultEx`를 호출하고 반환된 프록시 값을 적용하기 전에 비동기 결과를 기다립니다.
4. XBOX에서 이전 GDK를 대상으로 하거나 XBOX 전용 프록시 설정 경로를 사용하지 않는 스택을 사용하는 경우 타이틀 내에서 프록시 주소와 포트를 수동으로 설정합니다.
5. Schannel을 사용하는 경우 콘솔이 적용한 인증서에 의존합니다. OpenSSL 또는 다른 스택을 사용하는 경우 사용 중인 디버깅 도구에 대한 인증서를 인증서 저장소에서 검색하고 명시적으로 로드합니다.

## 관련 페이지

* [XBOX 개발 킷의 Fiddler](/build/console-features/networking/tools/fiddler-setup-networking)
* [Microsoft Game Development Kit 타이틀용 보안 웹 요청 및 WebSockets 모범 사례](https://learn.microsoft.com/gaming/gdk/docs/gdk-dev/game-principles/security/communication-security/communication-security-impl/gc-secure-webrequest-impl)


## Related topics

- [XBOX의 웹 요청 및 HTTP 스택](/ko/build/console-features/networking/web-requests/index.md)
- [사용자 지정 CloudScript 작성](/ko/services/playfab/live-service-management/service-gateway/automation/cloudscript/writing-custom-cloudscript.md)
- [VM 생성 중에 사용자 지정 스크립트 실행(미리 보기)](/ko/services/playfab/multiplayer/servers/vmstartupscript.md)
- [게임 저장 디버깅](/ko/build/core-features/common/game-save/game-saves-debugging.md)
- [Visual Studio를 사용한 XBOX 프로젝트 디버깅](/ko/tools/tools-console/visualstudio/debugging-with-visualstudio.md)
