# 배치 요청

> Batch your requests to execute UGS service calls in parallel for efficiency.

UGS 서비스를 호출할 때 요청을 배치 처리하여 병렬로 실행할 수 있습니다.

`Task.WhenAll` 메서드를 사용하여 병렬로 UGS 서비스를 호출할 수 있습니다. 이 메서드는 인수 목록의 모든 작업이 완료되면 마무리되는 `Task`를 반환합니다.

> **Note:**
>
> **참고:** 실패한 모든 요청은 로그됩니다. 해당 요청은 Unity Dashboard의 Cloud Code 로그에서 확인할 수 있습니다. 로깅에 관해 자세히 알아보려면 [로깅](../../logging/overview)을 참고하십시오.

> **Note:**
>
> **참고:** 일부 요청이 실패하더라도 성공한 요청은 롤백되지 않습니다.

## 결과 호출 및 반환##call-and-return-results

아래 샘플을 사용하여 서비스를 호출하고 요청의 결과를 반환할 수 있습니다.

이 샘플에서는 Leaderboards 서비스와 Economy 서비스를 호출합니다. 프로젝트에 리더보드나 재화가 구성되어 있지 않으면 호출이 실패합니다. 이렇게 하면 Cloud Code 모듈에서 오류 처리를 테스트할 수 있습니다.

메서드는 요청 결과가 포함된 딕셔너리를 반환하며, 요청을 만든 메서드의 서명을 키로 사용하고 요청의 결과를 값으로 사용합니다. 요청이 실패하면 어떤 요청이 실패했는지 알려 줍니다. 이러한 요청도 물론 로그됩니다.

```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Apis;
using Unity.Services.CloudCode.Core;
using Unity.Services.CloudSave.Model;
using Unity.Services.Economy.Model;

namespace BatchingRequests;

public class BatchingRequests
{
    private const string ExperienceKey = "experience";
    private const string ProgressXpKey = "progressXP";
    private const string FailedMessage = "Failed. Check logs.";

    private readonly ILogger<BatchingRequests> _logger;
    public BatchingRequests(ILogger<BatchingRequests> logger)
    {
        _logger = logger;
    }

    [CloudCodeFunction("HandleMultipleRequests")]
    public async Task<Dictionary<string, object>> HandleMultipleRequests(IGameApiClient gameApiClient, IExecutionContext ctx)
    {
        async Task<object>  GetCloudSaveData()
        {
           var res = await gameApiClient.CloudSaveData.GetItemsAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId, new List<string> { ExperienceKey, ProgressXpKey });
           return res.Data;
        }

        async Task<object>  SetCloudSaveData()
        {
            var res = await gameApiClient.CloudSaveData.SetItemAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId, new SetItemBody(ExperienceKey, 1));
            return res.Data;
        }
        async Task<object>  GetLeaderboardScore()
        {
            const string leaderboardId = "leaderboard";
            var res = await gameApiClient.Leaderboards.GetLeaderboardPlayerScoreAsync(ctx, ctx.AccessToken, Guid.Parse(ctx.ProjectId), leaderboardId, ctx.PlayerId);
            return res.Data;
        }

        async Task<object>  GetEconomyConfig()
        {
            var res = await gameApiClient.EconomyConfiguration.GetPlayerConfigurationAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId);
            return res.Data;
        }

        async Task<object> SetEconomyConfig()
        {
            const string currencyId = "NOT_REAL";
            var res = await gameApiClient.EconomyCurrencies.IncrementPlayerCurrencyBalanceAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId, currencyId, new CurrencyModifyBalanceRequest(currencyId, 1));
            return res.Data;
        }

        // Create a list of UGS tasks that return data
        var tasksWithObjects = new List<Func<Task<object>>>() { GetCloudSaveData, GetLeaderboardScore, SetCloudSaveData, GetEconomyConfig, SetEconomyConfig };
        var results = await AwaitBatch(tasksFns);

        foreach (var item in results)
        {
            if (item is Exception)
            {
                _logger.LogError($"AwaitBatch failed status {item}");
            }
            else if (item is CurrencyBalanceResponse)
            {
                _logger.LogInformation($"AwaitBatch CurrencyBalanceResponse status {item.GetType()} = {item}");
            }
            else
            {
                _logger.LogInformation($"AwaitBatch success status {item.GetType()} = {item}");
            }
        }
        // Alternatively to looping through all results, you may access function results directly
        // The order of function results follows the order of the function input e.g.
        // - GetCloudSaveData corresponds to results[0]: GetItemsResponse || Exception
        // - GetLeaderboardScore corresponds to results[1]: LeaderboardEntryWithUpdatedTime || Exception
        // ... etc
        // This can also help ascertain where the Exception originated from
    }

    private async Task<object[]> AwaitBatch(List<Func<Task<object>>> tasks)
    {
        var tasksToRun = tasks.Select(Run);
        var results = await Task.WhenAll(tasksToRun);
        return results;
    }

    async Task<object> Run(Func<Task<object>> fn)
    {
        try
        {
            return await fn();
        }
        catch (Exception e)
        {
            return e;
        }
    }
}

public class ModuleConfig : ICloudCodeSetup
{
    public void Setup(ICloudCodeConfig config)
    {
        config.Dependencies.AddSingleton(GameApiClient.Create());
    }
}
```
