> 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/unity/server-browser.md).

# 서버 브라우저

이 SDK는 Unity 사용자를 위한 선택적 스타터 키트로, 이후 확장 및 사용자 지정할 수 있습니다.

## 💡 기능

SDK를 설치하면 사전 구축된 자동화 기능을 사용할 수 있습니다:

{% columns %}
{% column %}

* 완전한 예제
* 수명 주기 관리
* 용량 관리
  {% endcolumn %}

{% column width="33.33333333333333%" %}

* 필터 쿼리 컴파일러
* 타입 정의(C#)
* 로컬 개발 테스트
  {% endcolumn %}

{% column width="33.33333333333333%" %}

* 크로스 플랫폼
* 쉽게 사용자 지정 가능
* 자동 재시도
  {% endcolumn %}
  {% endcolumns %}

## ✔️ 준비

Unity SDK에는 배포(Deployments), 매치메이킹(Matchmaking), 서버 브라우저(Server Browser)를 위한 선택적 통합 유틸리티가 포함되어 있습니다. 이 플러그인은 Unity 2021.3.0f1 이상 버전을 공식 지원합니다.

{% hint style="success" %}
이 플러그인은 무료 등급의 이용 약관에 따라 100% 무료로 제공됩니다.
{% endhint %}

#### 요구 사항

<details>

<summary>Git 클라이언트를 설치하세요(예: <a href="https://git-scm.com/">git-scm</a>)</summary>

Unity가 Unity 패키지를 자동으로 다운로드하고 설치하려면 Git 클라이언트가 필요합니다. 설치 후에는 git을 직접 사용할 필요가 없습니다.

</details>

#### 설치

1. Unity 프로젝트를 여세요,
2. 다음을 선택하세요 `Window > Package Management > Package Manager` ,
3. 다음 아이콘을 클릭하고 :heavy\_plus\_sign: 아이콘을 클릭하고 다음을 선택하세요 `git URL에서 패키지 추가...` ,
4. 안내가 표시되면 SDK의 URL을 입력하세요:

{% code title="" %}

```
https://github.com/edgegap/edgegap-unity-sdk.git
```

{% endcode %}

5. 클릭하세요 `추가`  를 클릭하고 설치가 완료될 때까지 기다리세요.

#### 샘플 가져오기

이 패키지에는 여러 샘플이 포함되어 있으며, 각각 개별적으로 사용하도록 되어 있습니다(샘플을 함께 결합하지 마세요).

#### 검증된 소스

이것이 이 SDK의 유일한 공식 배포 채널입니다. 검증되지 않은 출처를 신뢰하지 마세요!

#### 패키지 업데이트

Unity Package Manager에서 Edgegap SDK로 이동한 다음 `업데이트` .

{% hint style="warning" %}
**가져온 샘플은 자동으로 업데이트되지 않습니다!** 사용자 지정 속성 값이 있다면 백업한 뒤, 현재 씬에서 사용 중인 샘플 스크립트를 삭제하고 샘플을 다시 가져오세요.
{% endhint %}

{% hint style="info" %}
일부 릴리스에는 호환성을 깨는 변경 사항이 포함될 수 있습니다. 이는 새로운 MAJOR 버전으로 표시됩니다.
{% endhint %}

#### v3로 업데이트

이번 업데이트에는 많은 새로운 [서버 브라우저](/docs.edgegap.com-ko/unity/server-browser.md) 유틸리티와 예제가 포함되어 있으며, 매치메이킹 오류 처리 등이 개선되었습니다. 다음을 참조하세요 [릴리스 노트](/docs.edgegap.com-ko/docs/release-notes.md) 전체 목록은

{% hint style="warning" %}
Unity SDK v3 업데이트에는 몇 가지 호환성을 깨는 변경 사항이 포함되어 있습니다. 통합 기능을 주의 깊게 다시 테스트해 주세요.
{% endhint %}

## 🍀 시작하기

이 가이드는 다음에 대한 기본 지식을 가정합니다 [서버 브라우저](/docs.edgegap.com-ko/learn/server-browser.md) 개념과 실행 중인 Server Browser.

{% hint style="success" %}
**문서를 읽으며 함께 따라 하기 위해 Auto-Assign 예제를 가져오시길 강력히 권장합니다** 코드를 따라 할 수 있습니다. 다음에서 가능합니다 `Unity Package Manager > Edgegap SDK > Samples` .
{% endhint %}

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

### 개요

우리 SDK는 이를 적극적으로 활용합니다 [의존성 주입](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview#the-concept) 및 [옵저버](https://learn.microsoft.com/en-us/dotnet/standard/events/observer-design-pattern) 프로그래밍 패턴.

{% hint style="info" %}
이 패키지는 두 가지를 모두 통합합니다 [서버 브라우저](/docs.edgegap.com-ko/learn/server-browser.md) 및 [매치메이킹](/docs.edgegap.com-ko/learn/matchmaking.md)이며, 함께 또는 별도로 사용할 수 있습니다. 원하는 대로 어떤 스크립트든 자신의 맞춤형 포크와 통합에 자유롭게 재사용할 수 있습니다.
{% endhint %}

이 패키지에는 다음이 포함됩니다:

* 런타임 파일 - 클라이언트 및 서버 빌드와 함께 컴파일되어 번들됩니다:
  * 서비스별 유틸리티:
    * [#server-agent](#server-agent "mention") - 재사용/확장할 수 있는 완전한 서버 통합.
    * [#client-agent](#client-agent "mention") - 재사용/확장할 수 있는 완전한 클라이언트 통합.
    * API 함수 - 엔드포인트 정의, 오류 처리, 로깅 자동화.
    * 필터 컴파일러 - 필터 쿼리 구성을 위한 강타입 유틸리티.
  * 서비스별 DTO[^1] - Server Browser API를 위한 타입이 지정된 데이터 컨테이너.
  * 공유 유틸리티 - 로깅, HTTP, ping, observable 등...
  * 공유 DTO[^1] - 여러 Edgegap 서비스가 데이터를 주고받는 데 사용됩니다.
* 샘플 파일 - 프로젝트에 가져온 경우에만 번들 및 컴파일됩니다:
  * [#auto-assign](#auto-assign "mention") - 자동 할당 예약이 있는 예제 핸들러,
  * [#custom-search](#custom-search "mention") - 수동 인스턴스 선택이 있는 예제 핸들러.

### 서버 에이전트

**서버 수명 주기 및 용량 관리는** 서버 에이전트가 수행합니다.

인스턴스화되면 에이전트의 **부모 MonoBehaviour(핸들러)는 에이전트를 초기화해야 하며** 다음을 제공해야 합니다:

* `onMonitorUpdate`  콜백 - 서비스 상태 변화를 관찰,
* `onInstanceUpdate`  콜백 - 인스턴스 및 슬롯 변경을 관찰하고 반응,
* `onConfirmationsUpdate`  콜백 - 연동 인증을 관찰하고 반응.

초기화되면 이 에이전트는 자동으로 유효성 검사를 제공하고 로깅 옵저버를 연결하며, 서비스 상태를 나타내기 위해 모니터링 API 엔드포인트를 단 한 번 호출하는 것으로 마무리합니다.

이 시점부터 에이전트 핸들러는 제어를 맡아 에이전트 함수를 호출해야 합니다:

* `DiscoverInstance`  초기 Server Instance와 Slots를 생성하고 heartbeat를 시작하려면,
* `DeleteInstance`  경기가 끝난 후 / 새 플레이어의 참여를 막으려면,
* `ConfirmReservation`  플레이어가 참여할 때, 신원을 확인하고 슬롯 할당을 검증하려면,
* `UpdateSlot`  슬롯 용량을 업데이트하거나(플레이어 참여/이탈 시) 메타데이터를 수정하려면,
* `UpdateInstance`  인스턴스 메타데이터를 수정하려면,
* `상태`  Server Browser 서비스 상태를 확인하려면.

{% hint style="success" %}
확인 및 슬롯/인스턴스 업데이트는 **기본적으로 대기열에 들어가 배치로 수행됩니다** (Heartbeat Mode) 확장성을 극대화하기 위해서입니다. 개발 테스트 중 더 빠르게 반복하려면 Greedy Mode를 사용하세요.
{% endhint %}

{% hint style="warning" %}
**메타데이터를 업데이트할 때는 모든 인덱스를 정의해야 합니다.** 인덱싱되지 않은 키를 해제하려면 단순히 생략하면 됩니다.
{% endhint %}

에이전트는 실행 중 서버를 검색 가능 상태로 유지하기 위해 heartbeat를 자동으로 유지합니다. 에이전트가 연속된 여러 heartbeat 동안 서버 브라우저에 도달하지 못하면(설정 가능):

* 최대치 미만 - 인스턴스가 자동으로 다시 검색됩니다,
* 최대치 초과 - 인스턴스가 자동으로 삭제됩니다.

새 플레이어 연결이 설정되면, 플레이어는 예약 확인을 수행하기 위해 네트코드를 사용하여 예약 ID(제3자 플레이어 ID)를 게임 서버로 보내야 합니다.

한 번 `onConfirmationsUpdate`  가 트리거되면, 핸들러는 추가 작업을 수행해야 합니다:

* 호출 `UpdateSlot`  확인된 예약이 있는 모든 슬롯의 사용 가능 좌석 수를 줄이려면,
* 네트코드 전용 메서드를 사용해 연결을 허용하거나 거부합니다.

플레이어가 게임을 떠나면, 핸들러는 이 슬롯의 사용 가능 좌석 수를 늘려야 합니다.

{% hint style="info" %}
예기치 않은 충돌이 발생한 경우 플레이어가 재연결할 수 있도록, 이탈하기 전에 짧은 시간 동안 기다리게 하세요.
{% endhint %}

### 클라이언트 에이전트

**인스턴스 검색, 페이지네이션, 필터링 및 예약** 은 클라이언트 에이전트가 수행합니다.

인스턴스화되면 에이전트의 **부모 MonoBehaviour(핸들러)는 에이전트를 초기화해야 하며** 다음을 제공해야 합니다:

* `onMonitorUpdate`  콜백 - 서비스 상태 변화를 관찰,
* `onInstancesUpdate`  콜백 - 인스턴스 목록 변경을 관찰하고 반응.

초기화되면 이 에이전트는 자동으로 유효성 검사를 제공하고 로깅 옵저버를 연결하며, 서비스 상태를 나타내기 위해 모니터링 API 엔드포인트를 단 한 번 호출하는 것으로 마무리합니다.

이 시점부터 에이전트 핸들러는 제어를 맡아 에이전트 함수를 호출해야 합니다:

* `ReserveSeats`  특정 인스턴스/슬롯 또는 자동 할당에 대한 용량 예약을 생성하려면,
* `ListInstances`  특정 필터, 정렬, 커서, 페이지 크기로 인스턴스를 나열하려면,
* `GetNextPage`  현재 매개변수(필터 등)로 더 많은 인스턴스를 가져오려면,
* `RefreshList`  캐시를 지우고 첫 페이지를 다시 불러오거나, 특정 커서로 새로고침하려면,
* `GetInstanceDetails`  특정 인스턴스의 메타데이터와 슬롯 정보를 가져오려면,
* `상태`  Server Browser 서비스 상태를 확인하려면.

새 플레이어 연결이 설정되면, 플레이어는 예약 확인을 수행하기 위해 네트코드를 사용하여 예약 ID(제3자 플레이어 ID)를 게임 서버로 보내야 합니다.

{% hint style="success" %}
예기치 않은 충돌 시 재연결할 수 있도록 클라이언트 또는 게임 백엔드에 연결 세부 정보를 저장하세요.
{% endhint %}

## 🧪 샘플

서버와 클라이언트 모두에 대한 완전한 동작 통합을 포함한 샘플로 시작하세요.

### 자동 할당

클라이언트가 정책 이름만 지정하는 자동 할당 예약을 사용합니다. 서버 브라우저가 정책 필터와 충분한 좌석이 있는 슬롯을 자동으로 만족하는 인스턴스를 선택합니다.

### 사용자 지정 검색

인스턴스와 슬롯을 검색하는 방법, UI 요소를 연결하는 방법, 그리고 플레이어가 용량을 수동으로 예약할 위치를 선택하게 하는 방법을 보여주는 전체 구현을 포함합니다.

## ⚙️ 사용자 지정

이 SDK는 확장 및 수정이 가능하도록 설계되었지만, 일부 수정은 위험할 수 있습니다:

✅ 핸들러 - UI 옵저버를 안전하게 연결하고 사소한 추가 또는 수정을 수행,

⚠️ 에이전트 - 수명 주기 및 용량 관리는 본인 책임 하에 수정,

⚠️ API - 선별한 유틸리티를 사용하여 처음부터 직접 통합을 작성.

핸들러는 아래에 설명된 대로 Server 및 Client 에이전트가 내보내는 모든 이벤트를 관찰할 수 있습니다.

{% hint style="warning" %}
익숙해지도록 하세요 [서버 브라우저 심층 이해](/docs.edgegap.com-ko/learn/server-browser.md) 사용자 지정을 하기 전에 개념을 먼저.
{% endhint %}

{% hint style="info" %}
도움이 필요하시면 [디스코드를 통해 문의해 주세요](https://discord.gg/MmJf8fWjnt). 실시간 게임 지원은 저희의 [티켓 시스템](https://edgegap.atlassian.net/servicedesk/customer/portal/3).
{% endhint %}

### 서버 이벤트

서버 에이전트는 부모 핸들러가 관찰하고 처리할 수 있도록 이벤트(작업)를 내보냅니다.

{% hint style="success" %}
액세스하여 이벤트 페이로드를 읽습니다  `.Current` 모든 관찰 가능한 것의 상태. 🔴 `오류` 이벤트에는 주요 이벤트 메시지 뒤에 줄바꿈 문자로 구분된 전체 오류 메시지가 포함됩니다.
{% endhint %}

observable이 내보내는 미리보기 이벤트 `모니터` :

<table data-full-width="true"><thead><tr><th width="125">작업 유형</th><th width="450">이벤트 메시지</th><th>설명</th></tr></thead><tbody><tr><td>🟢 <code>업데이트</code> </td><td><code>정상</code></td><td>모든 시스템이 정상입니다.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>비정상</code></td><td>예기치 않은 문제입니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>모니터 가져오기에 실패했습니다</code></td><td>잘못된 구성 또는 예기치 않은 문제입니다.</td></tr><tr><td>🟡 <code>경고</code></td><td><code>요청 시간 초과가 heartbeat [{timeout}]로 제한됨</code></td><td>경쟁 조건을 방지합니다.</td></tr></tbody></table>

observable이 내보내는 미리보기 이벤트(작업) `인스턴스`:

<table data-full-width="true"><thead><tr><th width="125">작업 유형</th><th width="450">이벤트 메시지</th><th>설명</th></tr></thead><tbody><tr><td>🟢 <code>업데이트</code> </td><td><code>검색됨</code></td><td><a href="/pages/4f1b15a794e07e908de33f5a0461cc3efae3613a#discover-instance">인스턴스 검색</a> 정상적으로 완료됨. 일시적인 상황으로 인스턴스가 연결을 잃었다가 다시 검색되면 트리거될 수 있습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>중복 검색</code></td><td>이 요청 ID를 가진 인스턴스가 이미 검색되었습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>검색 실패</code></td><td>검색 중 예기치 않은 문제가 발생했습니다.</td></tr><tr><td>🔵 <code>알림</code></td><td><code>heartbeat 정상</code></td><td>Heartbeat가 정상적으로 완료되었습니다.</td></tr><tr><td>🟡 <code>경고</code></td><td><code>heartbeat 실패 [{consecutive}/{maximum}]</code></td><td>Heartbeat가 실패했습니다. 서버가 Server Browser에 도달할 수 없었습니다.</td></tr><tr><td>🔵 <code>알림</code></td><td><code>인스턴스 업데이트가 대기열에 추가됨</code></td><td>다음 배치(heartbeat/greedy)를 위해 인스턴스 업데이트를 대기열에 추가함.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>인스턴스가 업데이트됨</code></td><td>인스턴스 메타데이터가 정상적으로 업데이트되었습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>인스턴스 업데이트 실패, 재시도를 위해 대기열에 추가</code></td><td>비율 제한 또는 오류로 인해 인스턴스 업데이트에 실패했습니다.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>인스턴스가 삭제됨</code></td><td>인스턴스는 더 이상 플레이어가 찾을 수 없습니다.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>인스턴스 삭제 실패(찾을 수 없음)</code></td><td>너무 많은 heartbeat 누락으로 인해 인스턴스가 만료되었을 수 있습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>인스턴스 삭제 실패</code></td><td>비율 제한 또는 오류로 인해 인스턴스를 삭제하지 못했습니다.</td></tr><tr><td>🔵 <code>알림</code></td><td><code>슬롯 업데이트가 대기열에 추가됨 [{slot}]</code></td><td>다음 배치(heartbeat/greedy)를 위해 슬롯 업데이트를 대기열에 추가함.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>슬롯이 업데이트됨 [{slot}]</code></td><td>슬롯 좌석 용량 및/또는 메타데이터가 정상적으로 업데이트되었습니다.</td></tr><tr><td>🟡 <code>경고</code></td><td><code>에이전트가 동시 슬롯 업데이트를 제한함</code></td><td>동시 업데이트 시도를 방지함(경쟁 조건).</td></tr><tr><td>🔴 <code>오류</code></td><td><code>슬롯 업데이트 실패(찾을 수 없음) [{slot}]</code></td><td>이 이름의 슬롯은 아직 이 인스턴스에 정의되어 있지 않습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>슬롯 업데이트 실패(좌석 부족) [{slot}]</code></td><td>슬롯 업데이트가 사용 가능한 좌석 수를 0 미만으로 줄이려고 했습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>슬롯 업데이트 실패, 재시도를 위해 대기열에 추가 [{slot}]</code></td><td>비율 제한 또는 오류로 인해 슬롯 업데이트에 실패했습니다.</td></tr></tbody></table>

observable이 내보내는 미리보기 이벤트(작업) `확인`:

<table data-full-width="true"><thead><tr><th width="125">작업 유형</th><th width="450">이벤트 메시지</th><th>설명</th></tr></thead><tbody><tr><td>🔵 <code>알림</code></td><td><code>대기열에 추가됨 [{player}]</code></td><td>다음 배치(heartbeat/greedy)를 위해 확인을 대기열에 추가했습니다.</td></tr><tr><td>🟡 <code>경고</code></td><td><code>중복 [{player}]</code></td><td>중복 확인 시도를 방지했습니다(이미 대기열에 있음).</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>확인됨</code></td><td>개별 슬롯에 대한 확인된 예약입니다. 핸들러가 처리할 수 있도록 만료되었거나 알 수 없는 플레이어 ID도 포함됩니다(수락/추방).</td></tr><tr><td>🔴 <code>오류</code></td><td><code>실패</code></td><td>확인 중 예기치 않은 문제가 발생했습니다. 서비스 상태를 확인하세요.</td></tr></tbody></table>

### 클라이언트 이벤트

클라이언트 에이전트는 부모 핸들러가 관찰하고 처리할 수 있도록 이벤트(작업)를 내보냅니다.

{% hint style="success" %}
액세스하여 이벤트 페이로드를 읽습니다  `.Current` 모든 관찰 가능한 것의 상태. 🔴 `오류` 이벤트에는 주요 이벤트 메시지 뒤에 줄바꿈 문자로 구분된 전체 오류 메시지가 포함됩니다.
{% endhint %}

observable이 내보내는 미리보기 이벤트 `모니터` :

<table data-full-width="true"><thead><tr><th width="125">작업 유형</th><th width="450">이벤트 메시지</th><th>설명</th></tr></thead><tbody><tr><td>🟢 <code>업데이트</code> </td><td><code>정상</code></td><td>모든 시스템이 정상입니다.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>비정상</code></td><td>예기치 않은 문제입니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>모니터 가져오기에 실패했습니다</code></td><td>잘못된 구성 또는 예기치 않은 문제입니다.</td></tr></tbody></table>

observable이 내보내는 미리보기 이벤트 `인스턴스`:

<table data-full-width="true"><thead><tr><th width="125">작업 유형</th><th width="450">이벤트 메시지</th><th>설명</th></tr></thead><tbody><tr><td>🔵 <code>알림</code></td><td><code>좌석 예약됨</code></td><td>좌석 예약이 정상적으로 완료되었습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>좌석 예약 실패(찾을 수 없음)</code></td><td><a data-mention href="#auto-assign">#auto-assign</a> - 정책 이름을 찾을 수 없습니다(삭제되었거나 비활성 상태).<br><a data-mention href="#custom-search">#custom-search</a> - 인스턴스 또는 슬롯을 찾을 수 없습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>좌석 예약 실패(용량 도달)</code></td><td><a data-mention href="#auto-assign">#auto-assign</a> - 정책이 최대 용량에 도달했습니다.<br><a data-mention href="#custom-search">#custom-search</a> - 슬롯이 최대 용량에 도달했습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>좌석 예약 실패</code></td><td>좌석 예약에 실패했습니다. 정책, 요청 ID 또는 슬롯 ID가 잘못되었을 수 있습니다.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>인스턴스 목록 가져옴</code></td><td>인스턴스 목록을 정상적으로 가져왔습니다.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>인스턴스 목록 다음 페이지 가져옴</code></td><td>인스턴스의 다음 페이지를 정상적으로 가져왔습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>인스턴스 목록의 마지막 페이지에 도달함</code></td><td>다음 페이지를 가져오지 못했습니다. 새로고침하거나 필터를 변경해 보세요.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>인스턴스 목록 다음 페이지 가져오기 실패</code></td><td>다음 페이지를 가져오지 못했습니다. 잘못된 커서 때문일 수 있습니다.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>인스턴스 세부 정보 가져옴</code></td><td>목록에 있는 인스턴스의 세부 정보를 정상적으로 가져왔습니다.</td></tr><tr><td>🟢 <code>업데이트</code> </td><td><code>인스턴스가 캐시되지 않음, 앞에 추가 중</code></td><td>현재 목록 외부의 인스턴스 세부 정보를 가져왔습니다.</td></tr><tr><td>🔴 <code>오류</code></td><td><code>인스턴스 세부 정보 가져오기 실패</code></td><td>세부 정보 가져오기에 실패했습니다. 잘못된 요청 ID 때문일 수 있습니다.</td></tr></tbody></table>

[^1]: 데이터 전송 객체
