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


# 매일 플레이할 이유 만들기

3일간의 로그인 보너스와 모험을 세 번 클리어하는 미션을 추가합니다.



로그인한 날에 보상을 받고 모험을 세 번 클리어하면 추가 보상도 받도록 만듭니다. 지금까지 사용한 학습용 프로젝트와 계정으로 진행합니다.

## 1. 로그인 보너스 설정하기

1일 차에는 10코인, 2일 차에는 20코인, 3일 차에는 30코인을 지급합니다. 날짜는 일본 시간(JST, UTC+9) 자정에 바뀝니다. 접속하지 않은 날이 있어도 마지막으로 받은 보상 다음부터 이어서 받도록 합니다.


**Studio**

**Add features** 에서 **Login Rewards** 와 **Missions** 를 추가합니다.

**Tables → Login Rewards → LoginRewardCollection** 을 열고 다음 행을 만듭니다.

| 항목 | 입력할 값 |
| --- | --- |
| ID | `daily` |
| 진행 방식(mode) | `streaming` |
| 날짜 갱신 시각(resetHour) | `15`(UTC 15시 = 일본 시간 자정) |
| 반복(repeat) | `disabled` |
| 놓친 보상 구제(missedReceiveRelief) | `disabled` |

**LoginReward** 에 `day1`, `day2`, `day3` 행을 추가하고 보너스 그룹을 모두 `daily`로 설정합니다.

각 행에서 **Acquire actions → Add item → Not set** 을 엽니다. 액션으로 `DepositByUserId`(money2), 대상 리소스로 **None** 을 선택합니다.

| 항목 | 입력할 값 |
| --- | --- |
| namespaceName | `Currency` |
| userId | `#{userId}` |
| slot | `0` |

**depositTransactions** 에 항목을 하나 추가합니다. `price`는 `0`, `currency`는 **No value** 로 설정합니다. `count`는 `day1`이 `10`, `day2`가 `20`, `day3`이 `30`입니다.


**SDK (Unity)**

<a href="/samples/adventure/ko/extensions/daily-sdk.json" download>로그인 보너스와 미션 템플릿</a>을 다운로드합니다.

1. 관리 콘솔의 Tokyo 리전에서 **Deploy → Stack list → Create stack** 을 엽니다.
2. 스택 이름을 `adventure-daily`로 지정하고 **Template data** 에서 다운로드한 JSON 파일을 선택한 뒤 생성합니다.
3. 상태가 **Create complete** 가 되면 `adventure-login-reward`와 `adventure-mission`을 사용할 수 있습니다.

로그인 보너스 설정은 `LoginRewardMaster → Properties → Settings → bonusModels`에 있습니다.

| 항목 | 설정값 |
| --- | --- |
| name | `daily` |
| mode | `streaming` |
| resetHour | `15` |
| repeat | `disabled` |
| missedReceiveRelief | `disabled` |
| rewards | 10, 20, 30코인 입금 세 건을 이 순서대로 설정 |

이 스택은 로그인 보너스와 미션 설정만 추가합니다. 모험, 코인, 스태미나는 기존 `adventure-game` 스택을 사용합니다.




## 2. 모험을 세 번 클리어하는 미션 만들기

모험을 클리어할 때마다 1씩 증가하는 미션 카운터를 만듭니다. 세 번에 도달하면 버튼으로 30코인을 받을 수 있게 합니다.


**Studio**

**Tables → Missions → MissionSetting** 을 열고 **Add row** 를 누릅니다. 리셋 시각은 `15`, 주간 리셋 요일은 `sunday`, 월간 리셋 날짜는 `1`로 설정합니다.

다음 세 표에 행을 추가합니다.

| 표 | ID | 설정 |
| --- | --- | --- |
| 미션 카운터(MissionCounter) | `clear` | ID만 입력 |
| 미션 그룹(MissionCollection) | `adventure` | 리셋 단위 `notReset`, 개최 일정은 비워 둠 |
| 미션(Mission) | `clear3` | 미션 그룹 `adventure`, 진행 카운터 `clear`, 목표값 `3` |

`clear3`의 **Completion rewards** 에 `DepositByUserId`(money2)를 추가합니다. 로그인 보너스와 동일하게 `namespaceName = Currency`, `userId = #{userId}`, `slot = 0`으로 설정합니다. `depositTransactions`의 `price`는 `0`, `currency`는 값 없음, `count`는 `30`입니다.

**Tables → Quests → Quest** 를 엽니다. `forest`와 `cave`의 **Completion rewards** 에 각각 다음 액션을 하나씩 추가합니다. 기존 코인 입금 액션은 그대로 둡니다.

| 항목 | 입력할 값 |
| --- | --- |
| 액션 | `IncreaseCounterByUserId`(mission) |
| 대상 리소스 | None |
| namespaceName | `Mission` |
| userId | `#{userId}` |
| counterName | `clear` |
| value | `1` |

저장하고 **Publish → Calculate server differences** 에서 배포한 뒤 **Update the Unity project** 를 실행합니다.


**SDK (Unity)**

앞서 만든 `adventure-daily`에 다음 미션이 들어 있습니다.

| 설정 | 값 |
| --- | --- |
| 그룹 | `adventure`, 리셋 없음 |
| 카운터 | `clear`, 누적값 |
| 미션 | `clear3`, 목표값 `3` |
| 보상 | `adventure-coin`의 슬롯 `0`에 30코인 |

`adventure-game`에 사용한 템플릿을 엽니다. `forest`와 `cave`의 `contents → completeAcquireActions`에 기존 입금 액션과 같은 깊이로 다음 항목을 추가합니다.

```yaml
- action: Gs2Mission:IncreaseCounterByUserId
  request: |-
    {"namespaceName":"adventure-mission","userId":"#{userId}","counterName":"clear","value":1}
```

**Deploy → adventure-game → Update stack** 을 열고 **Template data** 에서 수정한 파일을 선택해 업데이트합니다. **Update complete** 가 될 때까지 기다립니다. 이제 퀘스트 클리어 보상으로 코인 입금과 카운터 증가를 함께 실행할 수 있습니다.




## 3. Unity에 보상 받기 화면 추가하기


**Studio**

기존 모험 화면에 로그인 보너스와 미션 보상 버튼을 추가합니다. 화면 표시에는 계속 `OnGUI()`를 사용합니다.

### Handler 추가하기

Unity의 재생을 멈추고 Hierarchy의 `Gs2`에 다음 컴포넌트를 추가합니다.

- Login Reward Collection Handler
- Mission Handler
- Mission Counter Handler

### 모험 화면에 보상 버튼 추가하기

1. <a href="/samples/adventure/ko/extensions/studio/AdventureStudioView.cs" download>이 장의 AdventureStudioView.cs</a>를 다운로드해 기존 `Assets/AdventureStudioView.cs`를 교체합니다.
2. `Gs2`의 **Adventure Studio View** 에서 새로 생긴 **Login Reward**, **Mission**, **Counter** 에 해당 Handler를 할당합니다. **Mission Group** 은 `adventure`, **Mission Name** 은 `clear3`으로 설정합니다.
3. 재생 후 로그인하면 스태미나 상점 아래에 보상 버튼과 진행 상황이 나타납니다.

기존 `OnGUI()`에서 추가한 `DrawDailyRewards()`를 호출합니다. 버튼을 눌렀을 때의 처리는 모험 시작·클리어와 같은 방식으로 작성합니다.

```csharp
if (GUILayout.Button("오늘의 로그인 보너스 받기"))
    Run(() => loginReward.Binder.Receive());

if (GUILayout.Button("미션 보상 30코인 받기"))
    Run(() => mission.Binder.Receive());
```

배포 코드에서는 로딩 중이나 처리 중에는 조작을 막고, 미션을 달성하면 보상 버튼을 누를 수 있게 합니다.

Binder 내부에서 트랜잭션 완료까지 기다리므로 화면에서 `WaitAsync()`를 호출할 필요가 없습니다. 표시할 때는 Handler의 `Model`을 참조합니다. 보상을 받거나 모험을 클리어해서 생긴 변경도 Handler에 반영됩니다.

재생 후 로그인하고 **오늘의 로그인 보너스 받기** 를 누릅니다. 1일 차가 ‘받음’으로 바뀌고 모험 화면의 코인이 10개 늘어납니다. 같은 날 다시 눌러도 코인은 늘지 않습니다.

다음으로 숲 탐험을 세 번 클리어합니다. 배포 후 카운터 추가가 적용되기까지 최대 5분이 걸릴 수 있습니다. 횟수가 늘지 않으면 잠시 기다린 뒤 숲 탐험을 새로 시작해 클리어합니다. 누적 클리어 횟수가 `3`이 되면 **미션 보상 30코인 받기** 를 누를 수 있습니다. 받으면 ‘받음’으로 바뀌고 일반 모험 보상에 더해 30코인을 얻습니다.


**SDK (Unity)**

재생을 멈추고 빈 GameObject를 만들어 `Extensions`라고 이름 붙입니다. <a href="/samples/adventure/ko/extensions/sdk/AdventureDaily.cs" download>AdventureDaily.cs</a>를 `Assets/AdventureExtensions`에 넣고 `Extensions`에 **Adventure Daily** 를 추가합니다. **Login** 에 씬의 `AdventureLogin`을 할당하고 **Mission Group** 은 `adventure`, **Mission Name** 은 `clear3`으로 설정합니다.

배포 코드의 `Client`는 GS2 클라이언트, `Session`은 로그인한 플레이어의 세션을 반환하는 프로퍼티입니다. 둘 다 `AdventureLogin`에서 가져옵니다.

보상 버튼은 `RunWrite()`를 통해 호출합니다. 여기서 요청에 키를 붙이고 `ReceiveLogin()`이나 `ReceiveMission()`의 `requestId`로 전달합니다. 통신 오류가 나면 **같은 작업 재시도** 로 동일한 요청을 보낼 수 있습니다. 8장과 마찬가지로 재생을 유지한 채 연결이 복구되면 바로 재시도합니다.

이 샘플은 `OnGUI()`로 모험 화면 오른쪽에 보상 버튼과 진행 상황을 표시합니다. Game 뷰를 넓혀 두 화면이 모두 보이게 한 뒤 재생하고 로그인합니다. **오늘의 로그인 보너스 받기** 를 누르면 1일 차가 ‘받음’으로 바뀌고 왼쪽 코인이 10개 늘어납니다. 다시 눌러도 같은 날의 보상은 중복 지급되지 않습니다.

보상 수령 상태와 미션 진행 상황은 `SubscribeWithInitialCallAsync()`로 구독합니다. 모험을 클리어하거나 보상을 받으면 변경 알림에 따라 화면이 갱신됩니다.

날짜가 바뀌면 2일 차, 3일 차 순서로 받을 수 있습니다. 보상 수령에 사용하는 API는 다음과 같습니다.

```csharp
private async UniTask ReceiveLogin(string requestId)
{
    var transaction = await Client.LoginReward.Namespace(LoginNamespace).Me(Session).Bonus()
        .ReceiveAsync("daily", speculativeExecute: false, duplicationAvoider: requestId);
    if (transaction != null) await transaction.WaitAsync(all: true);
}
```

다음으로 왼쪽 모험 화면에서 숲 탐험을 세 번 클리어합니다. 배포 후 카운터 추가가 적용되기까지 최대 5분이 걸릴 수 있습니다. 횟수가 늘지 않으면 잠시 기다린 뒤 숲 탐험을 새로 시작해 클리어합니다. 오른쪽 누적 횟수가 `3`이 되면 **미션 보상 30코인 받기** 를 누릅니다. ‘받음’으로 바뀌며 일반 모험 보상에 더해 왼쪽 코인이 30개 늘어납니다.

```csharp
private async UniTask ReceiveMission(string mission, string requestId)
{
    var transaction = await Client.Mission.Namespace(MissionNamespace).Me(Session).Complete(missionGroup)
        .ReceiveRewardsAsync(mission, speculativeExecute: false, duplicationAvoider: requestId);
    if (transaction != null) await transaction.WaitAsync(all: true);
}
```




## 4. 월요일 한정 미션 추가하기

월요일에 모험을 한 번 클리어하면 30코인을 받는 미션을 추가합니다. 월요일에 달성해 두면 화요일부터 일요일 사이에도 보상을 받을 수 있습니다. 다음 월요일 자정에 달성·수령 상태가 초기화되어 다시 도전할 수 있습니다. 이 예제의 요일과 시각은 일본 시간(JST, UTC+9) 기준입니다.

기존 `clear3`은 그대로 두고 주간 그룹 `weekly`에 `monday_clear`를 추가합니다.


**Studio**

### 월요일 일정 만들기

**Tables → Scheduling → Schedule** 에 다음 행을 추가합니다. 시작·종료 일시는 셀을 열고 **Local (UTC+9)** 로 전환한 뒤 날짜와 시각을 선택합니다.

| 항목 | 입력할 값 |
| --- | --- |
| ID | `monday` |
| 일정 방식 | `absolute` |
| 시작 일시 | `2020-01-01 00:00`(일본 시간) |
| 종료 일시 | `2099-01-01 00:00`(일본 시간) |
| 반복 방식 | `weekly` |
| 시작 요일 | `sunday` |
| 시작 시각 | `15` |
| 종료 요일 | `monday` |
| 종료 시각 | `15` |

시작·종료 일시는 이 주간 이벤트를 운영할 전체 기간입니다. 반복 설정으로 그 기간 중 매주 월요일에만 활성화합니다. 요일과 시각 필드는 UTC 기준이므로 일본 시간 월요일 0시부터 화요일 0시까지를 `sunday 15`부터 `monday 15`로 지정합니다.

### 주간 미션 만들기

리셋 시각은 일본 시간 월요일 자정입니다(미션 설정의 `sunday`, `15`).

1. **MissionCollection** 에 `weekly`를 추가합니다. 리셋 단위는 `weekly`, 개최 일정은 `monday`로 설정합니다.
2. **Mission** 에 `monday_clear`를 추가합니다. 그룹은 `weekly`, 카운터는 `clear`, 목표값은 `1`로 설정합니다. 완료 보상은 `clear3`과 같은 30코인입니다.
3. 저장하고 **Publish → Calculate server differences** 에서 배포한 뒤 **Update the Unity project** 를 실행합니다.
4. 재생을 멈추고 `Gs2`의 **Adventure Studio View** 에서 **Mission Group** 을 `weekly`, **Mission Name** 을 `monday_clear`로 바꿉니다.


**SDK (Unity)**

1. <a href="/samples/adventure/ko/extensions/daily-period-sdk.json" download>월요일 한정 미션 템플릿</a>을 다운로드합니다.
2. **Deploy → adventure-daily → Update stack** 을 열고 **Template data** 에서 파일을 선택해 업데이트합니다. **Update complete** 가 될 때까지 기다립니다.
3. 재생을 멈추고 **Adventure Daily** 의 **Mission Group** 을 `weekly`, **Mission Name** 을 `monday_clear`로 바꿉니다.

템플릿에는 로그인 보너스와 기존 `clear3`에 더해 다음 설정이 들어 있습니다. 날짜를 수정할 필요는 없습니다.

| 설정 | 내용 |
| --- | --- |
| 일정 `monday` | 매주 월요일 0시부터 화요일 0시까지 활성화(일본 시간) |
| 그룹 `weekly` | 매주 월요일 0시에 달성·수령 상태 초기화(일본 시간) |
| 미션 `monday_clear` | 카운터 `clear`, 목표값 `1`, 보상 30코인 |

일정의 반복 설정은 다음과 같습니다. GS2의 요일과 시각은 UTC 기준이므로 일본 시간 월요일 0시부터 화요일 0시까지를 일요일 15시부터 월요일 15시로 지정합니다.

```json
"repeatSetting": {
  "repeatType": "weekly",
  "beginDayOfWeek": "sunday",
  "beginHour": 15,
  "endDayOfWeek": "monday",
  "endHour": 15
}
```

미션의 `challengePeriodEventId`가 이 일정을 참조합니다. 그룹 리셋도 `resetType = weekly`, `resetDayOfWeek = sunday`, `resetHour = 15`로 설정되어 있습니다.




배포 후 추가한 미션 설정이 적용되기까지 최대 5분이 걸릴 수 있습니다. 5분을 기다린 뒤 재생하고 로그인해 숲 탐험을 한 번 클리어합니다. 월요일이면 ‘미션 보상 30코인을 받을 수 있습니다’라고 표시됩니다. 화요일부터 일요일에는 클리어해도 미션이 달성되지 않습니다.

카운터는 기존 누적값을 사용합니다. 모험 클리어로 카운터가 증가할 때 달성 여부를 판정하므로, 누적값이 이미 1 이상이어도 월요일에 한 번 더 클리어해야 합니다.

| 시점 | 미션 동작 |
| --- | --- |
| 월요일에 모험 클리어 | 미션을 달성하고 30코인을 받을 수 있음 |
| 화요일부터 일요일 | 새로 달성할 수 없음. 월요일에 달성한 보상은 받을 수 있음 |
| 다음 월요일 0시 | 달성·수령 상태 초기화. 받지 않은 지난주 보상은 이월되지 않음 |

[다음: 아이템과 상점 만들기]()



