Looker에서 맞춤 시각화 시작하기

Looker 시각화 API는 샌드박스 처리된 iframe 에서 실행되고 Looker 애플리케이션 내에서 호스팅되는 JavaScript API입니다.

동일한 시각화 코드를 사용하여 Looker의 모든 위치(Explore, Look, 대시보드, 삽입, PDF 또는 렌더링된 이미지)에 시각화를 제공할 수 있습니다.

각 시각화는 단일 Looker 쿼리의 뷰를 나타냅니다. Looker는 쿼리를 실행하고 결과를 시각화 코드에 전달합니다. 또한 Looker는 시각화 코드를 그릴 수 있는 DOM 요소를 전달합니다.

요구사항

시작하기 전에 다음 요구사항을 충족하는지 확인하세요.

Hello World 시각화 만들기

이 섹션에서는 지정된 쿼리의 첫 번째 측정기준을 표시하는 커스텀 시각화 스크립트를 만드는 방법을 설명합니다.

최종 결과는 다음 이미지와 비슷해야 합니다.

설정

Looker에서 시각화를 개발하고 테스트하는 방법에는 두 가지가 있습니다.

이러한 옵션 중 하나를 구성한 후 이 섹션의 예에 따라 hello_world.js를 만들 수 있습니다.

필수 코드 구조

이 예의 최종 소스 코드를 보려면 GitHub 저장소를 참조하세요.

호스팅하는 폴더에서 새 빈 JavaScript 파일을 만들고 이름을 hello_world.js로 지정합니다.

Looker에 새 커스텀 시각화를 등록하려면 looker.plugins.visualizations.add 함수를 호출하고 시각화 객체를 전달합니다. 이 객체에는 시각화 및 구성 UI의 정의가 포함되어 있습니다.

다음 예는 모든 필수 속성이 채워진 시각화의 스켈레톤을 보여줍니다. 시각화 코드를 작성할 수 있는 create 함수와 updateAsync 함수가 포함되어 있습니다.

looker.plugins.visualizations.add({
  create: function(element, config) {

  },
  updateAsync: function(data, element, config, queryResponse, details, done) {

  }
})

이 코드는 유효한 Looker 시각화이지만 아직 시각화되지 않았으며 빈 상자로 표시됩니다.

데이터 처리를 위한

element와 config라는 두 개의 인수가 있는 create 함수를 고려해 보세요.

Looker는 Looker에서 시각화를 배치해야 하는 element DOM 요소 를 제공합니다. Looker는 이 요소를 빌드하고 적절한 크기로 만듭니다. 콘텐츠를 내부에 배치해야 합니다.

create 함수는 요소의 초기 설정을 실행할 수 있는 기회를 제공합니다. 이 예에서는 다음 HTML 구조와 유사한 차트를 만들려고 합니다.

<style>
  /* custom styles for your chart */
</style>
<div class="hello-world-vis">
  <div>Data Goes Here</div>
</div>

JavaScript를 사용하여 이 구조와 일치하도록 DOM 요소에 요소를 삽입할 수 있습니다.

다음 예는 세부정보가 추가된 이 함수를 보여줍니다.

create: function(element, config) {

  // Insert a <style> tag with some styles to use later.
  var css = element.innerHTML = `
    <style>
      .hello-world-vis {
        /* Vertical centering */
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: center;
        text-align: center;
      }
    </style>
  `;

  // Create a container element to center the text.
  var container = element.appendChild(document.createElement("div"));
  container.className = "hello-world-vis";

  // Create an element to contain the text.
  this._textElement = container.appendChild(document.createElement("div"));

},

이 코드는 CSS를 추가하고 hello-world-vis 컨테이너를 만들고 그 안에 div를 추가합니다.

또한 코드는 렌더링하려는 컨테이너에 this._textElement를 할당합니다. 이렇게 하면 차트가 렌더링되기 시작할 때 나중에 다시 조회할 필요가 없습니다.

create 함수는 한 번만 실행해야 하는 설정 작업을 실행하기에 편리한 위치입니다.

렌더링

이 섹션에서는 데이터를 시각화하도록 updateAsync 메서드를 확장하는 방법을 설명합니다. 이 메서드는 차트가 변경사항을 시각화해야 할 때 또는 차트가 렌더링되는 방식에 영향을 줄 수 있는 다른 이벤트 (예: 차트 크기 조정 또는 구성 옵션 변경)가 발생할 때마다 호출됩니다.

이 예에서는 첫 번째 셀의 첫 번째 측정기준만 찾아야 합니다. 이렇게 하려면 데이터 세트의 모든 행으로 구성된 배열인 data와 필드 이름 및 유형과 같은 쿼리에 관한 메타데이터가 포함된 queryResponse를 사용하면 됩니다.

LookerCharts.Utils.htmlForCell이라는 도우미 메서드를 사용하여 해당 셀의 데이터 포인트에 적절한 HTML 표현을 제공할 수도 있습니다. 이 메서드는 드릴 링크, 형식 지정, 데이터 작업과 같은 요소를 자동으로 처리합니다.

  updateAsync: function(data, element, config, queryResponse, details, done) {

    // Grab the first cell of the data.
    var firstRow = data[0];
    var firstCell = firstRow[queryResponse.fields.dimensions[0].name];

    // Insert the data into the page.
    this._textElement.innerHTML = LookerCharts.Utils.htmlForCell(firstCell);

    // Always call done to indicate a visualization has finished rendering.
    done()
  }

이렇게 하면 작동하는 커스텀 시각화에 필요한 단계가 완료됩니다. 하지만 오류를 처리하는 메서드를 포함하는 것이 중요합니다.

오류 처리

updateAsync 코드는 데이터의 첫 번째 행을 가져오지만 쿼리가 결과를 반환하지 않거나 측정기준을 포함하지 않는 경우 오류가 발생할 수 있습니다.

오류 처리가 없으면 브라우저 콘솔에 JavaScript 오류가 표시되어 차트가 렌더링되지 않을 때 사용자가 혼동할 수 있습니다.

차트는 렌더링을 시도하는 중에 문제가 발생하면 커스텀 오류 메시지를 표시할 수 있습니다. Looker는 시각화 객체에 addError 및 clearErrors라는 두 가지 함수를 추가합니다. 이러한 함수를 사용하여 모든 사용자에게 의미가 있는 오류 메시지를 표시할 수 있습니다.

이러한 함수는 시각화 객체에 추가되므로 this 객체를 통해 updateAsync 함수의 컨텍스트에서 사용할 수 있습니다.

렌더링을 시도하는 대신 오류 조건을 감지하고 사용자에게 문제를 알리고 종료하도록 updateAsync 메서드의 시작 부분을 수정할 수 있습니다.

  updateAsync: function(data, element, config, queryResponse, details, done) {

    // Clear any errors from previous updates.
    this.clearErrors();

    // Throw some errors and exit if the shape of the data isn't what this chart needs.
    if (queryResponse.fields.dimensions.length == 0) {
      this.addError({title: "No Dimensions", message: "This chart requires dimensions."});
      // Always call done() before exiting early to avoid dashboard exports hanging
      done();
      return;
    }

    if (!data || data.length === 0) {
      this.addError({title: "No Data", message: "The query returned no results."});
      done();
      return;
    }

    // ... the rest of the update code here ...

사용자가 측정항목만 포함하는 쿼리를 만들면 다음과 유사한 오류 메시지가 표시됩니다.

비동기 렌더링 및 PDF 내보내기

앞의 'Hello World' 예에서는 DOM이 업데이트된 직후에 done() 함수가 호출됩니다. innerHTML 설정은 동기식이므로 이 접근 방식이 적절합니다.

하지만 D3.js, Chart.js 또는 React와 같은 차트 라이브러리를 사용하는 경우 렌더링이 비동기식일 가능성이 높습니다. 스크립트 하단에서 done()을 호출하면 Looker가 차트 그리기를 완료하기 전에 PDF 또는 PNG 내보내기를 위한 스크린샷을 캡처하여 빈 타일이 발생할 수 있습니다.

Looker는 대시보드를 내보낼 때 헤드리스 브라우저를 사용하고 done() 콜백을 사용하여 스크린샷을 찍어도 되는 시점을 파악합니다.

차트가 올바르게 내보내지도록 하려면 done() 콜백을 라이브러리의 완료 이벤트에 연결하고 details.print 플래그를 사용하여 내보내기 중에 애니메이션을 사용 중지해야 합니다.

다음 예는 D3.js를 사용하여 비동기 렌더링을 처리하는 방법을 보여줍니다.

  updateAsync: function(data, element, config, queryResponse, details, done) {
    this.clearErrors();

    // Check if Looker is exporting the chart to a PDF/PNG
    const isExport = details && details.print === true;

    // Clear previous elements
    this.svg.selectAll("*").remove();

    const circle = this.svg.append("circle")
      .attr("cx", 50)
      .attr("cy", 50);

    if (isExport) {
      // Instant render for exports. Draw the final state immediately with no transitions
      circle.attr("r", 40);

      // Tell Looker to take the screenshot right now
      done();
    } else {
      // If it's for the browser, start small and animate
      circle.attr("r", 0)
        .transition()
        .duration(1000)
        .attr("r", 40)
        .on("end", () => {
           // Tell Looker the animation is fully complete
           done();
        });
    }
  }

React를 사용하는 경우 Looker가 React가 구성요소를 마운트할 때까지 기다리도록 렌더링 함수의 세 번째 인수로 done 콜백을 전달해야 합니다.

  this.chart = ReactDOM.render(
    <MyCustomChart data={data} />,
    element,
    done // Looker waits for React to finish
  );

구성

사용자가 시각화의 측면을 맞춤설정하도록 허용할 수 있습니다.

시각화 객체의 다른 속성 외에도 options라는 속성을 사용할 수 있습니다.

이 속성을 사용하여 차트에 필요한 옵션 유형을 지정할 수 있습니다.

looker.plugins.visualizations.add({
  options: {
    font_size: {
      type: "string",
      label: "Font Size",
      values: [
        {"Large": "large"},
        {"Small": "small"}
      ],
      display: "radio",
      default: "large"
    }
  },
  // ... rest of visualization object ...

이 예에서는 라디오 버튼으로 표시되는 font_size 옵션을 만들어 사용자가 '작은' 글꼴 크기와 '큰' 글꼴 크기 중에서 선택할 수 있도록 합니다.

시각화 선택 도구에 옵션을 추가하려면 options 속성을 지정해야 합니다.

코드에서 이 옵션을 사용하려면 updateAsync 메서드에 config 매개변수가 있음을 기억하세요. 이 매개변수에는 선택된 다음 옵션이 포함되어 있습니다.

updateAsync: function(data, element, config, queryResponse, details, done) {

config 객체는 다음과 같습니다.

{font_size: "large"}

차트를 업데이트할 때 이 객체를 확인하고 적절하게 응답할 수 있습니다. 업데이트 메서드의 끝에 다음 체크를 추가하여 글꼴 크기 변경사항을 구현합니다.

if (config.font_size == "small") {
  this._textElement.className = "hello-world-text-small";
} else {
  this._textElement.className = "hello-world-text-large";
}

또한 <style> 태그를 업데이트하여 create 메서드에 추가된 hello-world-text-small 및 hello-world-text-large CSS 클래스를 정의해야 합니다.

.hello-world-text-large {
  font-size: 72px;
}
.hello-world-text-small {
  font-size: 18px;
}

updateAsync 함수는 구성이 변경될 때마다 호출되고 Looker는 구성을 자동으로 저장하므로 구성 설정에 응답하기 위해 이 작업만 실행하면 됩니다.

다음 단계

Looker 오픈소스 GitHub 저장소에서 이 "Hello World" 시각화의 전체 소스 코드를 볼 수 있습니다.

추가 기능에 대한 자세한 내용은 시각화 API 참조를 확인하세요.