Share via


TripPin 2부 - REST 서비스에 대한 데이터 커넥터

이 다중 파트 자습서에서는 파워 쿼리에 대한 새 데이터 원본 확장의 생성에 대해 설명합니다. 이 자습서는 순차적으로 수행됩니다. 각 단원은 이전 단원에서 만든 커넥터를 기반으로 하여 커넥터에 새 기능을 증분 방식으로 추가합니다.

이 단원에서는 다음을 수행합니다.

  • Web.Contents를 사용하여 REST API를 호출하는 기본 함수 만들기
  • 요청 헤더를 설정하고 JSON 응답을 처리하는 방법 알아보기
  • Power BI Desktop을 사용하여 응답을 사용자에게 친숙한 형식으로 랭글

이 단원에서는 TripPin 서비스에 대한 OData 기반 커넥터(이전 단원에서 만든)를 RESTful API에 대해 만든 것과 유사한 커넥터로 변환합니다. OData는 RESTful API이지만 규칙 집합이 고정된 API입니다. OData의 장점은 스키마, 데이터 검색 프로토콜 및 표준 쿼리 언어를 제공한다는 것입니다. OData.Feed사용하려면 이러한 기능을 커넥터에 직접 빌드해야 합니다.

OData 커넥터의 요약

커넥터에서 OData 함수를 제거하기 전에 현재 수행하는 작업(대부분 백그라운드에서)을 빠르게 검토하여 서비스에서 데이터를 검색해 보겠습니다.

Visual Studio의 1부에서 TripPin 커넥터 프로젝트를 엽니다. 쿼리 파일을 열고 다음 쿼리에 붙여넣습니다.

TripPin.Feed("https://services.odata.org/v4/TripPinService/Me")

Fiddler를 열고 Visual Studio에서 시작 단추를 선택합니다.

Fiddler에서 서버에 대한 세 가지 요청이 표시됩니다.

Fiddler OData requests.

  • /Me- 요청하는 실제 URL입니다.
  • /$metadata- 함수에서 자동으로 OData.Feed 호출하여 응답에 대한 스키마 및 형식 정보를 확인합니다.
  • /Me/BestFriend- /Me 싱글톤을 나열할 때 끌어온 필드 중 하나입니다. 이 경우 호출로 204 No Content 인해 상태.

M 평가는 대부분 지연됩니다. 대부분의 경우 데이터 값은 필요한 경우에만 검색/끌어오기됩니다. 값이 즉시 끌어오는 시나리오(예: /Me/BestFriend 사례)가 있습니다. 멤버에 형식 정보가 필요할 때 발생하는 경향이 있으며 엔진은 값을 검색하고 검사하는 것 외에 형식을 결정할 다른 방법이 없습니다. 지연을 만드는 것(즉, 즉시 끌어오기 방지)은 M 커넥터를 성능 있게 만드는 주요 측면 중 하나입니다.

/Me 요청 응답의 JSON 형식 및 요청과 함께 전송된 요청 헤더를 확인합니다.

{
  "@odata.context": "https://services.odata.org/v4/TripPinService/$metadata#Me",
  "UserName": "aprilcline",
  "FirstName": "April",
  "LastName": "Cline",
  "MiddleName": null,
  "Gender": "Female",
  "Age": null,
  "Emails": [ "April@example.com", "April@contoso.com" ],
  "FavoriteFeature": "Feature1",
  "Features": [ ],
  "AddressInfo": [
    {
      "Address": "P.O. Box 555",
      "City": {
        "Name": "Lander",
        "CountryRegion": "United States",
        "Region": "WY"
      }
    }
  ],
  "HomeAddress": null
}

쿼리 평가가 완료되면 M 쿼리 출력 창에 Me 싱글톤에 대한 레코드 값이 표시됩니다.

OData results.

출력 창의 필드를 원시 JSON 응답에서 반환된 필드와 비교하면 일치하지 않는 것을 알 수 있습니다. 쿼리 결과에는 JSON 응답의 아무 곳에도 표시되지 않는 추가 필드(Friends, Trips, GetFriendsTrips)가 있습니다. OData.Feed 함수는 $metadata 반환된 스키마에 따라 이러한 필드를 레코드에 자동으로 추가했습니다. 이는 커넥터가 더 나은 사용자 환경을 제공하기 위해 서비스에서 응답을 보강 및/또는 다시 포맷하는 방법의 좋은 예입니다.

기본 REST 커넥터 만들기

이제 Web.Contents를 호출 하는 새 내보낸 함수를 커넥터에 추가합니다.

그러나 OData 서비스에 대한 웹 요청을 성공적으로 수행하려면 몇 가지 표준 OData 헤더를 설정해야 합니다. 공통 헤더 집합을 커넥터에서 새 변수로 정의하여 이 작업을 수행합니다.

DefaultRequestHeaders = [
    #"Accept" = "application/json;odata.metadata=minimal",  // column name and values only
    #"OData-MaxVersion" = "4.0"                             // we only support v4
];

함수를 TripPin.Feed 사용하는 OData.Feed대신 Web.Contents를 사용하여 웹 요청을 수행하고 결과를 JSON 문서로 구문 분석하도록 함수 구현을 변경합니다.

TripPinImpl = (url as text) =>
    let
        source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
        json = Json.Document(source)
    in
        json;

이제 쿼리 파일을 사용하여 Visual Studio에서 이를 테스트할 수 있습니다. 이제 /Me 레코드의 결과는 Fiddler 요청에서 본 원시 JSON과 유사합니다.

새 함수를 실행할 때 Fiddler를 보는 경우 평가에서 이제 3개가 아닌 단일 웹 요청을 만드는 것을 확인할 수 있습니다. 축하합니다. 300% 성능 향상을 달성했습니다! 물론 이제 모든 형식 및 스키마 정보가 손실되었지만 아직 해당 부분에 집중할 필요가 없습니다.

다음과 같은 TripPin 엔터티/테이블 중 일부에 액세스하도록 쿼리를 업데이트합니다.

  • https://services.odata.org/v4/TripPinService/Airlines
  • https://services.odata.org/v4/TripPinService/Airports
  • https://services.odata.org/v4/TripPinService/Me/Trips

잘 서식이 지정된 테이블을 반환하는 데 사용된 경로는 이제 포함된 [목록]이 있는 최상위 수준 "값" 필드를 반환합니다. Power BI 시나리오에 사용할 수 있도록 결과에 대한 몇 가지 변환을 수행해야 합니다.

List results.

파워 쿼리에서 변환 작성

M 변환을 직접 작성할 수 있지만 대부분의 사람들은 파워 쿼리를 사용하여 데이터를 셰이프하는 것을 선호합니다. Power BI Desktop에서 확장을 열고 이를 사용하여 쿼리를 디자인하여 출력을 보다 사용자에게 친숙한 형식으로 전환합니다. 솔루션을 다시 빌드하고, 사용자 지정 데이터 커넥트ors 디렉터리에 새 확장 파일을 복사하고, Power BI Desktop을 다시 시작합니다.

새 빈 쿼리를 시작하고 다음을 수식 입력줄에 붙여넣습니다.

= TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines")

= 기호를 포함해야 합니다.

원본 OData 피드처럼 보일 때까지 출력을 조작합니다( AirlineCode 및 Name이라는 두 개의 열이 있는 테이블).

Formatted airlines.

결과 쿼리는 다음과 같이 표시됩니다.

let
    Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines"),
    value = Source[value],
    toTable = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    expand = Table.ExpandRecordColumn(toTable, "Column1", {"AirlineCode", "Name"}, {"AirlineCode", "Name"})
in
    expand

쿼리 이름을 지정합니다("Airlines").

새 빈 쿼리를 만듭니다. 이번에는 함수를 TripPin.Feed 사용하여 /Airports 엔터티에 액세스합니다. 아래 표시된 공유와 비슷한 항목이 표시될 때까지 변환을 적용합니다. 일치하는 쿼리는 아래에서도 찾을 수 있습니다. 이 쿼리의 이름("공항")도 지정합니다.

Formatted airports.

let
    Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airports"),
    value = Source[value],
    #"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"Name", "IcaoCode", "IataCode", "Location"}, {"Name", "IcaoCode", "IataCode", "Location"}),
    #"Expanded Location" = Table.ExpandRecordColumn(#"Expanded Column1", "Location", {"Address", "Loc", "City"}, {"Address", "Loc", "City"}),
    #"Expanded City" = Table.ExpandRecordColumn(#"Expanded Location", "City", {"Name", "CountryRegion", "Region"}, {"Name.1", "CountryRegion", "Region"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded City",{{"Name.1", "City"}}),
    #"Expanded Loc" = Table.ExpandRecordColumn(#"Renamed Columns", "Loc", {"coordinates"}, {"coordinates"}),
    #"Added Custom" = Table.AddColumn(#"Expanded Loc", "Latitude", each [coordinates]{1}),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "Longitude", each [coordinates]{0}),
    #"Removed Columns" = Table.RemoveColumns(#"Added Custom1",{"coordinates"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Removed Columns",{{"Name", type text}, {"IcaoCode", type text}, {"IataCode", type text}, {"Address", type text}, {"City", type text}, {"CountryRegion", type text}, {"Region", type text}, {"Latitude", type number}, {"Longitude", type number}})
in
    #"Changed Type"

서비스 아래의 추가 경로에 대해 이 프로세스를 반복할 수 있습니다. 준비가 되면 (모의) 탐색 테이블을 만드는 다음 단계로 이동합니다.

탐색 테이블 시뮬레이션

이제 잘 서식이 지정된 TripPin 엔터티를 제공하는 테이블(M 코드 사용)을 빌드하려고 합니다.

새 빈 쿼리를 시작하고 고급 편집기 표시합니다.

다음 쿼리에 붙여넣습니다.

let
    source = #table({"Name", "Data"}, {
        { "Airlines", Airlines },
        { "Airports", Airports }
    })
in
    source

개인 정보 수준 설정을 "항상 개인 정보 수준 설정 무시"("빠른 결합"이라고도 함)로 설정하지 않은 경우 개인 정보 프롬프트가 표시됩니다.

Firewall.

여러 원본의 데이터를 결합하고 원본에 대한 개인 정보 수준을 아직 지정하지 않은 경우 개인 정보 프롬프트가 표시됩니다. 계속 단추를 선택하고 상위 원본의 개인 정보 수준을 공용으로 설정합니다.

Privacy.

저장을 선택하면 테이블이 표시됩니다. 아직 탐색 테이블은 아니지만 후속 단원에서 탐색 테이블을 하나로 전환하는 데 필요한 기본 기능을 제공합니다.

FakeNav.

확장 내에서 여러 데이터 원본에 액세스할 때 데이터 조합 검사 발생하지 않습니다. 확장 내에서 수행된 모든 데이터 원본 호출은 동일한 권한 부여 컨텍스트를 상속하므로 결합하는 것이 "안전"하다고 가정합니다. 확장은 데이터 조합 규칙과 관련하여 항상 단일 데이터 원본으로 처리됩니다. 원본을 다른 M 원본과 결합할 때 사용자는 여전히 정기적인 개인 정보 보호 프롬프트를 받게 됩니다.

Fiddler를 실행하고 쿼리 편집기 새로 고침 미리 보기 단추를 클릭하면 탐색 테이블의 각 항목에 대해 별도의 웹 요청이 표시됩니다. 이는 열성적인 평가가 발생한다는 것을 나타내며, 요소가 많은 탐색 테이블을 빌드할 때는 이상적이지 않습니다. 후속 단원에서는 지연 평가를 지원하는 적절한 탐색 테이블을 빌드하는 방법을 보여 줍니다.

결론

이 단원에서는 REST 서비스에 대한 간단한 커넥터를 빌드하는 방법을 보여 줍니다. 이 경우 기존 OData 확장을 표준 REST 확장(Web.Contents 사용)으로 전환했지만 처음부터 새 확장을 만드는 경우에도 동일한 개념이 적용됩니다.

다음 단원에서는 Power BI Desktop을 사용하여 이 단원에서 만든 쿼리를 확장 내에서 진정한 탐색 테이블로 전환합니다.

다음 단계

TripPin 3부 - 탐색 테이블