> For the complete documentation index, see [llms.txt](/ko/llms.txt)


# 이벤트 열기

이벤트를 설정하고 개최 전후의 동작을 확인한 뒤 공지와 보상 지급을 추가합니다.



기간 한정 ‘숲 축제’를 추가합니다. 스태미나 5를 사용하고 클리어하면 30코인을 받는 모험입니다. 받은 편지함으로 공지를 보내고, 마지막으로 플레이어 A에게 10코인이 첨부된 메시지를 보냅니다.

## 1. 이벤트 확인 화면 추가하기

재생을 멈추고 `Extensions`의 **Adventure Social** 체크를 해제합니다. 로그인과 모험 화면은 그대로 사용합니다.


**Studio**

Studio의 **Add features** 에서 **Inbox** 를 추가하고 저장한 뒤 **Publish → Calculate server differences** 에서 배포합니다. 이어서 **Update the Unity project** 를 실행합니다. Unity의 `Gs2`에 **Message List Handler** 를 추가합니다. **Auto Subscribe** 는 켜 두고 **Item Prefab** 과 **Content Parent** 는 비워 둡니다. 이 장의 확인 화면이 Handler의 목록으로 메시지를 표시합니다.

<a href="/samples/adventure/ko/extensions/studio/AdventureEventView.cs" download>AdventureEventView.cs</a>를 `Assets/AdventureExtensions`에 넣고 `Extensions`에 **Adventure Event View** 를 추가합니다. **Messages** 에 방금 추가한 **Message List Handler** 를 할당합니다.


**SDK (Unity)**

<a href="/samples/adventure/ko/extensions/sdk/AdventureEventView.cs" download>AdventureEventView.cs</a>를 `Assets/AdventureExtensions`에 넣고 `Extensions`에 **Adventure Event View** 를 추가합니다. **Login** 에 씬의 `AdventureLogin`을 할당합니다.




계정 A로 재생하고 로그인하면 오른쪽에 개최 시각 입력란과 받은 편지함이 나타납니다.

개최 시각 입력란에는 지금부터 몇 분 뒤에 시작하고 종료할지 지정합니다. 위에 `15`, 아래에 `30`을 입력하고 **시작·종료 시각 만들기** 를 누릅니다. 표시된 시각을 다음 설정에 사용합니다.

## 2. 개최 기간과 모험 등록하기


**Studio**

### 개최 기간 만들기

**Tables → Scheduling → Schedule** 에서 행을 추가합니다. 시작·종료 일시 셀을 각각 열고 **Local** 로 전환한 뒤 확인 화면에 표시된 날짜와 시각을 선택합니다.

| 항목 | 값 |
| --- | --- |
| ID | `festival` |
| 일정 방식 | `absolute` |
| 시작 일시 | 확인 화면의 ‘Studio 시작 시각’ |
| 종료 일시 | 확인 화면의 ‘Studio 종료 시각’ |
| 반복 방식 | `always` |

### 모험 만들기

1. **Tables → Quests → QuestCollection** 에 ID가 `event`인 행을 추가합니다. **Schedule** 은 `festival`을 선택합니다.
2. **Quest** 에 ID `event_forest`, 그룹 `event`, `displayName`이 ‘숲 축제’인 행을 추가합니다.
3. **Start costs** 에 `ConsumeStaminaByUserId`를 하나 추가합니다. `namespaceName = Energy`, `userId = #{userId}`, `staminaName = stamina`, `consumeValue = 5`를 입력합니다.
4. **Completion rewards** 에 `DepositByUserId`를 하나 추가합니다. `namespaceName = Currency`, `userId = #{userId}`, `slot = 0`으로 설정합니다. `depositTransactions`에 항목을 하나 추가하고 `price = 0`, `currency`는 값 없음, `count = 30`으로 설정합니다.
5. 저장한 뒤 **Publish** 에서 서버에 배포하고 Unity 프로젝트를 업데이트합니다.
6. 재생을 멈추고 `Gs2`의 **Quest List Handler → Collection** 을 `event`로 바꿉니다. 다시 재생하고 로그인합니다.


**SDK (Unity)**

### 개최 기간 만들기

1. <a href="/samples/adventure/ko/extensions/events.yaml" download>이벤트 설정 템플릿</a>을 저장합니다.
2. `START_AT`과 `END_AT`을 확인 화면의 ‘SDK 템플릿’에 표시된 두 숫자로 바꿉니다. 숫자를 따옴표로 감싸지 않습니다.
3. 같은 파일의 `festival-news` → `metadata`에 시작·종료 시각을 포함한 공지문을 작성합니다.
4. 관리 콘솔의 **Deploy → Stack list → Create stack** 에서 이름을 `adventure-events`로 지정하고 **Template data** 에서 수정한 파일을 선택해 생성합니다.

### 모험 만들기

지금까지 수정한 `adventure-game` 템플릿에서 `QuestMaster` → `Settings` → `groups` 끝에 다음 그룹을 추가합니다. 기존 `main` 그룹은 그대로 둡니다.

```yaml
- name: event
  challengePeriodEventId: grn:gs2:{region}:{ownerId}:schedule:adventure-events:event:festival
  quests:
    - name: event_forest
      metadata: 숲 축제
      consumeActions:
        - action: Gs2Stamina:ConsumeStaminaByUserId
          request: '{"namespaceName":"adventure-stamina","userId":"#{userId}","staminaName":"stamina","consumeValue":5}'
      contents:
        - weight: 1
          completeAcquireActions:
            - action: Gs2Money2:DepositByUserId
              request: '{"namespaceName":"adventure-coin","userId":"#{userId}","slot":0,"depositTransactions":[{"price":0,"currency":"","count":30}]}'
```

**Deploy → adventure-game → Update stack** 에서 **Template data** 에 수정한 파일을 선택해 업데이트합니다.

Unity에서 `AdventurePlayer.cs`의 `LoadQuests()`에 있는 `QuestGroupModel("main")`과 `AdventurePlay.cs`의 `BeginQuest()`에 있는 `StartAsync("main", ...)` 두 곳의 `"main"`을 `"event"`로 바꿉니다. 재생하고 로그인합니다.




목록에 ‘숲 축제’가 있는지 확인합니다. 배포 후 모험 추가가 적용되기까지 최대 5분이 걸릴 수 있습니다. 아직 보이지 않으면 잠시 기다린 뒤 Unity 재생을 다시 시작합니다.

## 3. 개최 전·중·후에 플레이하기

개최 기간을 바꾸며 다음 순서대로 확인합니다. 배포 후 설정 적용까지 최대 5분이 걸릴 수 있으므로 각 단계에서 배포 후 5분을 기다린 뒤 Unity 재생을 다시 시작하고 로그인합니다.

1. 확인 화면에서 시작을 `15`분 뒤, 종료를 `30`분 뒤로 지정해 시각을 만듭니다. Studio는 `festival`의 시작·종료 일시를, SDK는 `adventure-events` 템플릿의 `absoluteBegin`과 `absoluteEnd`를 이 값으로 업데이트하고 배포합니다. ‘숲 축제’를 시작하면 개최 기간 밖이라 시작할 수 없습니다.
2. 시작 `-5`, 종료 `30`으로 시각을 다시 만들고 같은 설정을 업데이트해 배포합니다. 스태미나가 5 이상인지 확인하고 시작·클리어합니다. 스태미나가 5 줄고 코인이 30 늘어납니다.
3. 시작 `-30`, 종료 `-15`로 업데이트하고 배포합니다. 다시 시작하면 개최 기간 밖이라 시작할 수 없습니다.

이 설정은 새 모험을 시작할 수 있는 기간을 정합니다. 2번에서는 클리어한 뒤 종료 후 설정으로 변경합니다.

## 4. 받은 편지함에 공지 보내기

이 예제의 공지는 받은 편지함으로 전달되는 전체 메시지로 만듭니다. 확인 화면의 분 값을 `-5`와 `30`으로 바꾸고 시작·종료 시각을 다시 만듭니다.


**Studio**

`festival` 일정의 시작·종료 일시를 새로 만든 시각으로 바꿉니다.

1. **Tables → Inbox → GlobalMessage** 에 행을 추가합니다.
2. ID를 `festival-news`로 지정합니다. **Message content** 에 ‘숲 축제: 스태미나 5로 도전하고 클리어하면 30코인. 개최 기간: …’을 입력하고 실제 시작·종료 시각을 적습니다.
3. **Delivery start** 와 **Delivery end** 셀을 각각 열고 **Local** 로 전환해 확인 화면에서 만든 날짜와 시각을 선택합니다.
4. 저장하고 서버에 배포한 뒤 Unity 프로젝트를 업데이트합니다.


**SDK (Unity)**

공지는 `adventure-events` 템플릿에 포함되어 있습니다. `festival`의 `absoluteBegin`과 `absoluteEnd`를 확인 화면의 숫자로 바꾸고 `festival-news`의 `metadata`에 새 개최 시각을 적습니다. **Deploy → adventure-events → Update stack** 에서 반영합니다.




배포 후 5분을 기다리고 **공지 수신 및 목록 갱신** 을 누릅니다. 받은 편지함에 본문이 표시됩니다.


**Studio**

전체 메시지는 SDK의 `ReceiveGlobalMessageAsync()`로 수신한 뒤 **Message List Handler** 에 목록을 불러옵니다. 이 갱신 버튼은 콘솔에서 새로 보낸 메시지도 가져옵니다. 개봉 후에는 Handler의 변경 알림으로 화면이 갱신됩니다.

```csharp
var user = Client.Inbox.Namespace(Inbox).Me(Session);
await user.ReceiveGlobalMessageAsync();
user.InvalidateMessages();
await messages.ReloadAsync();
```


**SDK (Unity)**

확인 화면에서는 전체 메시지를 수신한 뒤 목록을 불러옵니다. 콘솔에서 보낸 메시지도 여기서 가져옵니다. 개봉 후에는 `SubscribeMessages()`의 변경 알림으로 화면을 갱신합니다.

```csharp
var user = Client.Inbox.Namespace(Inbox).Me(Session);
await user.ReceiveGlobalMessageAsync();
user.InvalidateMessages();
var items = new List<EzMessage>();
await foreach (var item in user.MessagesAsync())
    if (item != null) items.Add(item);
UpdateMessages(items.ToArray());
var id = user.SubscribeMessages(UpdateMessages);
```




## 5. 플레이어 A에게 10코인 보내기

1. 관리 콘솔의 Tokyo 리전에서 **Inbox** 를 엽니다. Studio는 `Inbox`, SDK는 `adventure-inbox`를 선택합니다.
2. **User data** 에 게임 화면에 표시된 A의 플레이어 ID를 입력합니다.
3. 메시지 목록에서 **Send message** 를 엽니다. **Metadata** 에 ‘참여해 주셔서 감사합니다! 10코인을 보내드립니다.’를 입력합니다.
4. **Acquire action on read** 를 하나 추가하고 **GS2-Money2 DepositByUserId** 를 선택합니다. **Namespace name** 을 **Text** 로 전환하고 Studio는 `Currency`, SDK는 `adventure-coin`을 입력합니다. **User ID** 는 `#{userId}`, **Slot** 은 `0`을 그대로 사용합니다.
5. **Deposit transactions → Add row** 를 누르고 **Purchase price** 를 `0`, **Currency amount** 를 `10`으로 설정합니다. **Deposit date** 는 끈 상태로 둡니다. 구매 가격이 0이므로 무상 코인 10개를 지급하는 설정입니다.

6. **Send** 를 누릅니다. Unity로 돌아가 코인 잔액을 확인하고 **공지 수신 및 목록 갱신** 을 누릅니다.
7. 도착한 ‘참여해 주셔서 감사합니다!’ 메시지에서 **개봉하고 보상 받기** 를 누릅니다. 완료 후 왼쪽 모험 화면의 코인이 10개 늘었는지 확인합니다.
8. 같은 메시지의 버튼이 **개봉 완료** 로 바뀌고 다시 받을 수 없는지 확인합니다.


**Studio**

목록의 각 메시지는 Binder입니다. `Read()`를 호출하면 개봉과 보상 수령이 끝날 때까지 기다립니다. 화면에서 트랜잭션을 기다리거나 개봉 후 다시 불러올 필요는 없습니다.

```csharp
private async UniTask Read(IActionableMessageBinder item)
{
    await item.Read();
    message = "개봉했습니다";
}
```


**SDK (Unity)**

`ReadAsync()`로 메시지를 개봉하고 보상을 받은 뒤 반환된 트랜잭션의 완료를 기다립니다. 개봉 상태 표시는 메시지 변경 알림으로 갱신됩니다.

```csharp
private async UniTask Read(string name, string requestId)
{
    var transaction = await Client.Inbox.Namespace(Inbox).Me(Session)
        .Message(name).ReadAsync(speculativeExecute: false, duplicationAvoider: requestId);
    if (transaction != null) await transaction.WaitAsync(all: true);
}
```




[다음: 샘플과 참고 자료]()



