다음을 통해 공유


빠른 시작: 보호 SDK용 클라이언트 애플리케이션 초기화(C++)

이 빠른 시작에서는 런타임 시 MIP C++ SDK에서 사용하는 클라이언트 초기화 패턴을 구현하는 방법을 보여 줍니다.

참고 항목

이 빠른 시작에 설명된 단계는 MIP 보호 SDK를 사용하는 모든 클라이언트 애플리케이션에 필요합니다. 이러한 빠른 시작은 애플리케이션 초기화와 인증 대리자 및 동의 대리자 클래스의 구현 후에 순차적으로 수행해야 합니다.

필수 조건

아직 없는 경우 다음을 확인해야 합니다.

  • MIP(Microsoft Information Protection) SDK 설정 및 구성의 단계를 완료합니다. 이 “클라이언트 애플리케이션 초기화” 빠른 시작은 적절한 SDK 설정 및 구성에 의존합니다.
  • 필요:
    • 프로필 및 엔진 개체를 검토합니다. 프로필 및 엔진 개체는 MIP 파일/정책/보호 SDK를 사용하는 클라이언트에서 요구하는 범용 개념입니다.
    • 인증 개념을 검토하여 SDK 및 클라이언트 애플리케이션에서 인증 및 동의를 구현하는 방법을 알아봅니다.
    • 관찰자 개념을 검토하여 관찰자 및 해당 구현 방법에 대해 자세히 알아봅니다. MIP SDK는 관찰자 패턴을 사용하여 비동기 이벤트 알림을 구현합니다.

Visual Studio 솔루션 및 프로젝트 만들기

먼저, 다른 빠른 시작이 빌드되는 Visual Studio 초기 솔루션 및 프로젝트를 만들고 구성합니다.

  1. Visual Studio 2017을 열고 파일 메뉴, 새로 만들기, 프로젝트를 선택합니다. 새 프로젝트 대화 상자에서 다음을 수행합니다.

    • 왼쪽 창의 설치됨, 기타 언어에서 Visual C++를 선택합니다.

    • 가운데 창에서 Windows 콘솔 애플리케이션을 선택합니다.

    • 아래쪽 창에서 프로젝트 이름, 위치, 포함된 솔루션 이름을 적절하게 업데이트합니다.

    • 완료되면 오른쪽 아래에서 확인 단추를 클릭합니다.

      Visual Studio solution creation

  2. 프로젝트에 MIP 보호 SDK에 대한 Nuget 패키지를 추가합니다.

    • 솔루션 탐색기에서 프로젝트 노드(상단/솔루션 노드 바로 아래)를 마우스 오른쪽 단추로 클릭하고 NuGet 패키지 관리...를 선택합니다.

    • 편집기 그룹 탭 영역에서 NuGet 패키지 관리자 탭이 열리면 다음을 수행합니다.

      • 찾아보기를 선택합니다.
      • 검색 상자에 “Microsoft.InformationProtection”을 입력합니다.
      • "Microsoft.InformationProtection.Protection" 패키지를 선택합니다.
      • “설치”를 클릭한 다음, 미리 보기 변경 확인 대화 상자가 표시되어 있는 경우 “확인”을 클릭합니다.

      Visual Studio add NuGet package

보호 프로필 및 엔진 개체를 모니터링하는 관찰자 클래스 구현

이제 SDK의 mip::ProtectionProfile::Observer 클래스를 확장하여 보호 프로필 관찰자 클래스에 대한 기본 구현을 만들 수 있습니다. 관찰자는 나중에 인스턴스화되어 보호 프로필 개체의 로드를 모니터링하고, 엔진 개체를 프로필에 추가하기 위해 사용됩니다.

  1. 프로젝트에 새 클래스를 추가하여 header/.h 및 implementation/.cpp 파일을 모두 생성합니다.

    • 솔루션 탐색기에서 프로젝트 노드를 다시 마우스 오른쪽 단추로 클릭하고 추가를 선택한 다음, 클래스를 선택합니다.

    • 클래스 추가 대화 상자에서 다음을 수행합니다.

      • 클래스 이름 필드에 “profile_observer”를 입력합니다. .h 파일.cpp 파일 필드는 모두 입력한 이름에 따라 자동으로 채워집니다.
      • 완료되면 확인 단추를 클릭합니다.

      Visual Studio add class

  2. 클래스에 대해 .h 및 .cpp 파일을 생성한 후 두 파일이 편집기 그룹 탭에서 열립니다. 이제 각 파일을 업데이트하여 새 관찰자 클래스를 구현합니다.

    • 생성된 profile_observer 클래스를 선택/삭제하여 “profile_observer.h”를 업데이트합니다. 이전 단계에서 생성된 전처리기 지시문(#pragma, #include)을 제거하지 않습니다. 그런 다음, 기존 전처리기 지시문 뒤에 다음 원본을 파일에 복사/붙여넣습니다.

      #include <memory>
      #include "mip/protection/protection_profile.h"
      using std::exception_ptr;
      using std::shared_ptr;
      
      
      class ProtectionProfileObserver final : public mip::ProtectionProfile::Observer {
      public:
           ProtectionProfileObserver() { }
           void OnLoadSuccess(const std::shared_ptr<mip::ProtectionProfile>& profile, const std::shared_ptr<void>& context) override;
           void OnLoadFailure(const std::exception_ptr& Failure, const std::shared_ptr<void>& context) override;
           void OnAddEngineSuccess(const std::shared_ptr<mip::ProtectionEngine>& engine, const std::shared_ptr<void>& context) override;
           void OnAddEngineFailure(const std::exception_ptr& Failure, const std::shared_ptr<void>& context) override;
      };
      
    • 생성된 profile_observer 클래스 구현을 선택/삭제하여 “profile_observer.cpp”를 업데이트합니다. 이전 단계에서 생성된 전처리기 지시문(#pragma, #include)을 제거하지 않습니다. 그런 다음, 기존 전처리기 지시문 뒤에 다음 원본을 파일에 복사/붙여넣습니다.

      #include <future>
      
      using std::promise;
      using std::shared_ptr;
      using std::static_pointer_cast;
      using mip::ProtectionEngine;
      using mip::ProtectionProfile;
      
      void ProtectionProfileObserver::OnLoadSuccess(const shared_ptr<ProtectionProfile>& profile, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<ProtectionProfile>>>(context);
           promise->set_value(profile);
      }
      
      void ProtectionProfileObserver::OnLoadFailure(const std::exception_ptr& error, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<ProtectionProfile>>>(context);
           promise->set_exception(error);
      }
      
      void ProtectionProfileObserver::OnAddEngineSuccess(const shared_ptr<ProtectionEngine>& engine, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<ProtectionEngine>>>(context);
           promise->set_value(engine);
      }
      
      void ProtectionProfileObserver::OnAddEngineFailure(const std::exception_ptr& error, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<ProtectionEngine>>>(context);
           promise->set_exception(error);
      }
      
  3. 1의 다음 단계. 프로젝트에 보호 엔지 관찰자 - "engine_observer"에 대한 새 클래스를 추가하여 header/.h 및 implementation/.cpp 파일을 모두 생성합니다.

  4. 클래스에 대해 .h 및 .cpp 파일을 생성한 후 두 파일이 편집기 그룹 탭에서 열립니다. 이제 각 파일을 업데이트하여 새 관찰자 클래스를 구현합니다.

    • 생성된 engine_observer 클래스를 선택/삭제하여 “engine_observer.h”를 업데이트합니다. 이전 단계에서 생성된 전처리기 지시문(#pragma, #include)을 제거하지 않습니다. 그런 다음, 기존 전처리기 지시문 뒤에 다음 원본을 파일에 복사/붙여넣습니다.

      #include <memory>
      #include "mip/protection/protection_engine.h"
      using std::vector;
      using std::exception_ptr;
      using std::shared_ptr;
      
      class ProtectionEngineObserver final : public mip::ProtectionEngine::Observer {
        public:
        ProtectionEngineObserver() {}
        void OnGetTemplatesSuccess(const vector<std::shared_ptr<mip::TemplateDescriptor>>& templateDescriptors, const shared_ptr<void>& context) override;
        void OnGetTemplatesFailure(const exception_ptr& Failure, const shared_ptr<void>& context) override;
      
      };
      
    • 생성된 engine_observer 클래스 구현을 선택/삭제하여 “engine_observer.cpp”를 업데이트합니다. 이전 단계에서 생성된 전처리기 지시문(#pragma, #include)을 제거하지 않습니다. 그런 다음, 기존 전처리기 지시문 뒤에 다음 원본을 파일에 복사/붙여넣습니다.

      #include "mip/protection/protection_profile.h"
      #include "engine_observer.h"
      
      using std::promise;
      void ProtectionEngineObserver::OnGetTemplatesSuccess(const vector<shared_ptr<mip::TemplateDescriptor>>& templateDescriptors,const shared_ptr<void>& context) {
          auto loadPromise = static_cast<promise<vector<shared_ptr<mip::TemplateDescriptor>>>*>(context.get());
          loadPromise->set_value(templateDescriptors);
        };
      
        void ProtectionEngineObserver::OnGetTemplatesFailure(const exception_ptr& Failure, const shared_ptr<void>& context) {
          auto loadPromise = static_cast<promise<shared_ptr<mip::ProtectionProfile>>*>(context.get());
          loadPromise->set_exception(Failure);
        };
      
  5. 선택적으로 Ctrl+Shift+B(빌드 솔루션)를 사용하여 솔루션의 테스트 컴파일/링크를 실행하여 계속하기 전에 성공적으로 빌드할 수 있도록 합니다.

MIP SDK는 클래스 확장성을 사용하여 인증을 구현합니다. 이는 클라이언트 애플리케이션과 인증 작업을 공유하는 메커니즘을 제공합니다. 클라이언트는 적절한 OAuth2 액세스 토큰을 획득하고 런타임 시 이를 MIP SDK에 제공해야 합니다.

SDK의 mip::AuthDelegate 클래스를 확장하고 mip::AuthDelegate::AcquireOAuth2Token() 순수 가상 함수를 재정의/구현하여 인증 대리자에 대한 구현을 만들 수 있습니다. 파일 SDK 애플리케이션 초기화 빠른 시작에서 자세히 설명한 단계를 따릅니다. 인증 대리자는 보호 프로필 및 보호 엔진 개체에 의해 인스턴스화되고 나중에 사용됩니다.

이제 SDK의 mip::ConsentDelegate 클래스를 확장하고 mip::AuthDelegate::GetUserConsent() 순수 가상 함수를 재정의/구현하여 동의 대리자에 대한 구현을 만들 수 있습니다. 파일 SDK 애플리케이션 초기화 빠른 시작에서 자세히 설명한 단계를 따릅니다. 동의 대리자는 보호 프로필 및 보호 엔진 개체에 의해 인스턴스화되고 나중에 사용됩니다.

보호 프로필 및 엔진 구성

위에서 언급한 것처럼 MIP API를 사용하는 SDK 클라이언트에 프로필 및 엔진 개체가 필요합니다. 프로필 및 엔진 개체를 인스턴스화하는 코드를 추가하여 이 빠른 시작의 코딩 부분을 완료합니다.

  1. 솔루션 탐색기에서 main() 메서드 구현을 포함하는 프로젝트의 .cpp 파일을 엽니다. 기본값은 프로젝트 생성 중에 지정한 이름이 포함된 프로젝트와 동일한 이름입니다.

  2. main()의 생성된 구현을 제거합니다. 프로젝트 생성 중 Visual Studio에서 생성된 전처리기 지시문(#pragma, #include)을 제거하지 않습니다. 전처리기 지시문 뒤에 다음 코드를 추가합니다.

#include "mip/mip_init.h"
#include "mip/mip_context.h"  
#include "auth_delegate.h"
#include "consent_delegate.h"
#include "profile_observer.h"
#include"engine_observer.h"

using std::promise;
using std::future;
using std::make_shared;
using std::shared_ptr;
using std::string;
using std::cout;
using mip::ApplicationInfo;
using mip::ProtectionProfile;
using mip::ProtectionEngine;

int main(){

  // Construct/initialize objects required by the application's profile object
  // ApplicationInfo object (App ID, name, version)
  ApplicationInfo appInfo{"<application-id>",                    
                          "<application-name>",
                          "<application-version>"};

  std::shared_ptr<mip::MipConfiguration> mipConfiguration = std::make_shared<mip::MipConfiguration>(mAppInfo,
				                                                                                               "mip_data",
                                                                                      			         mip::LogLevel::Trace,
                                                                                                     false);

  std::shared_ptr<mip::MipContext> mMipContext = mip::MipContext::Create(mipConfiguration);

  auto profileObserver = make_shared<ProtectionProfileObserver>(); // Observer object
  auto authDelegateImpl = make_shared<AuthDelegateImpl>("<application-id>"); // Authentication delegate object (App ID)
  auto consentDelegateImpl = make_shared<ConsentDelegateImpl>(); // Consent delegate object

  // Construct/initialize profile object
  ProtectionProfile::Settings profileSettings(
    mMipContext,
    mip::CacheStorageType::OnDisk,      
    consentDelegateImpl,
    profileObserver);

  // Set up promise/future connection for async profile operations; load profile asynchronously
  auto profilePromise = make_shared<promise<shared_ptr<ProtectionProfile>>>();
  auto profileFuture = profilePromise->get_future();
  try
  {
    mip::ProtectionProfile::LoadAsync(profileSettings, profilePromise);
  }
  catch (const std::exception& e)
  {
    cout << "An exception occurred... are the Settings and ApplicationInfo objects populated correctly?\n\n"
          << e.what() << "'\n";
    system("pause");
    return 1;
  }

  auto profile = profileFuture.get();

  // Construct/initialize engine object
  ProtectionEngine::Settings engineSettings(       
     mip::Identity("<engine-account>"),         // Engine identity (account used for authentication)
     authDelegateImpl,                          // Reference to mip::AuthDelegate implementation
     "",                                        // ClientData field
     "en-US");                                  // Locale (default = en-US)

  // Set the engineId so it can be cached and reused. 
  engineSettings.SetEngineId("<engine-account>");

  // Set up promise/future connection for async engine operations; add engine to profile asynchronously
  auto enginePromise = make_shared<promise<shared_ptr<ProtectionEngine>>>();
  auto engineFuture = enginePromise->get_future();
  profile->AddEngineAsync(engineSettings, enginePromise);
  std::shared_ptr<ProtectionEngine> engine;

  try
  {
    engine = engineFuture.get();
  }
  catch (const std::exception& e)
  {
    cout << "An exception occurred... is the access token incorrect/expired?\n\n"
         << e.what() << "'\n";
    system("pause");
    return 1;
  }

  // Application shutdown. Null out profile and engine, call ReleaseAllResources();
  // Application may crash at shutdown if resources aren't properly released.
  engine = nullptr;
  profile = nullptr;
  mipContext.Shutdown();
  mipContext = nullptr;

  return 0;
}
  1. 문자열 상수를 사용하여 방금 붙여넣은 소스 코드의 모든 자리 표시자 값을 바꿉니다.

    자리 표시자 예시
    <application-id> "MIP SDK 설치 및 구성"(setup-configure-mip.md) 문서의 2단계에서 등록된 애플리케이션에 할당된 Microsoft Entra 애플리케이션 ID(GUID)입니다. 2개의 인스턴스를 바꿉니다. "0edbblll-8773-44de-b87c-b8c6276d41eb"
    <application-name> 애플리케이션에 대한 사용자 정의 식별 이름입니다. 유효한 ASCII 문자(';' 제외)를 포함해야 하며 Microsoft Entra 등록에서 사용한 응용 프로그램 이름과 이상적으로 일치해야 합니다. "AppInitialization"
    <application-version> 애플리케이션에 대한 사용자 정의 버전 정보입니다. 유효한 ASCII 문자(‘;’ 제외)를 포함해야 합니다. "1.1.0.0"
    <engine-account> 엔진의 ID에 사용되는 계정입니다. 토큰을 획득하는 동안 사용자 계정으로 인증하는 경우 이 값과 일치해야 합니다. "user1@tenant.onmicrosoft.com"
    <engine-state> 엔진과 연결될 사용자 정의 상태입니다. "My App State"
  2. 이제 애플리케이션의 최종 빌드를 실행하고 오류를 해결합니다. 코드는 성공적으로 빌드해야 하지만 다음 빠른 시작을 완료할 때까지 아직 제대로 실행되지 않습니다. 애플리케이션을 실행하면 다음과 유사한 출력이 표시됩니다. 애플리케이션은 보호 프로필 및 보호 엔진을 성공적으로 구성하지만 인증 모듈이 실행되지 않았고 다음 빠른 시작을 완료할 때까지 아직 액세스 토큰이 없습니다.

     C:\MIP Sample Apps\ProtectionQS\Debug\ProtectionQS.exe (process 8252) exited with code 0.
     To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
     Press any key to close this window . . .
    

다음 단계

이제 초기화 코드가 완료되었으므로 다음 빠른 시작에 대한 준비가 완료되어 MIP 보호 SDK를 경험하기 시작할 수 있습니다.