> 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).

# 서버 브라우저

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="/files/7c2984dc6f2a416a77e05acf68813c4cc306894c" alt=""><figcaption><p>Server Browser: 흐름과 계층 구조</p></figcaption></figure>

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

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

* 클라이언트는 단일 메서드로 좌석을 예약하고 연결 정보를 받습니다.
* 클라이언트는 사용자 지정 필터(게임 내 UI)를 사용해 적합한 서버와 슬롯을 탐색할 수 있습니다.
* 서버는 다음을 사용해 서버에서 플레이어 연결을 인증합니다 [연합 ID](#user-content-fn-1)[^1].
* 서버는 슬롯 용량과 메타데이터를 업데이트하여 표시 여부를 변경하거나 확장 트리거를 실행합니다.

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

* 사용 가능한 서버 인스턴스, 슬롯, 용량을 지역별 또는 사용자 지정 기준별로 모니터링합니다.
* 미리 워밍하거나 적시 확장을 통해 용량을 늘리기 위해 새 서버를 배포합니다.
* 제한된 기간의 이벤트(QA 테스트, 토너먼트)를 위해 분리된 정책으로 작업을 자동화합니다.

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

## ▶️ 탐색 시작

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

### 인증

모든 요청에는 `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 %}

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

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

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

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

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

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

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

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

{% 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>총_참여_가능_좌석</code>, <code>총_사용_가능_좌석</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>사용_가능_좌석</code>, <code>예약된_좌석</code></td><td><code>정수</code></td><td>❌</td><td>✅</td></tr><tr><td><code>created_at</code>, <code>업데이트_시각</code></td><td><code>문자열</code></td><td>✅</td><td>✅</td></tr><tr><td><code>메타데이터.{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>에서</code>  (필터)<br><code>순위</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" %}
사용자 지정으로 필터링 및 정렬 `메타데이터.city`  측정 후 최적의 지연 시간을 위해 [핑 비콘](/ko/learn/orchestration/ping-beacons.md).
{% endhint %}

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

#### 좌석 예약

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

연합 ID: 플레이어는 예약 시 고유한 제3자 플레이어 ID를 제공해야 합니다. 그들이 [#connect-to-server](#connect-to-server "mention")서버 측 검증을 위해 동일한 ID를 보냅니다.

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

**슬롯의 참여 가능 좌석 용량을 초과하는 예약은 거부됩니다** ([409 충돌](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 에디터를 연결하려면** 또는 **게임 클라이언트** 를 클라우드 배포에 연결하려면 다음을 입력합니다:

* **배포** **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="/files/13a44dc3d8936f688df8b74b6d580ebbeab933ac" 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">{
  "이름": "sb-qa-pool",
<strong>  <a data-footnote-ref href="#user-content-fn-12">"필터"</a>: "metadata.policy_name eq 'sb-qa-pool'",
</strong>  "배포_요청": {
    "비공개_호스트_ID": [],
    "애플리케이션": <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "버전": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "사용자": [
      {
        "사용자_유형": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">사용자 데이터</a>": {
<strong>          "위도": 41.881832,
</strong><strong>          "경도": -87.623177
</strong>        }
      }
    ],
<strong>    "태그": ["sb-qa-pool"],
</strong>    "환경_변수": [
      {
<strong>        "키": "SB_SCALING_POLICY_NAME",
</strong><strong>        "값": "sb-qa-pool",
</strong>        "숨김_여부": false
      }
    ]
  },
<strong>  "최소_활성_인스턴스": 1
</strong>}
</code></pre>

{% endtab %}

{% tab title="🌡️ 지역 프리워밍" %}
수요를 예상하여 10배의 배포를 시작하세요. 지역별로 복사하세요.

<pre class="language-json" data-title=""><code class="lang-json">{
  "이름": "sb-v1.0.0-chicago",
<strong>  <a data-footnote-ref href="#user-content-fn-16">"필터"</a>: "total_joinable_seats gt 0 and metadata.policy_name eq 'sb-v1.0.0-chicago'",
</strong>  "배포_요청": {
    "비공개_호스트_ID": [],
    "애플리케이션": <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "버전": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "사용자": [
      {
        "사용자_유형": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">사용자 데이터</a>": {
<strong>          "위도": 41.881832,
</strong><strong>          "경도": -87.623177
</strong>        }
      }
    ],
<strong>    "태그": ["sb-v1.0.0-chicago"],
</strong>    "환경_변수": [
      {
<strong>        "키": "SB_SCALING_POLICY_NAME",
</strong><strong>        "값": "sb-v1.0.0-chicago",
</strong>        "숨김_여부": false
      }
    ]
  },
<strong>  <a data-footnote-ref href="#user-content-fn-17">"최소_활성_인스턴스"</a>: 10
</strong>}
</code></pre>

{% endtab %}

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

<pre class="language-json" data-title=""><code class="lang-json">{
<strong>  "이름": "sb-mmo-chicago",
</strong><strong>  <a data-footnote-ref href="#user-content-fn-18">"필터"</a>: "total_joinable_seats gt 5 and metadata.policy_name eq 'sb-mmo-chicago'",
</strong>  "배포_요청": {
<strong>    <a data-footnote-ref href="#user-content-fn-19">"private_host_ids"</a>: ["alpha-north-america-95fab093"],
</strong>    "애플리케이션": <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "버전": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "사용자": [
      {
        "사용자_유형": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">사용자 데이터</a>": {
<strong>          "위도": 41.881832,
</strong><strong>          "경도": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["sb-mmo-chicago"],
</strong>    "환경_변수": [
      {
<strong>        "키": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-mmo-chicago",
</strong>        "숨김_여부": 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">"필터"</a>: "metadata.policy_name eq 'sb-owner-jnjnc8mid'",
</strong>  "배포_요청": {
<strong>    <a data-footnote-ref href="#user-content-fn-19">"private_host_ids"</a>: ["alpha-north-america-95fab093"],
</strong>    "애플리케이션": <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "버전": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "사용자": [
      {
        "사용자_유형": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">사용자 데이터</a>": {
<strong>          "위도": 41.881832,
</strong><strong>          "경도": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["커뮤니티", "sb-owner-jnjnc8mid"],
</strong>    "환경_변수": [
      {
<strong>        "키": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-owner-jnjnc8mid",
</strong>        "숨김_여부": false
      },
      {
<strong>        "key": "SB_SERVER_PASSWORD",
</strong><strong>        "value": "password1234",
</strong>        "숨김_여부": 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"
    }
  },
  "최소_활성_인스턴스": 1
}
</code></pre>

{% endtab %}
{% endtabs %}

## ⚙️ 구성

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

{% 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>리소스</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 요청이 늘고 CPU 사용량이 증가합니다,
* **플레이어당 요청 수,** 재시도 빈도가 높을수록 ⇒ CPU 자원 사용량이 증가합니다,
* **서버 수,** 서버가 많을수록 ⇒ CPU 사용량과 메모리 사용량이 증가합니다,
* **클라이언트 재시도 폴백 로직** - 지터된 백오프 없이 재시도하면 ⇒ [썬더링 허드](https://en.wikipedia.org/wiki/Thundering_herd_problem),
* **평균 매치 지속 시간** - 세션이 짧을수록 ⇒ 라이프사이클 이벤트 발생 빈도가 높아집니다.

{% hint style="info" %}
저희 클러스터는 2.4 - 3.2 GHz의 클럭 속도를 가진 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 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당 req/s 속도 제한에 도달하면, 해당 클라이언트는 `429 요청 과다`  재시도 전에 물러나야(대기해야) 합니다.

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

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

### 페이지 매김

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

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

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

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

## 🔖 변경 로그

#### 시맨틱 버전 관리

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

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

{% hint style="info" %}
**서버 브라우저의 최신 버전은 `1.1.0`** . 다음을 주의 깊게 살펴보세요 [업데이트와 공지](/ko/docs/release-notes.md).
{% 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]: 용량이 가능하면 private fleet를 선호

[^20]: 서버가 재시작될 때 게임 백엔드에 알림

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

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

[^23]: 배포에 실패하면 게임 백엔드에 알림

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