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

# GS2-Enhance

강화 기능




이 기능은 개발자에 따라서는 전혀 이해가 안 될 수도 있는 기능일 것입니다.

하지만 일본을 비롯한 모바일 게임의 사실상 표준 게임 시스템에는 반드시 존재하는 기능입니다.
이 게임 메커니즘 자체는 Game as a Service를 실현하는 데 유용한 지식이 될 것이므로, 이해가 어려운 개발자를 위해 게임 메커니즘에 대한 해설을 추가합니다.

강화 기능의 게임 메커니즘에 대해 충분한 지식이 있는 경우에는 이어지는 해설 섹션은 건너뛰어도 무방합니다.

## 강화 기능의 게임 메커니즘

강화 기능 자체는 매우 단순한 게임 시스템으로, 소재 아이템을 소비함으로써 대상의 경험치를 가산할 수 있는 구조입니다.
배틀에 참가해 경험치를 얻는 방법뿐만 아니라, 다양한 방법으로 캐릭터나 장비를 육성할 수 있게 되어 있습니다.
(장비에도 레벨이 있습니다)

그럼 이 게임 시스템이 왜 Game as a Service를 실현하는 데 한몫하고 있는지 설명해 보겠습니다.

한마디로 말하면, 플레이 시간을 늘리는 데 활약하고 있습니다.

개발 속도보다 게임 플레이 속도가 압도적으로 빠르다는 것은 여러분도 충분히 이해하고 계실 것입니다.
우리 게임 개발자가 3년에 걸쳐 개발한 게임을 플레이어는 10시간 만에 끝내버리는 것입니다.
하지만 이 격차를 메우지 않으면 Game as a Service는 성립하지 않습니다.
즉, 플레이어의 콘텐츠 소비 속도에 디버프를 걸 필요가 있습니다. 그 마법이 바로 강화 게임 시스템입니다.

일반적인 Game as a Service 게임은 매달 한 번은 이벤트를 실시합니다.
그 이벤트에는 어떤 형태로든 보스가 존재하며, 플레이어는 그 보스를 이벤트 기간 동안 반복해서 토벌하게 됩니다.
보스는 플레이어의 성장 단계에 맞춰 여러 난이도를 준비해 두고, 토벌함으로써 캐릭터나 장비의 강화 소재를 얻을 수 있습니다.

모은 강화 소재를 사용해 캐릭터나 장비의 경험치로 전환해 성장시킵니다.
캐릭터나 장비를 성장시키면 더 높은 난이도의 보스에 도전할 수 있게 됩니다.

이렇게 함으로써 모든 플레이어가 이벤트에 참여하면서, 자신의 캐릭터 성장 단계에 맞는 강화 소재를 입수하고, 캐릭터나 장비를 성장시키는 재미를 즐길 수 있습니다.

실제로는 강화 소재를 보스가 직접 드롭하는 경우는 적을 수도 있습니다.
대신 이벤트 기간에만 도전할 수 있는 가챠가 있어, 그 가챠를 뽑기 위한 포인트를 모아 가챠를 통해 소재를 얻거나
이벤트 기간에만 개점하는 상점에서 보스를 토벌해 얻을 수 있는 게임 내 통화를 모아 상점에서 강화 소재를 구매하는 등
플레이어의 판단으로 어디를 우선해서 강화할지 결정할 수 있도록 하는 장치는 있지만, 최종적으로는 플레이어가 캐릭터 육성에 필요한 시간을 늘려 플레이 시간을 부풀리게 됩니다.

이벤트를 통해 일관되게 변하지 않는 것은 플레이어의 캐릭터가 강해진다는 점입니다.
이벤트가 끝나면 강해진 캐릭터를 사용해 더 높은 난이도의 상시 콘텐츠를 즐기는 등, 다음 재미로 이어집니다.

```mermaid
graph TD
  BossBattle["Boss Battle"] -- Acquire Event Point --> Shop
  Shop -- Buy Enhance Materials --> Enhance["Enhance Character"]
  Enhance -- More Formidable --> BossBattle
```

## 아키텍처

강화는 소재가 되는 아이템을 관리하는 GS2-Inventory와, 강화 대상인 캐릭터나 장비를 관리하는 GS2-Inventory 또는 GS2-Dictionary.
그리고 그 캐릭터나 장비의 경험치·레벨을 관리하는 GS2-Experience를 GS2-Enhance를 통해 조작함으로써 실현하고 있습니다.

현재 GS2에서는 1회의 API 요청으로 소비와 경험치 가산을 완결하는 `DirectEnhance`를 이용한 강화를 권장하고 있습니다.
이전에는 트랜잭션 자동 실행 기능이 없었기 때문에, 대성공이 나올 때까지 통신을 차단하고 다시 시도하는 행위를 방지할 목적으로, 준비·실행·완료 보고 단계로 나누어진 강화 프로세스(`Progress`)를 권장했지만, 지금은 트랜잭션 자동 실행이나 원자적 커밋(atomic commit) 구조가 갖추어져 그 우려는 해소되었습니다.

```plantuml
actor Player
participant "GS2-Enhance#Namespace"
participant "GS2-Inventory#ItemSet(Material)"
participant "GS2-Experience#Status"
Player -> "GS2-Enhance#Namespace" : Direct Enhance(Materials/Target Character)
"GS2-Enhance#Namespace" -> "GS2-Inventory#ItemSet(Material)" : Get experience value from metadata
"GS2-Enhance#Namespace" -> "GS2-Inventory#ItemSet(Material)" : Consume
"GS2-Enhance#Namespace" -> "GS2-Experience#Status": Add experience(Key: Target Character/Equipment Id)
"GS2-Enhance#Namespace" -> Player : Enhance result
```

GS2-Enhance에 "강화 대상", "강화에 사용할 소재"를 파라미터로 하여 강화 실행 API(`DirectEnhance`)를 호출합니다.

그러면 GS2-Enhance는 소재가 되는 아이템의 마스터 데이터를 GS2-Inventory에서 가져와, 메타데이터 내에 기록되어 있는 소재로 사용했을 때의 경험치량을 취득합니다.
경험치량이 확정되면 아이템을 소비하고 경험치 가산 처리를 GS2-Experience에서 실행합니다.

강화 대상을 지정하고는 있지만, GS2-Experience의 경험치를 관리하는 키로 사용될 뿐이며, 강화 대상의 정보를 직접 사용하는 일은 없습니다.

## DirectEnhance와 Progress의 차이

GS2-Enhance에서는 두 가지 강화 흐름을 제공하고 있습니다.

| 항목 | DirectEnhance | Progress (Start / End) |
| -- | -- | -- |
| API 호출 횟수 | 1회 | 2회(시작·종료) |
| 권장 용도 | 모든 강화 처리 | 대성공 등 추첨 결과를 연출에 반영하기 위해, 추첨 결과를 먼저 취득해 두고 싶은 경우 |
| 원자성 | 요청 내에서 소비·경험치 가산이 완결 | 서버에서 추첨한 결과를 유지하고, 이후 End에서 확정 |
| 통신 차단에 의한 부정 이용 | 발생하지 않음 | 진행 중인 `Progress`는 동시에 1건만 유지할 수 있어, `DeleteProgress`를 통해서만 재시도 가능 |

특별한 요건이 없는 한 `DirectEnhance` 이용을 권장합니다.

```mermaid
sequenceDiagram
  participant Player
  participant Enhance as GS2-Enhance
  participant Inventory as GS2-Inventory
  participant Experience as GS2-Experience
  Player ->> Enhance: DirectEnhance(rateName, targetItemSetId, materials)
  Enhance ->> Inventory: 메타데이터에서 경험치량 취득
  Enhance ->> Inventory: 소재 아이템 소비
  Enhance ->> Experience: 경험치 가산
  Enhance -->> Player: 가산 경험치·대성공 배율
```

## 강화 레이트

강화에 사용할 수 있는 소재나 강화 대상을 한정하기 위해 강화 레이트를 마스터 데이터로 설정해야 합니다.

마스터 데이터에는 소재로 사용할 수 있는 아이템의 GS2-Inventory 네임스페이스 이름이나 인벤토리 이름,
강화 대상으로 사용할 수 있는 아이템의 GS2-Inventory 네임스페이스 이름이나 인벤토리 이름과 같은 정보를 기록합니다.

마스터 데이터는 JSON 형식으로 관리합니다.

여기서는 ItemModel의 메타데이터에 { "experience": 50 } 와 같은 JSON 형식으로 설정하는 예를 보여드립니다.

RateModel의 acquireExperienceHierarchy에서 JSON의 키(위의 예에서는 `experience`)를 정의합니다.
또한 acquireExperienceHierarchy에는 계층 구조를 정의할 수 있습니다.
예를 들어 { "aaa": { "bbb": { "experienceValue": 100 } } } 와 같은 구조의 데이터를 메타데이터에 설정하고 싶은 경우에는, acquireExperienceHierarchy에 [ "aaa", "bbb", "experienceValue" ] 와 같이 지정합니다.

### RateModel의 주요 설정 항목

| 항목 | 설명 |
| -- | -- |
| `name` | 강화 레이트 이름 |
| `targetInventoryModelId` | 강화 대상 아이템이 저장되어 있는 GS2-Inventory의 인벤토리 모델 ID |
| `materialInventoryModelId` | 강화 소재 아이템이 저장되어 있는 GS2-Inventory의 인벤토리 모델 ID |
| `acquireExperienceHierarchy` | 소재 아이템의 메타데이터에서 경험치량을 꺼내기 위한 JSON 경로 |
| `acquireExperienceSuffix` | 경험치 모델 이름 키에 부여하는 접미사(예: `:level`) |
| `experienceModelId` | 경험치를 가산할 GS2-Experience의 경험치 모델 ID |
| `bonusRates` | 대성공 시의 배율과 추첨 가중치 |

GS2-Enhance RateModel 마스터 데이터 메타데이터에 경험치를 설정하는 예:
```json

{
  "version": "2020-08-22",
  "rateModels": [
    {
      "name": "enhanceRate",
      "description": "",
      "metadata": "",
      "targetInventoryModelId": "grn:gs2:ap-northeast-1:YourOwnerId:inventory:enhance-inventory:model:character",
      "acquireExperienceSuffix": ":level",
      "materialInventoryModelId": "grn:gs2:ap-northeast-1:YourOwnerId:inventory:enhance-inventory:model:material",
      "acquireExperienceHierarchy": [
        "experience"
      ],
      "experienceModelId": "grn:gs2:ap-northeast-1:YourOwnerId:experience:enhance-experience:model:character",
      "bonusRates": [
        {
          "rate": 2,
          "weight": 1
        },
        {
          "rate": 1,
          "weight": 1
        }
      ]
    }
  ]
}
```

`bonusRates`에는 여러 엔트리를 설정할 수 있으며, 각 엔트리는 추첨 가중치(`weight`)와 배율(`rate`)을 가집니다.
위의 예에서는 "2배 경험치"와 "등배 경험치"가 동일한 가중치로 추첨되어, 대략 50%의 확률로 대성공(2배)이 발생합니다.

GS2-Experience ExperienceModel 마스터 데이터 예:
```json
{
  "version": "2019-01-11",
  "experienceModels": [
    {
      "name": "character",
      "metadata": "CHARACTER",
      "defaultExperience": 0,
      "defaultRankCap": 50,
      "maxRankCap": 80,
      "rankThreshold": {
        "metadata": "RANK_THRESHOLD",
        "values": [
          100,
          300,
          500,
          1000
        ]
      }
    }
  ]
}
```

GS2-Inventory ItemModel 마스터 데이터 메타데이터에 경험치를 설정하는 예:
```json
{
  "version": "2019-02-05",
  "inventoryModels": [
    {
      "name": "character",
      "initialCapacity": 1,
      "maxCapacity": 1,
      "protectReferencedItem": false,
      "itemModels": [
        {
          "name": "character-0001",
          "stackingLimit": 1,
          "allowMultipleStacks": false,
          "sortValue": 0
        }
      ]
    },
    {
      "name": "material",
      "metadata": "",
      "initialCapacity": 10,
      "maxCapacity": 10,
      "protectReferencedItem": false,
      "itemModels": [
        {
          "name": "material-0001",
          "metadata": "{\"experience\":50}",
          "stackingLimit": 99,
          "allowMultipleStacks": false,
          "sortValue": 0
        }
      ]
    }
  ]
}
```

## 한계 돌파 (Unleash)

같은 종류의 아이템 등을 소재로 소비해, 대상의 레벨 상한을 끌어올리는 "한계 돌파" 기능도 제공하고 있습니다.
GS2-Experience가 관리하는 `rankCap`(랭크 상한)을 한 단계 끌어올리는 구조로, `UnleashRateModel`의 마스터 데이터로 다음을 설정합니다.

- 한계 돌파 대상 인벤토리 모델
- 연결된 그레이드 모델(GS2-Grade)
- 각 그레이드에 도달하기 위해 필요한 소재 아이템과 그 수량

이를 통해 "같은 캐릭터를 4명 모으면 한계 돌파를 할 수 있다", "특정 소재 아이템을 N개 소비하면 레벨 상한이 해제된다" 같은 게임 메커니즘을 구현할 수 있습니다.

## 스크립트 트리거

네임스페이스에 `enhanceScript`를 설정하면 강화 실행 전후로 커스텀 스크립트를 호출할 수 있습니다. 처리의 허가·거부나 입수 경험치의 오버라이드가 가능하며, 실행 방식은 동기·비동기를 선택할 수 있습니다. 비동기에서는 GS2-Script나 Amazon EventBridge를 이용한 외부 연동에도 대응합니다.

설정할 수 있는 주요 이벤트 트리거와 스크립트 설정 이름은 다음과 같습니다.

- `enhanceScript`(완료 통지: `enhanceDone`): 강화 실행 전후

스크립트 내에서는 강화 대상·소재 아이템·추첨되는 배율을 참조할 수 있어, 게임 밸런스 조정이나 이벤트 기간 중 부스트 배율 변경과 같은 용도로 활용할 수 있습니다.

## 마스터 데이터 운용

마스터 데이터를 등록함으로써 마이크로서비스에서 이용 가능한 데이터나 동작을 설정할 수 있습니다.

마스터 데이터의 종류에는 다음이 있습니다.

- `RateModel`: 강화 소재나 대상의 정의
- `UnleashRateModel`: 상한 해제용 레이트 정의

마스터 데이터의 등록은 관리 콘솔에서 등록하는 방법 외에도, GitHub에서 데이터를 반영하거나 GS2-Deploy를 사용해 CI에서 등록하는 워크플로우를 구성할 수 있습니다.

## 트랜잭션 액션

GS2-Enhance에서는 다음 트랜잭션 액션을 제공하고 있습니다.

- 소비 액션: 실행 중인 강화 진행 정보(`Progress`)의 삭제
- 입수 액션: 즉시 강화(`DirectEnhance`)의 실행, 한계 돌파(`Unleash`)의 실행, 강화(`Progress`)의 시작

"즉시 강화(DirectEnhance)의 실행"을 입수 액션으로 이용함으로써, 특정 퀘스트 클리어 보상이나 상점에서의 아이템 구매 시 보상으로, 직접 캐릭터나 장비에 경험치를 부여하는 처리가 가능해집니다. 또한 "한계 돌파(Unleash)"를 보상으로 설정함으로써, 특정 미션 달성 시 자동으로 레벨 상한을 끌어올리는 운용도 가능합니다.

## 구현 예제

### 강화 레이트 목록 취득



**Unity**
```csharp

    var items = await gs2.Enhance.Namespace(
        namespaceName: "namespace-0001"
    ).RateModelsAsync(
    ).ToListAsync();
```
**Unreal Engine**
```cpp

    const auto It = Gs2->Enhance->Namespace(
        "namespace-0001" // namespaceName
    )->RateModels();
    TArray<Gs2::UE5::Enhance::Model::FEzRateModelPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
```
**Godot**
```gdscript

var iterator = ez.enhance.namespace_(
        "namespace-0001"
    ).rate_models(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 오류를 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


### 강화 실행(DirectEnhance)

`materials`에는 소비할 소재 아이템의 `ItemSetId`와 수량을 지정합니다.
강화 대상으로 `targetItemSetId`에 강화하고 싶은 캐릭터·장비의 `ItemSetId`를 전달합니다.



**Unity**
```csharp

    var result = await gs2.Enhance.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Enhance(
    ).EnhanceAsync(
        rateName: "rate-0001",
        targetItemSetId: "item-set-0001",
        materials: new [] {
            new Gs2.Unity.Gs2Enhance.Model.EzMaterial
            {
                MaterialItemSetId = "material-0001",
                Count = 1,
            },
        }
    );

    var transaction = result;
    await transaction.WaitAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Enhance->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Enhance(
    );
    const auto Future = Domain->Enhance(
        "rate-0001",
        "item-set-0001",
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Enhance::Model::FMaterial>>>();
            v->Add(MakeShared<Gs2::Enhance::Model::FMaterial>()
                ->WithMaterialItemSetId(TOptional<FString>("material-0001"))
                ->WithCount(TOptional<int32>(1)));
            return v;
        }()
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    const auto Transaction = Future->GetTask().Result();
    const auto Future2 = Transaction->Wait();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.enhance.namespace_(
        "namespace-0001"
    ).me(game_session).enhance(
    )

var async_result = await domain.enhance(
    "rate-0001", # rate_name
    "item-set-0001", # target_item_set_id
    [
        Gs2EnhanceEzMaterial.new()
            .with_material_item_set_id("material-0001")
            .with_count(1),
    ], # materials
    null # config
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 강화(Progress)의 시작

추첨 결과를 먼저 취득해 두고 싶은 경우의 흐름입니다.
진행 중인 `Progress`는 1명의 사용자당 1건만 유지할 수 있습니다.



**Unity**
```csharp

    var result = await gs2.Enhance.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Progress(
    ).StartAsync(
        rateName: "rate-0001",
        targetItemSetId: "item-set-0001",
        materials: new [] {
            new Gs2.Unity.Gs2Enhance.Model.EzMaterial
            {
                MaterialItemSetId = "material-0001",
                Count = 1,
            },
        }
    );
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Enhance->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Progress(
    )->Start(
        "rate-0001", // rateName
        "item-set-0001", // targetItemSetId
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Enhance::Model::FMaterial>>>();
            v->Add(MakeShared<Gs2::Enhance::Model::FMaterial>()
                ->WithMaterialItemSetId(TOptional<FString>("material-0001"))
                ->WithCount(TOptional<int32>(1)));
            return v;
        }()
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.enhance.namespace_(
        "namespace-0001"
    ).me(game_session).progress(
    )

var async_result = await domain.start(
    "character-level", # rate_name
    "item-set-0001", # target_item_set_id
    [
        Gs2EnhanceEzMaterial.new()
            .with_material_item_set_id("material-0001")
            .with_count(1),
    ], # materials
    null, # force
    null # config
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 진행 중인 강화 정보 취득

추첨이 완료된 배율(대성공 플래그)이나 입수 경험치량을 취득해, 강화 연출에 활용할 수 있습니다.



**Unity**
```csharp

    var item = await gs2.Enhance.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Progress(
    ).ModelAsync();

    var experienceValue = item.ExperienceValue;
    var rate = item.Rate;
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Enhance->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Progress(
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    const auto Result = Future->GetTask().Result();
    const auto ExperienceValue = Result->GetExperienceValue();
    const auto Rate = Result->GetRate();
```
**Godot**
```gdscript

var domain = ez.enhance.namespace_(
        "namespace-0001"
    ).me(game_session).progress(
    )

var async_result = await domain.model()
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 강화(Progress)의 완료

`Start`로 시작한 강화를 `End`로 확정하여, 소비·경험치 가산을 실행합니다.



**Unity**
```csharp

    var result = await gs2.Enhance.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Progress(
    ).EndAsync();

    await result.WaitAsync();
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Enhance->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Progress(
    )->End();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    const auto Transaction = Future->GetTask().Result();
    const auto Future2 = Transaction->Wait();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.enhance.namespace_(
        "namespace-0001"
    ).me(game_session).progress(
    )

var async_result = await domain.end(
    null # config
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 진행 중인 강화 정보 파기

통신이 끊겨 완료 보고를 할 수 없게 된 경우 등의 복구에 사용합니다.



**Unity**
```csharp

    await gs2.Enhance.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Progress(
    ).DeleteProgressAsync();
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Enhance->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Progress(
    )->DeleteProgress();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.enhance.namespace_(
        "namespace-0001"
    ).me(game_session).progress(
    )

var async_result = await domain.delete_progress(
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


## 기타 기능

### 강화의 대성공

강화 사양으로서, 일정 확률로 "대성공"이 발생해 입수할 수 있는 경험치량이 1.5배나 2배가 되는 사양이 있는 경우가 있습니다.
GS2-Enhance에서는 강화 레이트로서 대성공이 발생할 확률과 그 경우의 경험치 획득량 배율을 설정할 수 있습니다.

`bonusRates`의 `weight` 합계값에 대한 각 엔트리의 `weight` 비율이 그 배율의 발생 확률이 됩니다.

### Config를 사용한 스크립트로의 파라미터 전달

`EnhanceAsync` / `StartAsync`에는 `config` 파라미터를 지정할 수 있어, 스크립트 트리거 실행 시 임의의 키와 값의 쌍을 전달할 수 있습니다.
게임 내 이벤트 부스트 상태나 플레이어가 선택한 강화 모드와 같은 정보를 스크립트에 전달할 수 있습니다.

### 투기적 실행(Speculative Execute)

`speculativeExecute` 인수(기본값 `true`)를 활성화하면, API 요청의 응답을 기다리기 전에 클라이언트 측 캐시를 갱신하여, UI 상에서는 즉시 경험치 가산 후의 상태를 표시할 수 있습니다.
통신 지연의 영향을 덜 받는 쾌적한 조작감을 실현할 수 있습니다.

## 상세 레퍼런스

[GS2-Enhance API 레퍼런스](../../api_reference/enhance)



