> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 타이틀 관리 업적 가져오기

> 플레이어의 타이틀 관리형 XBOX services 업적 목록을 검색하고, 결과를 페이지별로 탐색하며, ID로 단일 업적을 가져오는 샘플 XSAPI 코드입니다.

이 항목에서는 다음을 다룹니다.

* [시작하기 전에](#before-you-begin)
* [업적 목록 가져오기](#getting-the-achievements-list)
* [다음 페이지의 업적 결과 가져오기](#getting-the-next-page-of-achievements-results)
* [단일 업적 가져오기](#getting-a-single-achievement)

## 시작하기 전에

* 제품에서 전체 XBOX services 기능 집합을 사용합니다. 새 제품은 제한된 XBOX services로 시작하며, 제품이 온보딩될 때까지 업적과 게이머스코어를 사용할 수 없습니다. 자세한 내용은 [전체 XBOX services를 사용하도록 제품 온보딩 요청](https://docs.xbox.com/ko/home/onboarding.md#request-product-onboarding-to-enable-full-xbox-services)을 참조하세요.
* Partner Center에서 업적을 구성하고 XBOX services 구성을 개발 샌드박스에 게시했습니다. 자세한 내용은 [Partner Center에서 타이틀 관리 업적 구성](https://docs.xbox.com/ko/services/xbox-services/player-data/achievements/title-managed/config/live-achievements-tm-config.md)을 참조하세요.
* 개발 키트 또는 Windows PC가 같은 개발 샌드박스를 사용하며, 해당 샌드박스에 액세스할 수 있는 테스트 계정으로 로그인합니다. 자세한 내용은 [XBOX services 샌드박스 개요](https://docs.xbox.com/ko/services/xbox-services/fundamentals/sandboxes/live-setup-sandbox.md)를 참조하세요.
* 게임에서 Gaming Runtime을 초기화한 다음 `scid` 멤버에 서비스 구성 ID(SCID)가 포함된 [XblInitArgs](https://docs.xbox.com/ko/reference/live/xsapi-c/xbox_live_global_c/structs/xblinitargs.md) 구조체를 사용하여 [XblInitialize](https://docs.xbox.com/ko/reference/live/xsapi-c/xbox_live_global_c/functions/xblinitialize.md)를 호출했습니다. SCID는 대/소문자를 구분합니다. 자세한 내용은 [XBOX services API 사용 방법](https://docs.xbox.com/ko/services/xbox-services/fundamentals/xbox-services-api/live-gs-xbl-apis.md)을 참조하세요.
* 사용자를 로그인시키고 [XblContextCreateHandle](https://docs.xbox.com/ko/reference/live/xsapi-c/xbox_live_context_c/functions/xblcontextcreatehandle.md)을 호출하여 해당 사용자에 대한 `XblContextHandle`을 만들었습니다.

## 업적 목록 가져오기

실행 중인 타이틀에 대한 업적을 가져오려면 다음과 같이 [XblAchievementsGetAchievementsForTitleIdAsync](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementsfortitleidasync.md)를 호출합니다.

### XblAchievementsGetAchievementsForTitleIdAsync 호출

**C++**

```cpp theme={null}
auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of XAsyncBlock*.

    XblAchievementsResultHandle resultHandle;
    auto hr = XblAchievementsGetAchievementsForTitleIdResult(asyncBlock, &resultHandle);

    if (SUCCEEDED(hr))
    {
        const XblAchievement* achievements = nullptr;
        size_t achievementsCount = 0;
        hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount);
        if (SUCCEEDED(hr))
        {
            for (size_t i = 0; i < achievementsCount; i++)
            {
                LogToScreen("Achievement %s: %s = %s", 
                    achievements[i].id, 
                    achievements[i].name,
                    (achievements[i].progressState == XblAchievementProgressState::Achieved) ? "Achieved" : "Not achieved");
            }
        }

        XblAchievementsResultCloseHandle(resultHandle); // When you're done with the handle, close it.
        achievements = nullptr; // Clear the array after calling XblAchievementsResultCloseHandle to avoid the pointer to freed memory.
        // Instead, you could keep the handle open and store it, for example to get the next page of results.
        // If you need another reference to the handle, call XblAchievementsResultDuplicateHandle().
    }
};

HRESULT hr = XblAchievementsGetAchievementsForTitleIdAsync(
    xboxLiveContext,
    xboxUserId,
    titleId,
    achievementType,
    unlockedOnly,
    orderBy,
    skipItems,
    maxItems,
    asyncBlock.get()
);
if (SUCCEEDED(hr))
{
    // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* because the callback will take over ownership.
    // If the call fails, std::unique_ptr will keep ownership and delete XAsyncBlock*.
    asyncBlock.release(); 
}
```

자세한 내용은 다음을 참조하세요.

* [XAsyncBlock](https://docs.xbox.com/ko/reference/system/xasync/structs/xasyncblock.md)
* [XblAchievement](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/structs/xblachievement.md)
* [XblAchievementProgressState](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/enums/xblachievementprogressstate.md)
* [XblAchievementsGetAchievementsForTitleIdAsync](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementsfortitleidasync.md)
* [XblAchievementsGetAchievementsForTitleIdResult](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementsfortitleidresult.md)
* [XblAchievementsResultCloseHandle](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultclosehandle.md)
* [XblAchievementsResultDuplicateHandle](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultduplicatehandle.md)
* [XblAchievementsResultGetAchievements](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetachievements.md)

[이 항목의 맨 위로 돌아가기.](#top)

## 다음 페이지의 업적 결과 가져오기

업적 페이지가 더 있을 수 있습니다. 확인하려면 [XblAchievementsResultHasNext](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresulthasnext.md)를 호출합니다. 다음 페이지를 가져오려면 현재 결과 핸들을 사용하여 [XblAchievementsResultGetNextAsync](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetnextasync.md)를 호출한 다음, 콜백에서 [XblAchievementsResultGetNextResult](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetnextresult.md)를 호출합니다.

`XblAchievementsResultGetNextResult`는 새 결과 핸들을 반환합니다. 각 결과 핸들은 별도로 닫아야 합니다. `maxItems`에 0을 전달하면 모든 항목을 검색하려고 시도하지만, 서비스는 기본 페이지 크기를 적용할 수 있으며 페이지가 더 남아 있더라도 요청한 것보다 적은 항목을 반환할 수 있습니다. 한 번의 호출로 모든 업적이 반환된다고 가정하지 말고 항상 `XblAchievementsResultHasNext`를 확인하세요.

### 모든 결과 페이지 처리

다음 예제에서는 `XblAchievementsGetAchievementsForTitleIdResult`의 결과 핸들을 닫는 대신 `ProcessAchievementsPage`에 전달합니다. 이 함수는 전달된 핸들의 소유권을 가져와 해당 핸들을 정확히 한 번 닫습니다. 현재 핸들은 다음 페이지에 대한 요청이 완료될 때까지 열린 상태로 유지됩니다.

**C++**

```cpp theme={null}
void ProcessAchievementsPage(XblAchievementsResultHandle resultHandle, XTaskQueueHandle queue, uint32_t maxItems)
{
    const XblAchievement* achievements = nullptr;
    size_t achievementsCount = 0;
    HRESULT hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount);
    if (SUCCEEDED(hr))
    {
        for (size_t i = 0; i < achievementsCount; i++)
        {
            LogToScreen("Achievement %s: %s = %s",
                achievements[i].id,
                achievements[i].name,
                (achievements[i].progressState == XblAchievementProgressState::Achieved) ? "Achieved" : "Not achieved");
        }
    }

    bool hasNext = false;
    if (SUCCEEDED(hr))
    {
        hr = XblAchievementsResultHasNext(resultHandle, &hasNext);
    }

    if (FAILED(hr) || !hasNext)
    {
        // This is the last page, or an error occurred. Close the handle.
        XblAchievementsResultCloseHandle(resultHandle);
        return;
    }

    struct NextPageContext
    {
        XblAchievementsResultHandle currentHandle;
        uint32_t maxItems;
    };

    auto asyncBlock = std::make_unique<XAsyncBlock>();
    asyncBlock->queue = queue;
    asyncBlock->context = new NextPageContext{ resultHandle, maxItems };
    asyncBlock->callback = [](XAsyncBlock* asyncBlock)
    {
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of XAsyncBlock*.
        std::unique_ptr<NextPageContext> context{ static_cast<NextPageContext*>(asyncBlock->context) };

        XblAchievementsResultHandle nextHandle = nullptr;
        HRESULT hr = XblAchievementsResultGetNextResult(asyncBlock, &nextHandle);

        // The request for the next page is complete, so close the handle for the previous page.
        XblAchievementsResultCloseHandle(context->currentHandle);

        if (SUCCEEDED(hr))
        {
            // Process the new page. ProcessAchievementsPage closes nextHandle.
            ProcessAchievementsPage(nextHandle, asyncBlock->queue, context->maxItems);
        }
        else
        {
            LogToScreen("XblAchievementsResultGetNextResult failed: 0x%08X", static_cast<unsigned int>(hr));
        }
    };

    hr = XblAchievementsResultGetNextAsync(resultHandle, maxItems, asyncBlock.get());
    if (SUCCEEDED(hr))
    {
        // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* because the callback will take over ownership.
        asyncBlock.release();
    }
    else
    {
        // The callback won't run, so clean up here.
        delete static_cast<NextPageContext*>(asyncBlock->context);
        XblAchievementsResultCloseHandle(resultHandle);
    }
}
```

자세한 내용은 다음을 참조하세요.

* [XAsyncBlock](https://docs.xbox.com/ko/reference/system/xasync/structs/xasyncblock.md)
* [XblAchievement](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/structs/xblachievement.md)
* [XblAchievementProgressState](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/enums/xblachievementprogressstate.md)
* [XblAchievementsResultCloseHandle](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultclosehandle.md)
* [XblAchievementsResultGetAchievements](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetachievements.md)
* [XblAchievementsResultGetNextAsync](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetnextasync.md)
* [XblAchievementsResultGetNextResult](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetnextresult.md)
* [XblAchievementsResultHasNext](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresulthasnext.md)

[이 항목의 맨 위로 돌아가기.](#top)

## 단일 업적 가져오기

단일 업적을 가져오려면 다음과 같이 [XblAchievementsGetAchievementAsync](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementasync.md)를 호출합니다.

**Flat C API**

```cpp theme={null}
auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of XAsyncBlock*.
    XblAchievementsResultHandle resultHandle;
    auto hr = XblAchievementsGetAchievementResult(asyncBlock, &resultHandle);

    if (SUCCEEDED(hr))
    {
        const XblAchievement* achievements = nullptr;
        size_t achievementsCount = 0;
        hr = XblAchievementsResultGetAchievements( resultHandle, &achievements, &achievementsCount );

        // Use the achievements array to read the achievement data.

        XblAchievementsResultCloseHandle(resultHandle); // When you're done with the handle, close it.
        achievements = nullptr; // Clear the array after calling XblAchievementsResultCloseHandle to avoid the pointer to freed memory.
    }
};

HRESULT hr = XblAchievementsGetAchievementAsync(
    xboxLiveContext,
    xboxUserId,
    scid,
    achievementId.c_str(),
    asyncBlock.get()
);
if (SUCCEEDED(hr))
{
    // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* because the callback will take over ownership.
    // If the call fails, std::unique_ptr will keep ownership and delete XAsyncBlock*.
    asyncBlock.release();
}
```

자세한 내용은 다음을 참조하세요.

* [XAsyncBlock](https://docs.xbox.com/ko/reference/system/xasync/structs/xasyncblock.md)
* [XblAchievement](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/structs/xblachievement.md)
* [XblAchievementsGetAchievementAsync](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementasync.md)
* [XblAchievementsGetAchievementResult](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementresult.md)
* [XblAchievementsResultCloseHandle](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultclosehandle.md)
* [XblAchievementsResultGetAchievements](https://docs.xbox.com/ko/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetachievements.md)

[이 항목의 맨 위로 돌아가기.](#top)


## Related topics

- [타이틀 관리 업적 예제 코드(목차)](https://docs.xbox.com/ko/services/xbox-services/player-data/achievements/title-managed/how-to/live-achievements-howto-nav.md)
- [타이틀 관리 업적 예제](https://docs.xbox.com/ko/services/xbox-services/player-data/achievements/title-managed/how-to/index.md)
- [통계와 업적](https://docs.xbox.com/ko/build/steam-porting-guide/features/stats-and-achievements.md)
- [이벤트 기반 업적 가져오기](https://docs.xbox.com/ko/services/xbox-services/player-data/achievements/event-based/how-to/live-getting-event-based-achievements.md)
- [타이틀 관리 리더보드 가져오기](https://docs.xbox.com/ko/services/xbox-services/player-data/stats-leaderboards/title-managed/how-to/live-getting-tm-leaderboard.md)
