> For the complete documentation index, see [llms.txt](https://docs.edgegap.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.edgegap.com/ko/learn/server-browser.md).

# 서버 브라우저

Edgegap의 서버 브라우저를 설정하여 플레이어가 가장 가까운 지역의 지속형 게임 서버를 발견하고 참여하거나, 게임 내 목록 UI에서 선택할 수 있게 하세요.

Server Browser를 빠르게 시작하고 다양한 장르의 예시 시나리오를 살펴보세요.

Server Browser는 다음을 위한 관리형 서비스입니다. [배포](/ko/learn/orchestration/deployments.md#match-bound) 및 [지속형](/ko/learn/orchestration/persistence.md) 서버:

* **플레이어가 적합한 서버를 검색하고 참여하도록 돕고** 용량, 지연 시간 또는 게임 매개변수에 따라;
* **새 서버를 사전 예열하여** 전 세계 사용자를 대규모로 처리하고 답답한 대기열을 방지합니다;
* **서버 운영을 간소화하며** 업데이트, 재시작, 지속성, 메시싱 등을 포함합니다.

{% hint style="success" %}
서버 선택을 허용하지 않고 엄격한 규칙에 따라 플레이어를 매칭하려고 하나요? 다음을 고려하세요. [매치메이킹](/ko/learn/matchmaking.md).
{% endhint %}

## ✔️ 준비

**이 서비스를 테스트하는 것은 전적으로 무료이며 신용카드가 필요하지 않습니다.**

무료 요금제는 각 재시작 후 공유 테스트 클러스터에서 최대 3시간의 런타임을 허용합니다.

이 튜토리얼은 다음을 이미 완료했다고 가정합니다:

* [Edgegap의 배포 모델을 이해한](https://docs.edgegap.com/ko/learn/pages/1e75126474c80b6c476cbd5e97b171fce5779d47#id-1.-just-in-time-deployment-dedicated-servers),
* Edgegap에 서버 애플리케이션을 배포한(게시한)[Unreal Engine](/ko/unreal-engine.md), [Unity](/ko/unity.md)),
* 게임 클라이언트에서 Edgegap의 서버에 성공적으로 연결한

### 기능 및 흐름

<figure><img src="https://1562312210-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fnwlt2Ot2ahlyLvI7kdGx%2Fimage.png?alt=media&amp;token=14b5a6c8-48c8-4f23-a50a-0783acab14e3" alt=""><figcaption><p>Server Browser: 흐름 및 계층 구조</p></figcaption></figure>

Server Browser는 두 가지 주요 기능을 제공합니다:

[#start-browsing](#start-browsing "mention") 클라이언트 및 서버 통합과 함께:

* 클라이언트는 단일 메서드로 좌석을 예약하고 연결 세부 정보를 받습니다.
* 클라이언트는 사용자 지정 필터(게임 내 UI)를 사용하여 적합한 서버와 슬롯을 탐색할 수 있습니다.
* 서버는 다음을 사용하여 서버 내 플레이어 연결을 인증합니다. [페더레이티드 아이덴티티](#user-content-fn-1)[^1].
* 서버는 가시성을 수정하거나 스케일링을 트리거하기 위해 슬롯 용량과 메타데이터를 업데이트합니다.

[#automated-scaling](#automated-scaling "mention") (선택 기능) 및 스케일링 정책:

* 지역별 또는 사용자 지정 기준별로 사용 가능한 서버 인스턴스, 슬롯, 용량을 모니터링합니다.
* 사전 예열 또는 적시 스케일링으로 용량을 늘리기 위해 새 서버를 배포합니다.
* 제한된 기간 이벤트(QA 테스트, 토너먼트)를 위한 격리된 정책으로 운영을 자동화합니다.

{% hint style="info" %}
게임 출시 후, **서버 브라우저는 24시간 연중무휴로 실행되어야 하며** 전 세계의 플레이어가 서버에 참여할 수 있도록 해야 합니다.
{% endhint %}

## ▶️ 탐색 시작

효율적인 서버 사용을 위해 서버와 플레이어(클라이언트) 수명 주기를 알아보세요.

**빠르게 시작하세요 - SDK를 게임에 추가하세요**:

* 언리얼 엔진 [개발자 도구](/ko/unreal-engine/developer-tools.md#integration-kit):
  * [문서를 읽어보세요](https://egik.betide.studio/) Betide Studios 제공,
  * [Fab Marketplace에서 설치](https://www.fab.com/listings/ff17ad88-12a1-49cf-9a41-31695ed11e16) (개인용으로는 무료).
* Unity [개발자 도구](/ko/unity/developer-tools.md#software-development-kit):
  * [Unity 패키지 관리자를 사용하여 무료로 패키지 설치](https://github.com/edgegap/edgegap-unity-sdk),
  * [시작 가이드와 완전한 예제를 살펴보세요](/ko/unity/matchmaking.md).

### 인증

모든 요청에는 `Authorization`  비밀값이 포함된 HTTP 헤더를 보내야 합니다 **인증 토큰:**

<pre><code>Authorization: <a data-footnote-ref href="#user-content-fn-2">xxxxxxxx-e458-4592-b607-c2c28afd8b62</a>
</code></pre>

{% hint style="warning" %}
**토큰을 비밀로 안전하게 보관하세요! Edgegap 직원은 절대 토큰을 요구하지 않습니다.**
{% endhint %}

{% hint style="info" %}
Matchmaker 토큰과 Server Browser 토큰은 Edgegap API 토큰과 별개입니다.
{% endhint %}

Server Browser는 자동으로 두 가지 유형의 토큰을 생성합니다:

* **서버 토큰** - 다음에 필요 [서버 API](#server-lifecycle) 메서드는 [앱 버전 변수로 주입할 수 있습니다.](/ko/learn/orchestration/application-and-versions.md#injected-variables).
  * 모든 API 메서드에 대한 접근 권한을 부여하며, 테스트, DevOps 또는 사용자 지정 스케일링에 유용합니다.
* **클라이언트 토큰** - 다음에 필요 [모니터 API 및 좌석 예약 API](#player-lifecycle) 게임 클라이언트가 사용합니다.

{% hint style="success" %}
프로덕션에서 토큰 로테이션을 쉽게 하기 위해 게임 백엔드의 시크릿 저장소에 클라이언트 토큰을 저장하세요.
{% endhint %}

### 인스턴스 검색

검색은 완전히 초기화된 서버가 Server Browser에 알리고 표시되는 과정입니다. [검색 또는 자동 할당 예약을 통해](#allocate-capacity).

{% hint style="warning" %}
**새** [배포](/ko/learn/orchestration/deployments.md) **새 인스턴스를 생성해야 합니다.** 추가된 용량을 추적하기 위해 초기화할 때.
{% endhint %}

{% hint style="info" %}
다음을 참조하세요 [#automated-scaling](#automated-scaling "mention") 스케일링 정책에 대해 알아보고 배포를 자동으로 시작합니다.
{% endhint %}

**필수 정보** 각 서버 인스턴스에 포함됩니다:

* 인스턴스를 초기화할 때 정의된 슬롯이 최소 1개 이상,
* 서버 연결 세부 정보 - URL, IP, 포트 정보 및 위치.

**선택적 사용자 지정 메타데이터 매개변수** 플레이어 필터링, 정렬 및 탐색용; 예:

* 슬롯 정보 - 팀 용량 및 팀별 메타데이터(예: 팀 이름),
* 이름 및 태그 - 사용자 지정 가능하며, 고유하고, 사람이 읽을 수 있고 검색 가능한 레이블;
* 호환성 데이터 - 서버 버전 또는 지원되는 클라이언트 버전;
* 지연 시간 식별자 - 도시 및 지역 식별자, 그리고 할당된 [핑 비컨](/ko/learn/orchestration/ping-beacons.md) 세부 정보;
* 게임 매개변수 - 레벨/씬/맵, 게임 모드, 난이도, 사용된 모드;
* 플레이어가 필터링하고 적합한 서버를 찾는 데 도움이 되는 기타 사용자 지정 매개변수.

{% hint style="info" %}
위의 메타데이터 매개변수는 예시일 뿐이며, 필요에 따라 원하는 수만큼 매개변수를 정의할 수 있습니다.
{% endhint %}

{% hint style="success" %}
중첩 객체를 직렬화하려면 키에 접근 경로를 다음과 같이 인코딩하세요. `"object.child.property"`.
{% endhint %}

서버는 **언제든지 인스턴스 또는 슬롯 메타데이터를 업데이트하여** 검색 가능성 기준을 수정할 수 있습니다. 메타데이터를 업데이트할 때는 인덱싱된 모든 키에 유효한 값이 제공되어야 합니다(수정되지 않았더라도).

**서버 인스턴스는 주기적으로 유지 신호(heartbeat)를 보내야 합니다.** 지속적인 가용성을 확인하고 플레이어가 충돌했거나 오프라인인 서버에 연결하는 것을 방지하기 위해서입니다. 설정된 만료 기간 동안 heartbeat가 누락되면 인스턴스와 대기 중인 좌석 예약이 자동으로 삭제됩니다.

{% hint style="info" %}
다음을 참조하세요 [지속성](/ko/learn/orchestration/persistence.md) 영구적인 월드 상태를 관리하고 [앱 및 버전](/ko/learn/orchestration/application-and-versions.md#active-caching) 더 빠른 배포를 위해.
{% endhint %}

### 용량 할당

인스턴스 및 슬롯 용량은 다음 두 가지 방식으로 할당할 수 있으며, 개별적으로 또는 함께 사용할 수 있습니다:

* [#auto-assigned-reservation](#auto-assigned-reservation "mention") 특정 스케일링 정책으로 시작된 서버를 선택하려면,
* [#search-and-browse](#search-and-browse "mention") 플레이어가 필터를 설정하고 적합한 서버를 탐색해 직접 선택할 수 있게 합니다.

{% hint style="success" %}
다음을 통해 빠르게 시작하는 것을 권장합니다. [#auto-assigned-reservation](#auto-assigned-reservation "mention") 더 간단한 옵션으로.
{% endhint %}

#### 자동 할당 예약

{% hint style="info" %}
자동 할당을 구현하여 **서버를 자동으로 선택**하도록 합니다. 플레이어 지역을 기준으로.
{% endhint %}

플레이어는 플레이어 ID와 스케일링 정책 이름만 제공하여 자동 할당 예약을 생성할 수 있습니다. Server Browser는 충분한 참여 가능 용량을 제공하는 인스턴스 슬롯을 자동으로 찾아 좌석을 예약하고, 인스턴스 연결 세부 정보와 함께 즉시 응답합니다.

이 예약에 적합한 인스턴스 슬롯이 없는 경우, 응답은 다음과 같습니다:

* **상태 코드는 정책 스케일링 상태를 나타내며** 추가 용량이 더해지고 있다면,
* 스케일업 중이라면, **헤더 `Retry-After`  재시도 전 대기 시간(초)을 나타냅니다**.

예약이 완료되면 다음으로 건너뛸 수 있습니다. [#connect-to-server](#connect-to-server "mention").

#### 검색 및 탐색

{% hint style="info" %}
다음을 위한 사용자 지정 검색을 구현하세요. **사용자 지정 필터 기준 및/또는 플레이어에게 서버 목록을 보여주기 위해.**
{% endhint %}

플레이어는 사용자 지정 필터와 정렬로 서버 인스턴스를 나열하고 [결과를 페이지 처리하여](#pagination) 참여하고 싶은 서버를 찾을 수 있습니다. 각 인스턴스의 슬롯도 동일한 방식으로 검색할 수 있습니다.

인스턴스와 슬롯은 내장 매개변수 또는 [인덱싱된 메타데이터](#configuration):

<table><thead><tr><th width="400">속성</th><th width="140">데이터 유형</th><th width="105">인스턴스</th><th width="105">슬롯</th></tr></thead><tbody><tr><td><code>request_id</code></td><td><code>문자열</code></td><td>✅</td><td>❌</td></tr><tr><td><code>total_joinable_seats</code>, <code>total_available_seats</code></td><td><code>정수</code></td><td>✅</td><td>❌</td></tr><tr><td><code>이름</code></td><td><code>문자열</code></td><td>❌</td><td>✅</td></tr><tr><td><code>available_seats</code>, <code>reserved_seats</code></td><td><code>정수</code></td><td>❌</td><td>✅</td></tr><tr><td><code>created_at</code>, <code>updated_at</code></td><td><code>문자열</code></td><td>✅</td><td>✅</td></tr><tr><td><code>metadata.{index}</code> (사용자 지정)</td><td><code>문자열</code>, <code>정수</code>, <code>실수</code>, <code>불리언</code></td><td>✅</td><td>✅</td></tr></tbody></table>

사용 가능한 필터링 연산자는 필터링된 속성의 데이터 유형에 따라 달라집니다:

<table><thead><tr><th width="125">매개변수</th><th width="135">연산자</th><th>예시 필터(간단한 예시 기준)</th></tr></thead><tbody><tr><td><code>문자열</code></td><td><p><a data-footnote-ref href="#user-content-fn-3"><code>eq</code></a>  또는 <a data-footnote-ref href="#user-content-fn-4"><code>ne</code></a> 또는 </p><p><a data-footnote-ref href="#user-content-fn-5"><code>lt</code></a>  또는 <a data-footnote-ref href="#user-content-fn-6"><code>le</code></a> 또는 </p><p><a data-footnote-ref href="#user-content-fn-7"><code>gt</code></a>  또는 <a data-footnote-ref href="#user-content-fn-8"><code>ge</code></a>  또는<br><code>포함</code>  또는<br></p></td><td><pre><code>?$filter=metadata.custom_name contains 'my game'
and metadata.server_version le '1.1.0'
and metadata.server_version ge '1.0.0'
&#x26;$order=metadata.custom_name asc
</code></pre></td></tr><tr><td><code>문자열</code></td><td>리터럴 값<br><code>in</code>  (필터)<br><code>rank</code>  (정렬)</td><td><pre><code>?$filter=metadata.city in ('Chicago', 'Toronto')
&#x26;$order=rank(metadata.city, 'Chicago', 'Toronto')
</code></pre></td></tr><tr><td><code>정수</code>, <code>실수</code></td><td><p><a data-footnote-ref href="#user-content-fn-3"><code>eq</code></a>  또는 <a data-footnote-ref href="#user-content-fn-4"><code>ne</code></a> 또는 </p><p><a data-footnote-ref href="#user-content-fn-5"><code>lt</code></a>  또는 <a data-footnote-ref href="#user-content-fn-6"><code>le</code></a> 또는 </p><p><a data-footnote-ref href="#user-content-fn-7"><code>gt</code></a>  또는 <a data-footnote-ref href="#user-content-fn-8"><code>ge</code></a>  </p></td><td><pre><code>?$filter=metadata.xp_multiplier gt 1.0
&#x26;$order=metadata.xp_multiplier desc
</code></pre></td></tr><tr><td><code>불리언</code></td><td><a data-footnote-ref href="#user-content-fn-3"><code>eq</code></a>  또는 <a data-footnote-ref href="#user-content-fn-4"><code>ne</code></a></td><td><pre><code>?$filter=metadata.allows_new_connections eq true
</code></pre></td></tr></tbody></table>

{% hint style="success" %}
사용자 지정으로 필터링 및 정렬 `metadata.city`  측정 후 최적의 지연 시간 기준으로 [핑 비컨](/ko/learn/orchestration/ping-beacons.md).
{% endhint %}

{% hint style="info" %}
커서 기반 방식에 대해 알아보세요. [#pagination](#pagination "mention") 사용자가 더 많은 결과를 가져오도록 하세요.
{% endhint %}

#### 좌석 예약

**서버에 참여하기 전에 플레이어는 좌석을 예약해야 합니다.** 인스턴스가 충분한 사용 가능 용량을 제공하고 과밀을 방지하도록 하기 위해서입니다. 예약에는 플레이어 그룹 또는 단일 개인이 포함될 수 있습니다.

페더레이티드 아이덴티티: 플레이어는 예약 시 고유한 제3자 플레이어 ID를 제공해야 합니다. 일단 [#connect-to-server](#connect-to-server "mention")되면 서버 측 검증을 위해 동일한 ID를 전송하세요.

예약이 성공적으로 완료되면 플레이어는 즉시 연결을 시도해야 합니다. 대기 중인 **예약은 확인되지 않으면 30초 후(구성 가능) 만료됩니다.** 서버에 의해.

**슬롯의 참여 가능 좌석 용량을 초과하는 예약은 거부됩니다.** ([409 Conflict](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/409)). 참여 가능 좌석은 아직 다른 플레이어에게 예약되지 않은 사용 가능한 좌석입니다.

### 서버에 연결

좌석 예약이 이루어지는 즉시, **플레이어는 배포의 게임 서버에 연결하고 netcode RPC로 플레이어 ID를 전달해야 합니다.**.

{% tabs %}
{% tab title="Unreal Engine" %}
다음에 **PIE(에디터)에서 연결하려면** 개발 및 테스트 중에 물결표 키를 누르고 `~`  다음을 입력하세요 `open {URL}:{port}` 그리고 에디터가 맵을 불러올 때까지 기다리세요.

{% hint style="success" %}
연결 실패 또는 검은 화면이 발생하면 다음을 참조하세요. [문제 해결 가이드](/ko/unreal-engine.md#troubleshooting-and-faq-1).
{% endhint %}
{% endtab %}

{% tab title="Unity" %}
다음에 **Unity Editor를 연결하려면** 또는 **게임 클라이언트를** 클라우드 배포에 연결하고 다음을 입력하세요:

* **배포** **URL** 서버의 IP를 가리키며, 일반적으로 `NetworkManager`  컴포넌트에 있습니다.
* **외부 포트** 다음에 매핑되는 [서버의 내부 수신 포트](https://docs.edgegap.com/learn/advanced-features/application-and-versions#port-mapping), 일반적으로 Transport 컴포넌트에 있습니다.

{% hint style="success" %}
연결 시간 초과 또는 기타 문제가 발생하면 다음을 참조하세요. [문제 해결 가이드](/ko/unity.md#troubleshooting-and-faq-4).
{% endhint %}
{% endtab %}
{% endtabs %}

새 연결을 인증하려면, **서버는 모든 새 플레이어의 ID를 포함한 대량 예약 확인** 요청을 보내야 하며, 응답으로 다음을 받습니다:

* 수락된 플레이어 예약을 선호 슬롯에 할당,
* 만료된 플레이어 예약을 선호 슬롯에 할당,
* 알 수 없는 플레이어 ID 목록.

당신의 **서버는 만료된 플레이어 그룹을 어떻게 처리할지 결정하며** 만료되었거나 거부된 사용자를 허용할지, 퇴장/차단할지도 결정합니다. 각 **인스턴스 슬롯은 새로운 사용 가능 좌석 수로 즉시 업데이트되어야 하며** 향후 예약이 슬롯 용량을 초과하지 않도록 해야 합니다.

{% hint style="info" %}
당신의 서버는 모든 슬롯의 용량을 변경하고, 슬롯을 추가, 삭제 또는 업데이트할 권한을 가집니다. 대기 중인 예약이 사용 가능한 좌석을 초과하면 이 슬롯의 모든 예약을 제거합니다.
{% endhint %}

### 서버 포기

플레이어가 떠나면, 당신의 **서버는 할당된 슬롯의 사용 가능 좌석 수를 늘려야 합니다.**.

{% hint style="success" %}
서버가 재연결 기간을 허용하는 경우, 슬롯을 업데이트하기 전에 기다릴 수 있습니다.
{% endhint %}

다음을 읽어보세요. [지속성](/ko/learn/orchestration/persistence.md#recovery-objectives) 성가신 지속형 서버 롤백을 방지하려면.

## 🚀 자동 스케일링

Server Browser는 여러 가지 서로 다른 자동 스케일링 방식과 호환됩니다:

* **사전 예열 방식** - Server Browser 스케일링 정책으로만 서버를 엄격하게 시작,
* **적시 방식** - 다음을 통해 시작 [매치메이킹](/ko/learn/matchmaking.md) 및 [Server Browser로 채우기](#allocate-capacity),
* **사용자 지정 오토스케일러** - 사용자 지정 게임 백엔드와 함께 시작 [Server Browser로 채우기](#allocate-capacity).

다음 가이드는 다음에 초점을 맞춥니다. **스케일링 정책을 통한 사전 예열**.

<figure><img src="https://1562312210-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2F0RCiModlkLY6BVAjcrFJ%2Fimage.png?alt=media&amp;token=7f7c5639-13ad-4780-9839-b9e4bd67fe80" alt=""><figcaption><p>Server Browser 스케일링 정책 UI</p></figcaption></figure>

{% hint style="success" %}
배포를 중지하려면 [Unreal Engine](/ko/unreal-engine.md#stop-deployments), [Unity](/ko/unity.md#stop-deployments)또는 [API로](/ko/docs/api/dedicated-servers.md#delete-v1-self-stop-request_id-access_point_id) 서버 비용을 안정적으로 제어합니다.
{% endhint %}

### 용량 모니터링

Server Browser는 발견된 인스턴스 목록을 매 [`monitoring_interval`](#user-content-fn-9)[^9] .

{% hint style="warning" %}
다음에 **과도한 스케일링을 방지하려면**모니터링 간격을 평균 서버 시작 시간보다 약간 더 크게 설정하세요.
{% endhint %}

{% hint style="info" %}
각 정책은 다음을 사용하는 필터를 사용해야 합니다. [필터링 구문](#search-and-browse))을 사용하여 지역 용량을 모니터링합니다.
{% endhint %}

구성한 [`minimum_active_instances`](#user-content-fn-10)[^10]  수량은 다음 중 하나로 간주할 수 있습니다:

* **고정 용량** 항상 실행 상태로 유지하려는 배포의
* **사전 예열 대기** 초기화 지연을 숨기기 위한 배포 버퍼입니다.

#### 고정 용량

고정 용량 정책은 좌석 관련 필터를 사용해서는 안 됩니다.

이 유형의 정책 구성은 품질 보증, 토너먼트, 비공개 알파, 퍼블리셔 데모 또는 기타 제한 용량 이벤트에 가장 자주 사용됩니다.

대안으로, [지속성](/ko/learn/orchestration/persistence.md) 가 있는 게임은 일반적으로 장시간 실행되는 서버를 유지하려 하며, 특히 플레이어에게 프로비저닝을 제공할 때 그렇습니다. [지속성](/ko/learn/orchestration/persistence.md#community-servers).

{% hint style="info" %}
스케일링 정책은 충돌한 서버를 즉시 자동 재시작하고 재활용하는 데 도움이 됩니다.
{% endhint %}

#### 사전 예열 대기

사전 예열 정책은 용량 사용량을 모니터링하기 위해 참여 가능한 좌석 필터를 사용해야 합니다.

다음과 같은 경우 플레이어 수요보다 앞서기 위해 대기 중인 유휴 서버를 시작하세요:

* 전 세계 출시를 진행하며 짧은 시간에 플레이어가 급격히 유입될 것으로 예상하거나,
* 또는 서버 초기화에 30초 이상이 걸리는 경우([배포 시간은 제외](#user-content-fn-11)[^11]),
* 또는 서버가 더 복잡한 네트워크 토폴로지를 요구하는 메시 전략을 사용하는 경우.

### 서버 배포

새 배포는 발견된 인스턴스*들* 수가 정책의 최소 활성 인스턴스 수보다 낮아지면 자동으로 시작됩니다. 배포는 [`deployment_registration_period`](#user-content-fn-9)[^9]  경과 후 모니터링 간격마다 무한히 재시도됩니다.

{% hint style="warning" %}
**다음을 확인하세요.** [**서버 자동 검색**](#discover-instance) **통합이 정책 필터와 올바르게 연결되었는지 확인하세요. 그렇지 않으면 정책이 무한 루프에 빠져 대량의 사용되지 않는 배포를 생성할 수 있습니다!**
{% endhint %}

{% hint style="info" %}
배포는 [프라이빗 플릿](/ko/learn/orchestration/private-fleets.md) (Overflow to Cloud 포함) 또는 Cloud를 직접 사용할 수 있습니다.
{% endhint %}

사용 가능한 매개변수에는 다음이 포함됩니다([전체 API 사양 참조](/ko/docs/api/dedicated-servers.md#private-fleets)):

* [**애플리케이션 및 버전**](/ko/learn/orchestration/application-and-versions.md) - 빌드 버전, 리소스 및 기타 오케스트레이션 매개변수,
* **사용자** - 선호하는 [서버 배치를 위한 하나의 지리 좌표 세트](/ko/learn/orchestration/deployments.md#regional-standby),
* [**비공개 호스트 ID**](/ko/learn/orchestration/private-fleets.md) - 클라우드는 비워 두거나 원하는 지역의 호스트를 지정,
* [**태그**](/ko/learn/orchestration/deployments.md#dashboard-monitoring) - 나중에 이 정책으로 시작된 배포를 찾기 위해 정책 이름으로 태그 지정,
* [**환경 변수**](/ko/learn/orchestration/deployments.md#custom-variables) - 서버에 사용자 지정 매개변수와 시크릿 전달,
* [**웹훅**](/ko/learn/orchestration/deployments.md#webhooks-and-postbacks) - 배포 수명 주기 이벤트를 게임 백엔드(또는 매치메이커)에 알림,
* [**캐시된 위치가 필요함**](/ko/learn/orchestration/application-and-versions.md#active-caching) - 캐시된 위치에서만 더 빠른 배포를 선호하는 경우.

### 예제 정책

필요에 따라 이들 정책을 테스트하고 수정하세요. 대부분의 게임은 여러 정책을 사용합니다.

{% tabs %}
{% tab title="🍀 QA 인스턴스" %}
테스트를 위해 항상 하나의 서버를 배포 상태로 유지하는 간단한 정책입니다.

<pre class="language-json" data-title=""><code class="lang-json">{
  "name": "sb-qa-pool",
<strong>  <a data-footnote-ref href="#user-content-fn-12">"filter"</a>: "metadata.policy_name eq 'sb-qa-pool'",
</strong>  "deployment_request": {
    "private_host_ids": [],
    "application":  <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "version": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "users": [
      {
        "user_type": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">user_data</a>": {
<strong>          "latitude": 41.881832,
</strong><strong>          "longitude": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["sb-qa-pool"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-qa-pool",
</strong>        "is_hidden": false
      }
    ]
  },
<strong>  "minimum_active_instances": 1
</strong>}
</code></pre>

{% endtab %}

{% tab title="🌡️ 사전 예열 지역" %}
수요를 예상해 배포를 10배로 시작합니다. 지역별로 복사하세요.

<pre class="language-json" data-title=""><code class="lang-json">{
  "name": "sb-v1.0.0-chicago",
<strong>  <a data-footnote-ref href="#user-content-fn-16">"filter"</a>: "total_joinable_seats gt 0 and metadata.policy_name eq 'sb-v1.0.0-chicago'",
</strong>  "deployment_request": {
    "private_host_ids": [],
    "application":  <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "version": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "users": [
      {
        "user_type": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">user_data</a>": {
<strong>          "latitude": 41.881832,
</strong><strong>          "longitude": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["sb-v1.0.0-chicago"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-v1.0.0-chicago",
</strong>        "is_hidden": false
      }
    ]
  },
<strong>  <a data-footnote-ref href="#user-content-fn-17">"minimum_active_instances"</a>: 10
</strong>}
</code></pre>

{% endtab %}

{% tab title="🔒 MMO" %}
사용 가능한 용량이 임계값 아래로 떨어지면 배포를 추가합니다. 지역별로 복사하세요.

<pre class="language-json" data-title=""><code class="lang-json">{
<strong>  "name": "sb-mmo-chicago",
</strong><strong>  <a data-footnote-ref href="#user-content-fn-18">"filter"</a>: "total_joinable_seats gt 5 and metadata.policy_name eq 'sb-mmo-chicago'",
</strong>  "deployment_request": {
<strong>    <a data-footnote-ref href="#user-content-fn-19">"private_host_ids"</a>: ["alpha-north-america-95fab093"],
</strong>    "application":  <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "version": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "users": [
      {
        "user_type": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">user_data</a>": {
<strong>          "latitude": 41.881832,
</strong><strong>          "longitude": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["sb-mmo-chicago"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-mmo-chicago",
</strong>        "is_hidden": false
      }
    ],
<strong>    <a data-footnote-ref href="#user-content-fn-20">"webhook_on_terminated"</a>: {
</strong>      "url": "https://my-webhook.com"
    }
  },
<strong>  "minimum_active_instances": 3
</strong>}
</code></pre>

{% endtab %}

{% tab title="🔑 커뮤니티" %}
서버 소유자당 하나의 정책이며, 소유자가 정의한 맞춤 비밀번호를 전달합니다.

<pre class="language-json" data-title=""><code class="lang-json">{
<strong>  "name": "sb-owner-jnjnc8mid",
</strong><strong>  <a data-footnote-ref href="#user-content-fn-21">"filter"</a>: "metadata.policy_name eq 'sb-owner-jnjnc8mid'",
</strong>  "deployment_request": {
<strong>    <a data-footnote-ref href="#user-content-fn-19">"private_host_ids"</a>: ["alpha-north-america-95fab093"],
</strong>    "application":  <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "version": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "users": [
      {
        "user_type": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">user_data</a>": {
<strong>          "latitude": 41.881832,
</strong><strong>          "longitude": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["community", "sb-owner-jnjnc8mid"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-owner-jnjnc8mid",
</strong>        "is_hidden": false
      },
      {
<strong>        "key": "SB_SERVER_PASSWORD",
</strong><strong>        "value": "password1234",
</strong>        "is_hidden": false
      }
    ],
<strong>    <a data-footnote-ref href="#user-content-fn-22">"webhook_on_ready"</a>: {
</strong>      "url": "https://my-webhook.com"
    },
<strong>    <a data-footnote-ref href="#user-content-fn-23">"webhook_on_error"</a>: {
</strong>      "url": "https://my-webhook.com"
    },
<strong>    <a data-footnote-ref href="#user-content-fn-20">"webhook_on_terminated"</a>: {
</strong>      "url": "https://my-webhook.com"
    }
  },
  "minimum_active_instances": 1
}
</code></pre>

{% endtab %}
{% endtabs %}

## ⚙️ 구성

Server Browser API는 Server Browser 시작 시 JSON 구성으로 생성됩니다. 사용자 지정 만료/등록 시간 범위와 사용자 지정 메타데이터를 지정할 수 있습니다:

{% tabs %}
{% tab title="🍀 간단한 예시" %}

<pre class="language-json" data-title="sb-simple-example-v1-1-0.json"><code class="lang-json">{
	"version": "1.1.0",
	"server_instances": {
		"expiration_period": "1m",
		"<a data-footnote-ref href="#user-content-fn-24">인덱스</a>": {
			"policy_name": "string",
			"name": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-24">인덱스</a>": {}
	},
	"seat_reservations": {
		"expiration_period": "30s"
	},
	"scaling_policies": {
		"monitoring_interval": "10s",
		"deployment_registration_period": "1m"
	},
  "rate_limits": {
    "per_client_ip": 5
  }
}
</code></pre>

{% endtab %}

{% tab title="🎈 소셜 게임" %}

<pre class="language-json" data-title="sb-social-example-v1-1-0.json"><code class="lang-json">{
	"version": "1.1.0",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-24">인덱스</a>": {
			"policy_name": "string",
			"name": "string",
			"third_party_id": "string",
			"level": "string",
			"mode": "string",
			"difficulty": "string",
			"seed": "string",
			"max_players": "int",
			"app_version": "string",
			"location.city": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-24">인덱스</a>": {
			"third_party_id": "string",
			"max_players": "int",
			"avg_latency": "int",
			"player_ids": "string"
		}
	},
	"seat_reservations": {
		"expiration_period": "30s"
	},
	"scaling_policies": {
		"monitoring_interval": "10s",
		"deployment_registration_period": "30s"
	},
  "rate_limits": {
    "per_client_ip": 5
  }
}
</code></pre>

{% endtab %}

{% tab title="🤝 협동 게임" %}

<pre class="language-json" data-title="sb-cooperative-example-v1-1-0.json"><code class="lang-json">{
	"version": "1.1.0",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-24">인덱스</a>": {
			"policy_name": "string",
			"name": "string",
			"third_party_id": "string",
			"level": "string",
			"mode": "string",
			"difficulty": "string",
			"avg_rank": "int",
			"max_players": "int",
			"app_version": "string",
			"tags": "string",
			"match_id": "string",
			"location.city": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-24">인덱스</a>": {
			"third_party_id": "string",
			"max_players": "int",
			"player_ids": "string"
		}
	},
	"seat_reservations": {
		"expiration_period": "30s"
	},
	"scaling_policies": {
		"monitoring_interval": "10s",
		"deployment_registration_period": "30s"
	},
  "rate_limits": {
    "per_client_ip": 5
  }
}
</code></pre>

{% endtab %}

{% tab title="⚔️ 경쟁 게임" %}

<pre class="language-json" data-title="sb-competitive-example-v1-1-0.json"><code class="lang-json">{
	"version": "1.1.0",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-24">인덱스</a>": {
			"policy_name": "string",
			"name": "string",
			"third_party_id": "string",
			"avg_rank": "int",
			"max_players": "int",
			"is_ranked": "bool",
			"app_version": "string",
			"cpu_frequency": "int",
			"match_id": "string",
			"location.city": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-24">인덱스</a>": {
			"third_party_id": "string",
			"max_players": "int",
			"avg_rank": "int",
			"avg_latency": "int"
		}
	},
	"seat_reservations": {
		"expiration_period": "30s"
	},
	"scaling_policies": {
		"monitoring_interval": "10s",
		"deployment_registration_period": "15s"
	},
  "rate_limits": {
    "per_client_ip": 5
  }

</code></pre>

{% endtab %}
{% endtabs %}

{% hint style="info" %}
최고의 성능을 위해 필터링이나 정렬에 사용되지 않는 인덱스는 생략하세요. 인덱싱되지 않은 매개변수는 API 메서드나 엔진별 SDK로 계속 설정하고 읽을 수 있습니다.
{% endhint %}

## ☁️ 호스팅 클러스터

Server Browser는 Edgegap이 24시간 내내 편리하게 호스팅하고 관리합니다.

목표에 가장 적합한 호스팅 옵션을 선택하세요:

* **무료 클러스터(공유)** 모든 기능을 테스트하고 디자인과의 시너지를 탐색하려면,
  * 3시간 후 자동으로 종료되며 테스트를 계속하려면 재시작이 필요합니다.
* **프라이빗 클러스터** **(전용)** 프로덕션 요구에 맞는 안정적인 환경을 보장하려면,
  * 지역을 선택하고 24시간 연중무휴 라이브 게임 지원을 받아 안심하고 출시하세요.

#### 프라이빗 클러스터 등급

현재 제공 중인 [3가지 프라이빗 클러스터 티어](https://edgegap.com/resources/pricing#managed-infrastructure) 모두의 필요를 충족하기 위해:

<table><thead><tr><th width="160">티어</th><th align="right">취미용 티어</th><th align="right">스튜디오 티어</th><th align="right">엔터프라이즈 티어</th></tr></thead><tbody><tr><td>가장 적합한 대상</td><td align="right">열정적인 사용자,<br>개인 개발자</td><td align="right">상용 출시</td><td align="right">트래픽이 많은 출시</td></tr><tr><td>레이트 리밋 (req/s)</td><td align="right">200</td><td align="right">750</td><td align="right">2,000</td></tr><tr><td>리소스</td><td align="right">1 vCPU + 2GB RAM</td><td align="right">6 vCPU + 12GB RAM</td><td align="right">18 vCPU + 48GB RAM</td></tr><tr><td>이중화</td><td align="right">가상 노드 1개</td><td align="right">가상 노드 3개</td><td align="right">가상 노드 3개</td></tr><tr><td>가격, 시간당</td><td align="right">$0.0312</td><td align="right"> $0.146</td><td align="right">$0.548</td></tr><tr><td><strong>가격, 30일</strong><br>(중단 없는 사용)</td><td align="right"><strong>$22.464</strong></td><td align="right"><strong>$105.12</strong></td><td align="right"><strong>$394.56</strong></td></tr></tbody></table>

🌟 무료 인스턴스를 한 번의 클릭으로 프라이빗 클러스터로 업그레이드하고, 공개 출시된 게임을 위해 Edgegap 팀이 유지 관리하는 고가용성 호스팅과 24/7 실시간 지원을 받으세요.

인스턴스의 리소스 요구 사항은 다음 요소에 따라 달라집니다:

* **플레이어 수,** 플레이어가 많을수록 ⇒ API 요청이 늘고 CPU 사용량이 증가합니다,
* **플레이어당 요청 수,** 재시도가 더 잦을수록 ⇒ CPU 리소스 사용량이 증가합니다,
* **서버 수,** 서버가 많을수록 ⇒ CPU 사용량과 메모리 사용량이 증가합니다,
* **클라이언트 재시도 폴백 로직** - 지터 백오프 없이 재시도 ⇒ [써딩 허드](https://en.wikipedia.org/wiki/Thundering_herd_problem),
* **평균 매치 지속 시간** - 세션이 짧을수록 ⇒ 라이프사이클 이벤트 빈도가 높아집니다.

{% hint style="info" %}
당사의 클러스터는 최대 3.7GHz의 클록 속도를 갖춘 AMD/Intel CPU를 탑재한 클라우드 머신을 사용합니다.
{% endhint %}

## 📗 API

**다음에 대해 저희 SDK를 고려해 보세요** [**Unreal Engine**](/ko/unreal-engine/developer-tools.md) **또는** [**Unity**](/ko/unity/server-browser.md) **미리 준비된 예제로 시작할 수 있습니다.**

{% hint style="info" %}
Unity/Android - 고려하세요 [원시 문자열 보간 사용](https://www.c-sharpcorner.com/article/convert-string-to-json-in-c-sharp/) 하드코딩된 JSON의 코드 제거를 방지하기 위해.
{% endhint %}

{% hint style="success" %}
**Swagger 웹 UI**: 관리형 서비스를 배포하면 OpenAPI 사양과 편리한 웹 UI가 생성되며, 이는 엣지 케이스를 테스트하거나 페이로드 형식을 검증하는 데 유용합니다.
{% endhint %}

{% file src="/files/9fc3a075650e9dcb69df327221d41c474e273641" %}

API 사양을 가져와 [Scalar API 웹 클라이언트](https://client.scalar.com/workspace/default/request/default) 또는 [Swagger Editor](https://editor.swagger.io/) 세부 정보를 확인하세요.

### 요청 제한

인스턴스가 버스트 용량을 초과해 충돌하는 것을 방지하기 위해, 클라이언트의 공용 IP 주소별로 초당 클라이언트 요청 수를 제한합니다.

제한은 다음에 의해 구성됩니다 [#configuration](#configuration "mention") 매개변수 `rate_limits.per_client_ip`.

{% hint style="warning" %}
게임 클라이언트가 응답을 받은 뒤 요청을 재시도하지 않는다면 `429 요청이 너무 많음` **일부 플레이어는 서버에 참여하지 못할 수 있습니다** 짧은 트래픽 급증 및 피크 트래픽 기간 동안입니다.
{% endhint %}

{% hint style="success" %}
**개발 중에는 더 낮은 요청 제한(초당 1회 요청)으로 앱 동작을 테스트하는 것을 권장합니다.**
{% endhint %}

#### 부하 테스트

실서비스와 유사한 환경에서의 부하 테스트에는 배포 호스팅 비용이 발생합니다. 각 등급과 관련된 리소스 및 가격은 [요금제 페이지에서](https://edgegap.com/resources/pricing#matchmaker).

{% hint style="warning" %}
**사용하세요** [**전용 클러스터**](#private-cluster-tiers) **를 스트레스 테스트에.** 무료 인스턴스는 개발 테스트용으로만 엄격히 제한됩니다.
{% endhint %}

부하 테스트를 설계할 때, **현실적인 플레이어 패턴을 고려해 주세요**:

| 현실적인 시나리오                                            | 비현실적인 트래픽 패턴                                     |
| ---------------------------------------------------- | ------------------------------------------------ |
| ✅ 플레이어들이 몇 시간에 걸쳐 점진적으로 게임에 참여하여 req/s가 증가합니다.       | ❌ 모든 플레이어가 협력하여 정확히 같은 초에 API를 호출합니다.            |
| ✅ 플레이어들은 재시도 사이에 점점 더 긴 시간을 기다립니다(예: 1초-5초-10초-10초). | ❌ 모든 플레이어가 `429 요청이 너무 많음`  응답을 받자마자 즉시 재시도합니다.  |
| ✅ 대부분의 플레이어는 짧은 시간(10\~60초) 안에 배정을 받고 폴링을 중단합니다.     | ❌ 모든 플레이어가 배정을 받은 후에도 정해진 시간 동안 계속 폴링합니다.        |
| ✅ 대부분의 플레이어는 새 세션을 시작하기 전에(시간이 걸리며) 게임을 마칩니다.        | ❌ 모든 플레이어가 서버 배정을 받은 직후 즉시 새로 세션을 재시작합니다.        |
| ✅ 피크 트래픽은 하루 약 6시간 동안 유지되며, 이후 일부 시간대에서 트래픽이 줄어듭니다.  | ❌ 피크 트래픽이 하루 24시간 내내 유지되며, 모든 플레이어가 밤낮없이 플레이합니다. |

#### 부하 시 동작

**클라이언트 요청** - 어떤 클라이언트라도 설정된 IP당 초당 요청 제한에 도달하면, 다음을 받습니다 `429 요청이 너무 많음`  그리고 재시도하기 전에 대기(백오프)해야 합니다.

**배포 요청** - 스케일링 정책이 조직의 허용된 초당 요청 한도보다 더 많은 배포를 트리거하면, 서버 브라우저는 모든 인스턴스 정책 간에 가중 라운드 로빈 전략을 사용하여 모니터링 간격마다 자동으로 재시도합니다.

정책 가중치는 각 라운드의 예정된 배포 수에 따라 산정되며, 사용 가능한 배포 할당량을 모든 스케일링 정책에 고르게 분배합니다.

### 페이지네이션

**Server Browser는 특정 순서로 필터링된 결과를 점진적으로 가져오기 위해 커서 페이지네이션을 제공합니다.** 이 방식은 기존의 limit-offset 페이지네이션과 달리, 더 많은 결과를 가져올 때마다 커서(시작점)와 페이지 크기(응답 항목 수)를 보내야 합니다.

{% hint style="info" %}
커서 페이지네이션은 메타데이터 인스턴스 인덱싱 시스템과 결합되어, 매우 동적인 데이터를 필터링할 때 가장 일관되면서도 유연한 사용자 경험을 제공합니다.
{% endhint %}

가장 중요한 우선순위는 사용자가 첫 페이지에서 적합한 서버를 찾을 수 있도록 하는 것입니다.

최상의 경험을 위해 이전 페이지의 캐시된 결과를 보여주고, 사용자가 검색을 클릭하거나 수동으로 새로고침을 요청할 때만 결과를 갱신하는 것을 권장합니다.

## 🔖 변경 로그

다음을 참조하세요 [릴리스 노트](/ko/docs/release-notes.md) 이 서비스의 최신 변경 로그를 확인하세요.

#### 시맨틱 버저닝

당사의 개발자 도구와 관리형 서비스는 공식 [시맨틱 버저닝](https://semver.org/), 어떤 업데이트가 ✅ 안전한지(마이너, 패치)와 어떤 업데이트에 ⚠️ 파괴적 변경(메이저)이 포함될 수 있는지를 나타냅니다.

**한 번 버전이 릴리스되면 절대 수정/변경되지 않습니다**.

{% hint style="warning" %}
**서비스 버전을 업그레이드하려면 - 중지, 편집, 재시작.** 빠른 재시작은 버전 변경 사항을 적용하지 않습니다.
{% endhint %}

[^1]: 제3자 플레이어 식별자

[^2]: 예시 값

[^3]: 같음

[^4]: 같지 않음

[^5]: 보다 작음

[^6]: 작거나 같음

[^7]: 보다 큼

[^8]: 크거나 같음

[^9]: 구성을 참조하세요

[^10]: 예제 정책을 참조하세요

[^11]: 활성 캐싱을 사용하면 배포 시간을 쉽게 줄일 수 있습니다

[^12]: * 고정 용량
    * 인스턴스가 주입된 변수에서 메타데이터로 정책 이름을 제공한다고 가정합니다

[^13]: 자신의 애플리케이션 이름으로 바꾸세요

[^14]: 자신의 애플리케이션 버전으로 바꾸세요

[^15]: 시카고 좌표

[^16]: * 참여 가능한 인스턴스가 10개 미만이면 배포됩니다
    * 인스턴스가 주입된 변수에서 메타데이터로 정책 이름을 제공한다고 가정합니다

[^17]: 시카고 지역에서 최소 10개의 배포가 있을 것으로 예상합니다

[^18]: * 참여 가능한 좌석이 5개 이하인 인스턴스가 3개 미만이면 배포됩니다
    * 인스턴스가 주입된 변수에서 메타데이터로 정책 이름을 제공한다고 가정합니다

[^19]: 용량이 가능하면 프라이빗 플릿을 우선 사용

[^20]: 서버가 재시작되면 게임 백엔드에 알립니다

[^21]: * 용량을 모니터링하지 않습니다
    * 인스턴스가 주입된 변수에서 메타데이터로 정책 이름을 제공한다고 가정합니다

[^22]: 준비되면 게임 백엔드에 알립니다

[^23]: 배포가 실패하면 게임 백엔드에 알립니다

[^24]: 인덱스에는 필터링 또는 정렬에 사용되는 사용자 지정 메타데이터 매개변수가 포함됩니다
