> 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/docs.edgegap.com-ko/learn/server-browser.md).

# 서버 브라우저

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

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

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

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

## ✔️ 준비

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

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

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

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

### 기능 및 흐름

<figure><img src="/files/7c2984dc6f2a416a77e05acf68813c4cc306894c" alt=""><figcaption><p>Server Browser: 흐름과 계층</p></figcaption></figure>

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

[#start-browsing](#start-browsing "mention") 게임 클라이언트와 함께 다음을 수행:

* 적합한 서버 인스턴스를 검색 및 찾고, 슬롯을 확인하고, 사용 가능한 용량을 예약합니다.
* 인스턴스 슬롯의 자리를 예약하고, 연결 정보를 가져와 서버에 연결합니다.
* 다음을 사용하여 배포 환경에서 플레이어 연결을 인증합니다 [연합 ID](#user-content-fn-1)[^1].
* 검색 기준을 수정하기 위해 인스턴스 슬롯의 사용 가능 용량 및/또는 메타데이터를 업데이트합니다.

[#automated-scaling](#automated-scaling "mention") (선택 사항) 다음과 함께 스케일링 정책 사용:

* 지역 및/또는 기타 기준별로 사용 가능한 서버 인스턴스, 슬롯, 용량을 모니터링합니다.
* 사전 예열 또는 Just-In-Time 스케일링으로 용량을 늘리기 위해 서버를 배포합니다.
* 데모, 업데이트, 테스트, QA, 토너먼트 등을 위한 특수 정책으로 운영을 자동화합니다.

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

## ▶️ 브라우징 시작

효율적인 서버 사용을 보장하기 위해 서버/플레이어 수명 주기와 각자의 책임에 대해 알아보세요.

{% embed url="<https://youtu.be/P8xWrD4UCxg>" %}

### 인증

모든 요청은 다음을 전송해야 합니다 `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 %}

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

* **서버 토큰** - 다음에 필요 [서버 API](#server-lifecycle) 메서드는 [앱 버전 변수로 주입될 수 있습니다](/docs.edgegap.com-ko/learn/orchestration/application-and-versions.md#injected-variables).
  * 모든 API 메서드에 대한 액세스를 부여하며, 테스트, DevOps 또는 맞춤 오케스트레이션에 유용합니다.
* **클라이언트 토큰** - 다음에 필요 [모니터 API 및 좌석 예약 API](#player-lifecycle) 게임 클라이언트에서 사용됩니다.
  * 토큰 순환을 쉽게 하려면 이 토큰을 타사 비밀 저장소에 보관할 것을 권장합니다.

### 인스턴스 검색

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

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

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

* 인스턴스를 초기화할 때 최소 1개의 슬롯이 정의되어야 하며,
* 서버 연결 정보 - URL, IP, 포트 정보 및 위치.

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

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

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

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

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

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

{% hint style="info" %}
다음을 참조하세요 [영속성](/docs.edgegap.com-ko/learn/orchestration/persistence.md) 영구 세계 상태를 관리하고 [앱 및 버전](/docs.edgegap.com-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></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>, <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" %}
지역 및/또는 도시 메타데이터로 필터링하여 서버 지연 시간을 측정하기 전에 선택 범위를 좁히세요.
{% endhint %}

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

#### 좌석 예약

서버에 참여하기 전에, 인스턴스가 충분한 사용 가능 용량을 제공하는지 확인하려면 좌석 예약이 필요합니다. 예약에는 플레이어 그룹 또는 단일 개인이 포함될 수 있습니다.

연합 ID: 플레이어는 예약 시 고유한 타사 플레이어 ID를 제공해야 합니다. 동일한 ID를 보내면 [#connect-to-server](#connect-to-server "mention") 서버가 해당 신원을 확인할 수 있습니다.

예약이 성공적으로 완료되면([200 OK](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/200)) 플레이어는 즉시 연결을 시도해야 합니다. 보류 중인 **예약은 확인되지 않으면 30초 후 만료되며(구성 가능)** 귀하의 서버에 의해.

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

{% hint style="info" %}
서버는 모든 슬롯의 용량을 강제로 변경하거나, 슬롯을 추가/삭제/업데이트할 수 있습니다. **보류 중인 예약이 새 슬롯의 사용 가능 용량을 초과하면 해당 슬롯에 대한 모든 예약이 제거됩니다.**
{% endhint %}

### 서버에 연결

플레이어가 적합한 인스턴스를 찾으면, **다음에서 필요한 연결 정보를 가져옵니다** (URL 또는 IP, [외부 포트](/docs.edgegap.com-ko/learn/orchestration/application-and-versions.md#port-mapping)). 좌석 예약이 생성되는 즉시, **플레이어는 배포 환경의 게임 서버에 연결을 진행하고 자신의 플레이어 ID를 전달할 수 있습니다**.

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

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

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

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

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

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

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

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

### 서버 포기

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

{% hint style="success" %}
게임 설계상 재연결 기간을 허용한다면, 서버는 슬롯을 업데이트하기 전에 기다릴 수 있습니다.
{% endhint %}

다음을 읽어보세요 [영속성](/docs.edgegap.com-ko/learn/orchestration/persistence.md#recovery-objectives) 답답한 영구 서버 롤백을 방지하려면.

## 🚀 자동 확장

Server Browser는 여러 가지 다른 자동 확장 방법과 호환됩니다:

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

다음 가이드는 **스케일링 정책을 통한 사전 예열** 을 주요 방법으로 다룹니다.

{% hint style="success" %}
다음에서 배포를 중지하는 방법을 알아보세요 [Unreal Engine](/docs.edgegap.com-ko/unreal-engine.md#stop-deployments), [Unity](/docs.edgegap.com-ko/unity.md#stop-deployments)또는 [API와 함께](/docs.edgegap.com-ko/docs/api/dedicated-servers.md#delete-v1-self-stop-request_id-access_point_id) 수명 주기를 안정적으로 관리하려면.
{% endhint %}

### 용량 모니터링

스케일링 정책은 모니터링된 서버 인스턴스 목록을 지속적으로 새로고침하며, 매번 [`monitoring_interval`](#user-content-fn-9)[^9] . 각 정책에는 동일한 [필터링 구문](#search-and-browse) 을 사용한 필터가 필요합니다 - 플레이어가 인스턴스를 검색할 때처럼 지역, 용량 또는 기타 기준별로.

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

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

#### 고정 용량

다음과 같은 게임을 위해 고정 수의 활성 서버를 유지하세요 [영속성](/docs.edgegap.com-ko/learn/orchestration/persistence.md)특히 이러한 게임이 플레이어에게 프로비저닝을 제공할 때 [영속성](/docs.edgegap.com-ko/learn/orchestration/persistence.md#community-servers).

이 유형의 정책 구성은 품질 보증, 토너먼트, 클로즈드 알파, 퍼블리셔 데모 또는 기타 제한 용량 이벤트 및 운영에도 때때로 사용됩니다.

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

#### 사전 예열 대기

다음과 같은 경우 플레이어 수요에 앞서 서버를 시작하세요:

* 대규모 릴리스를 출시하고 짧은 시간에 많은 플레이어 유입이 예상될 때,
* 또는 서버 초기화에 30초 이상이 걸릴 때([배포 시간은 포함하지 않음](#user-content-fn-11)[^11]),
* 또는 게임이 계층형 또는 순환형 네트워크 종속성을 요구하는 메싱 전략을 구현할 때.

### 서버 배포

모니터링된 서버 인스턴스*s* 의 수가 구성된 활성 인스턴스 최소값 아래로 떨어지면 새 배포가 자동으로 시작됩니다. 모든 배포는 즉시 요청되며 [`deployment_registration_period`](#user-content-fn-9)[^9]  이 경과한 후 모니터링 간격마다 무한히 재시도됩니다.

{% hint style="warning" %}
새 배포가 [자동 검색을 수행하고 인스턴스를 생성하는지 확인하세요](#discover-instance) 정책 필터와 정확히 일치하도록, 그렇지 않으면 **정책이 무한 루프에 빠져 대량의 사용되지 않는 배포를 생성할 수 있습니다**!&#x20;
{% endhint %}

정책은 배포를 시작할 때 [전용 플릿](/docs.edgegap.com-ko/learn/orchestration/private-fleets.md) (Overflow to Cloud 사용) 또는 직접 Cloud로.

사용 가능한 매개변수에는 ([API 사양 참조](/docs.edgegap.com-ko/docs/api/dedicated-servers.md#private-fleets)):

* [**애플리케이션 및 버전**](/docs.edgegap.com-ko/learn/orchestration/application-and-versions.md) - 빌드 버전, 리소스 및 기타 오케스트레이션 매개변수,
* **사용자** - 선호하는 [서버 배치](/docs.edgegap.com-ko/learn/orchestration/deployments.md#regional-standby),
* [**에 대한 단일 지리적 좌표 집합**](/docs.edgegap.com-ko/learn/orchestration/private-fleets.md) - 클라우드는 비워 두거나, 원하는 지역 내 호스트를 지정,
* [**태그**](/docs.edgegap.com-ko/learn/orchestration/deployments.md#dashboard-monitoring) - 나중에 이 정책으로 시작된 배포를 찾을 수 있도록 정책 이름으로 태그 지정,
* [**환경 변수**](/docs.edgegap.com-ko/learn/orchestration/deployments.md#custom-variables) - 사용자 지정 매개변수와 비밀 정보를 서버에 전달,
* [**웹훅**](/docs.edgegap.com-ko/learn/orchestration/deployments.md#webhooks-and-postbacks) - 배포 수명 주기 이벤트를 게임 백엔드(또는 매치메이커)에 알림,
* [**캐시된 위치 필요**](/docs.edgegap.com-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 %}

{% tab title="❄️ 메시 그룹" %}
서버 그룹당 하나의 정책입니다. 게임 백엔드가 기본 노드를 시작하면 복제본이 생성됩니다. 각 노드는 주입된 메시 그룹 ID를 읽고 네트워크를 형성할 다른 노드를 검색합니다.

<pre class="language-json" data-title=""><code class="lang-json">{
<strong>  "name": "sb-meshgroup-pqyt8sxcb",
</strong><strong>  <a data-footnote-ref href="#user-content-fn-12">"filter"</a>: "metadata.policy_name eq 'sb-meshgroup-pqyt8sxcb'",
</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-meshgroup-pqyt8sxcb"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-meshgroup-pqyt8sxcb",
</strong>        "is_hidden": false
      },
      {
<strong>        "key": "SB_MESH_GROUP_ID",
</strong><strong>        "value": "pqyt8sxcb",
</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-24">"webhook_on_terminated"</a>: {
</strong>      "url": "https://my-webhook.com"
    }
  },
<strong>  <a data-footnote-ref href="#user-content-fn-25">"minimum_active_instances"</a>: 9
</strong>}
</code></pre>

{% endtab %}
{% endtabs %}

## ⚙️ 구성

Server Browser API는 새 Server Browser를 생성할 때(또는 빠른 재시작 시) 지정하는 JSON 구성에서 생성됩니다. 서버 및 슬롯 만료와 사용자 지정 메타데이터를 지정할 수 있습니다:

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

<pre class="language-json" data-title="sb-simple-example-v1-0-1.json"><code class="lang-json">{
	"version": "1.0.1",
	"server_instances": {
		"expiration_period": "1m",
		"<a data-footnote-ref href="#user-content-fn-26">인덱스</a>": {
			"policy_name": "string",
			"name": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-26">인덱스</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-0-1.json"><code class="lang-json">{
	"version": "1.0.1",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-26">인덱스</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-26">인덱스</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-0-1.json"><code class="lang-json">{
	"version": "1.0.1",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-26">인덱스</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-26">인덱스</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-0-1.json"><code class="lang-json">{
	"version": "1.0.1",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-26">인덱스</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-26">인덱스</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 메서드로 계속 설정하고 읽을 수 있습니다. 참조 [#api](#api "mention").
{% 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>리소스</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>요청 제한(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">$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시간 라이브 지원과 함께 유지 관리하는 고가용성 호스팅의 이점을 누리세요.

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

* **플레이어 수** - 플레이어가 많을수록 API 요청이 증가합니다.
* **플레이어당 요청 수** - 재시도가 빠를수록 서비스 부하가 증가하고 리소스를 소모합니다.
* **서버 수** - 서버가 많을수록 저장되는 데이터와 API 요청이 더 많아집니다.
* **클라이언트 재시도 폴백 로직** - 지터가 있는 백오프로 재시도하면 트래픽 급증 피크를 분산하는 데 도움이 됩니다.
* **평균 매치 지속 시간** - 세션이 짧을수록 Server Browser와의 상호작용이 더 자주 필요합니다.

{% hint style="info" %}
저희 클러스터는 2.4 - 3.2 GHz의 클럭 속도를 가진 AMD/Intel CPU를 탑재한 클라우드 머신을 사용합니다.
{% endhint %}

## 📗 API

**다음을 위해 SDK 사용을 고려하세요** [**Unreal Engine**](/docs.edgegap.com-ko/unreal-engine/developer-tools.md) **또는** [**Unity**](/docs.edgegap.com-ko/unity/server-browser.md) **사전 제작된 예제로 빠르게 시작할 수 있습니다.**

게임 클라이언트와 전용 서버는 전체 수명 주기 동안 Server Browser에 API 요청을 보냅니다.

{% 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가 생성됩니다. 브라우저에서 해당 URL을 열어 모든 API 엔드포인트를 확인하고 테스트하며 페이로드 예시를 검토하세요.
{% endhint %}

{% file src="/files/1c6d459a27e33e6f5e83c155e1922ea959dd475c" %}

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 req/s)으로 앱 동작을 테스트하는 것을 권장합니다.**
{% 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 요청이 너무 많음`  응답을 받게 되며, 점점 늘어나는 백오프 기간을 두고 재시도해야 합니다.

스케일링 정책이 조직의 허용된 req/s 한도보다 더 많은 배포를 유발한다면, Server Browser는 계획된 배포 수를 기준으로 가중 라운드 로빈 전략을 사용하여 사용 가능한 배포 할당량을 모든 스케일링 정책에 고르게 분배하려고 시도하면서, 각 모니터링 간격마다 자동으로 재시도합니다.

### 페이지 매김

**Server Browser는 필터링된 데이터를 특정 순서로 점진적으로 가져오기 위한 커서 페이지 매김을 제공합니다.** 이 방식은 전통적인 limit-offset 페이지 매김 대신, 더 많은 결과를 가져올 때마다 커서(시작점)와 페이지 크기(응답 항목 수)를 보내야 합니다.

{% hint style="info" %}
게임 서버 메타데이터를 위해 개발된 독자적인 데이터베이스 인덱싱 시스템과 결합되어, 커서 페이지 매김은 매우 동적인 데이터를 필터링할 때 빠르고 일관되며 유연한 사용자 경험을 제공합니다.
{% endhint %}

우리의 목표는 사용자가 첫 페이지에서 적합한 서버를 찾는 것입니다. 최상의 경험을 위해 이전 페이지의 캐시된 결과를 표시하고, 사용자가 검색을 클릭할 때만 결과를 새로 고침하는 것을 권장합니다.

## 🔖 변경 기록

#### 시맨틱 버전 관리

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

**버전이 출시되면, 절대 수정/변경되지 않습니다**.

{% hint style="info" %}
**Server Browser의 최신 버전은 `1.0.0`** 입니다. 다음 사항을 주의 깊게 살펴보세요 [업데이트 및 공지사항](/docs.edgegap.com-ko/docs/release-notes.md).
{% endhint %}

[^1]: 타사 플레이어 식별자

[^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]: 중지되면 게임 백엔드에 알립니다

[^25]: 3x3 그리드 = 세계당 9개 서버

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