# 서버 브라우저

이 SDK는 Unity 사용자를 위한 선택적 스타터 키트로, 나중에 확장하고 커스터마이즈할 수 있습니다.

## 💡 기능

SDK를 설치하면 미리 만들어진 자동화 기능을 사용할 수 있습니다:

{% columns %}
{% column %}

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

{% column width="33.33333333333333%" %}

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

{% column width="33.33333333333333%" %}

* 크로스 플랫폼
* 쉽게 커스터마이즈 가능
* 자동 재시도
  {% endcolumn %}
  {% endcolumns %}

## ✔️ 준비

## 🍀 시작하기

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

{% hint style="success" %}
**문서를 읽으면서 코드 흐름을 따라가려면 Auto-Assign 예제를 가져오는 것을 강력히 권장합니다.** 이는 다음에서 할 수 있습니다. `Unity Package Manager > Edgegap SDK > Samples` .
{% 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`  초기 서버 인스턴스와 슬롯을 생성하고 하트비트를 시작하려면,
* `DeleteInstance`  매치가 끝났을 때 / 새 플레이어의 입장을 방지하려면,
* `ConfirmReservation`  플레이어가 참여할 때, 식별과 슬롯 할당을 확인하려면,
* `UpdateSlot`  슬롯 용량을 업데이트하거나(플레이어 참가/이탈 시) 메타데이터를 수정하려면,
* `UpdateInstance`  인스턴스 메타데이터를 수정하려면,
* `Status`  Server Browser 서비스 상태를 확인하려면.

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

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

에이전트는 실행 중 서버를 계속 발견 가능하도록 하트비트를 자동으로 유지합니다. 에이전트가 여러 번 연속된 하트비트 동안(설정 가능) 서버 브라우저에 도달하지 못하면:

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

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

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

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

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

{% hint style="info" %}
예기치 않은 충돌이 발생한 경우를 대비해, 플레이어가 포기되기 전에 재연결할 수 있도록 짧은 시간을 허용하세요.
{% endhint %}

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

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

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

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

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

이 시점부터 에이전트의 핸들러가 제어권을 가져가 에이전트 함수를 호출해야 합니다:

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

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

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

## 🧪 샘플

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

### 자동 할당

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

### 사용자 지정 검색

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

## ⚙️ 사용자 지정

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

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

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

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

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

{% hint style="warning" %}
다음에 대해 충분히 익숙해지세요. [Server Browser 심화](/docs.edgegap.com-ko/learn/server-browser.md) 사용자 지정 작업을 하기 전에 이러한 개념에 대해.
{% 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>요청 타임아웃이 하트비트로 제한됨 [{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>하트비트 정상</code></td><td>하트비트가 정상적으로 완료되었습니다.</td></tr><tr><td>🟡 <code>경고</code></td><td><code>하트비트 실패 [{consecutive}/{maximum}]</code></td><td>하트비트 실패, 서버가 Server Browser에 도달하지 못했습니다.</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>슬롯 업데이트가 대기열에 추가됨 [{slot}]</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>에이전트가 동시 슬롯 업데이트를 제한함</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>다음 배치(하트비트/그리디)를 위해 확인을 대기열에 넣었습니다.</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>

### 클라이언트 이벤트

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

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]: 데이터 전송 객체


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.edgegap.com/docs.edgegap.com-ko/unity/server-browser.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
