시각화 구성요소를 사용해 맞춤 시각화 빌드하기

이 튜토리얼은 숙련된 자바스크립트 개발자를 대상으로 하며 함수 프로그래밍 기술에 어느 정도 익숙하다고 가정합니다.

이 예시에서는 가상의 주간 판매 정보를 보여주는 네이티브 Looker 시각화인 막대 그래프부터 시작합니다.

그런 다음 시각화 구성요소를 사용해 지난 한 분기 동안 각 브랜드의 동향이 어떤지 보여주는 맞춤 시각화를 만들어 보겠습니다. 그러면 다음 예시와 같이 테이블 내에 중첩된 일련의 스파크라인으로 구성된 새로운 종류의 시각화가 생성됩니다.

이 예시는 맞춤 시각화를 만드는 방법과 함께, React 애플리케이션 내에서 Looker API를 사용하기 위한 몇 가지 권장사항을 보여줍니다.

Looker 구성요소로 맞춤설정된 시각화를 빌드하려면 설정이 요구사항을 충족하는지 확인한 후 다음 단계를 수행합니다.

  1. 탐색에서 쿼리를 작성하고 qid 값을 복사합니다.
  2. 맞춤 시각화 구성요소에 데이터 전달
  3. CustomVis 구성요소 빌드
  4. 정규화된 데이터 변환
  5. 변환된 데이터를 CustomVis에 삽입
  6. 맞춤 시각화 생성하기

삽입된 애플리케이션이나 확장 프로그램에 맞춤 시각화가 사용되는 경우 시각화 구성요소를 사용하여 맞춤 시각화를 제작하는 것이 적절합니다. Looker 인스턴스에서 커스텀 시각화를 Looker 사용자에게 제공하려면 visualization 문서 페이지의 안내를 따르세요. 맞춤 시각화를 개발하고 Looker Marketplace에 업로드하려면 Looker Marketplace를 위한 맞춤 시각화 개발 문서 페이지의 안내를 따르세요.

요구사항

시작하기 전에 다음 몇 가지 요소가 필요합니다.

  • Looker 인스턴스에 액세스할 수 있어야 합니다.
  • 확장 프로그램 프레임워크에서 빌드하든 자체 React 애플리케이션을 빌드하든 상관없이 Looker API로 인증하고 Looker SDK 객체에 액세스해야 합니다. 자세한 내용은 Looker API 인증 또는 확장 프레임워크를 참고하세요.
  • Looker 시각화 구성요소 NPM 패키지@looker/components-data NPM 패키지를 설치했는지 확인합니다. 시각화 구성요소 패키지 설치 및 사용에 관한 정보는 GitHubNPM에 제공되는 README 문서에서 찾을 수 있습니다.

1단계: 탐색에서 쿼리 빌드 및 쿼리 ID 복사하기

이 예에서는 한 분기 동안 추적 중인 브랜드에 대한 가상의 주간 판매 정보를 사용합니다.

탐색에서 쿼리를 실행하고 Looker의 네이티브 시각화 유형 중 하나를 사용하여 데이터 차트를 만들 수 있습니다. 차트는 많은 정보를 제공하지만 각 브랜드의 제품 트렌드를 한눈에 보기는 어렵습니다.

간단한 시각화 렌더링 예시와 마찬가지로 다음 단계는 탐색 분석의 URL 표시줄에서 qid 값을 복사하는 것입니다. 이 예에서 qid4tQKzCBOwBGNWTskQyEpG8입니다.

2단계: 맞춤 시각화 구성요소에 데이터 전달

시작하려면 탐색 URL에서 가져온 qid 값을 Query 구성요소에 전달하고 인증된 SDK 객체를 DataProvider에 전달합니다.

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query } from '@looker/visualizations'

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'></Query>
    </DataProvider>
  )
}

다음으로 Visualization 구성요소를 통해 네이티브 Looker 시각화를 렌더링하는 대신 CustomVis이라는 자체 맞춤 구성요소를 빌드합니다.

Query 구성요소는 모든 React 요소를 하위 요소로 사용할 수 있으며 config, data, fields, totals 값을 속성으로 전달하여 자체 시각화 구성요소를 렌더링합니다. CustomVisQuery의 하위 요소로 렌더링되므로 모든 관련 데이터를 속성으로 받을 수 있습니다.

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query } from '@looker/visualizations'
import { CustomVis } from '../path/to/MyCustomVis'

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'>
        <CustomVis />
      </Query>
    </DataProvider>
  )
}

3단계: CustomVis 구성요소 빌드

다음으로 CustomVis 구성요소를 빌드합니다. Query 구성요소에서 상속된 속성은 config, fields, data, totals입니다.

  • config는 스파크라인 선의 두께, 산점도의 점의 크기 및 모양 등 차트에서 데이터를 렌더링하는 모든 방법을 설명합니다.
  • fields는 쿼리에서 반환된 측정 및 측정기준 값에 대한 추가 메타데이터(예: 값의 형식 지정 방법 또는 각 축의 라벨 지정)를 저장합니다.
  • data는 쿼리에서 반환된 키/값 응답입니다.
  • totals는 테이블 기반 시각화에 사용하기 위해 Looker의 행 총계를 참조합니다.

Table 구성요소를 삽입하여 수정되지 않은 속성을 표 시각화에 전달할 수 있습니다.

import React from 'react'
import { Table } from '@looker/visualizations'

export const CustomVis = ({ config, fields, data }) => {
  return <Table config={config} data={data} fields={fields} />
}

이렇게 하면 SDK에서 직접 반환되는 데이터를 이해할 수 있습니다. 렌더링된 응답에는 매주 모든 브랜드에 대한 행이 있습니다.

4단계: 정규화된 데이터 변환

이 데이터에서 시계열 트렌드 정보를 가져오려면 행을 브랜드 이름별로 그룹화해야 합니다. 저희는 매주, 모든 브랜드에 새 행을 추가하는 대신 각 브랜드 이름에 단일 행을 사용하려고 하며, 그 안에 날짜 및 주문 수의 목록이 중첩될 것입니다.

이러한 값을 그룹화하는 커스텀 변환을 만듭니다. 아래는 이 시나리오와 관련된 예입니다. 이에 따라 자체 데이터를 파싱해야 합니다.

import React from 'react'
import { Table } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  return <Table config={config} data={data} fields={fields} />
}

이 함수를 만들려면 다음 단계를 따르세요.

  1. 먼저 brandKey라는 변수에 products.brand_name 참조를 할당합니다. 데이터 응답에서 시리즈에 라벨을 지정하는 방법이고, 이를 통해 정보를 그룹화할 수 있습니다.
  2. 다음으로, 응답을 매핑하고 products.brand_name에 할당된 값만 반환하여 고유한 브랜드 이름 목록을 빌드합니다. 그런 다음 Set 생성자로 전달됩니다. 자바스크립트에서 Set는 값이 한 번만 발생할 수 있는 목록으로, 고유한 값 목록이 자동으로 빌드됩니다.
  3. 그런 다음 고유 브랜드의 Set를 다시 배열로 변환하여 map와 같은 목록 변환 유틸리티를 활용합니다.
  4. 고유한 각 브랜드에 대해 원래 데이터 응답을 해당 브랜드 이름과 관련된 시계열 행만 필터링합니다.
  5. 마지막으로 각 브랜드 이름의 orders.count 객체 키 아래에 해당 값을 중첩합니다. 여기서도 예시로 볼 수 있습니다. 예를 들어 브랜드 이름은 문자열 값으로 설정되고 orders.count은 해당 브랜드와 관련된 모든 값의 목록으로 설정됩니다.

5단계: 변환된 데이터를 CustomVis에 삽입

이제 새 함수를 사용하여 데이터를 변환하고 nestedData라는 새 변수에 출력을 할당합니다.

import React from 'react'
import { Table } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  return <Table config={config} data={data} fields={fields} />
}

그런 다음 nestedData 배열을 루프하고 중첩된 값을 Looker의 시각화 구성요소 라이브러리에서 제공하는 표준 스파크라인 차트에 전달합니다.

import React from 'react'
import { Table, Sparkline } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  const nestedSparklines = nestedData.map(d => {
    return {
      ...d,
      'orders.count': () => (
        <Sparkline
          data={d['orders.count']}
          config={config}
          fields={fields}
          height={75}
        />
      ),
    }
  })

  return <Table config={config} data={nestedSparklines} fields={fields} />
}

위 코드에서는 다음과 같은 결과가 발생합니다.

  • 데이터가 한 번 이상 매핑되어 행마다 새로운 스파크라인을 만듭니다.
  • orders.count 키 아래에 중첩된 값 (해당 브랜드에 속하는 일련의 날짜 및 개수)이 Sparklinedata 속성에 전달됩니다.
  • 모든 차트를 렌더링하는 데 필요한 configfields 객체도 전달됩니다. 표 내의 각 행에 편안한 레이아웃을 제공하기 위해 높이도 75픽셀로 설정되어 있습니다.
  • 마지막으로 중첩된 차트 객체가 Table 차트에 전달됩니다.

6단계: 맞춤 시각화 생성

변환된 데이터를 삽입하고 차트를 구성하면 시각화는 아래 예처럼 각 행의 개별 스파크라인 차트가 표시됩니다.

위의 시각화를 렌더링하는 데 필요한 코드 전체는 다음과 같습니다.

import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query, Table, Sparkline } from '@looker/visualizations'
import { filter, pick } from 'lodash'

const transformData = data => {
  const brandKey = 'products.brand_name'

  // create a unique set of brand names
  const uniqueBrands = new Set(data.map(d => d[brandKey]))

  // convert the Set back to an array and nest values below each brand name
  // sample output:
  // [{
  //     products.brand_name: "Looker",
  //     orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
  //  }, ...]
  return Array.from(uniqueBrands).map(brand => {
    const values = filter(data, { [brandKey]: brand }).map(d =>
      pick(d, ['orders.created_week', 'orders.count'])
    )

    return { [brandKey]: brand, 'orders.count': values }
  })
}

export const CustomVis = ({ config, fields, data }) => {
  const nestedData = transformData(data)

  const nestedSparklines = nestedData.map(d => {
    return {
      ...d,
      'orders.count': () => (
        <Sparkline
          data={d['orders.count']}
          config={config}
          fields={fields}
          height={75}
        />
      ),
    }
  })

  return <Table config={config} data={nestedSparklines} fields={fields} />
}

export const MyReactApp = () => {
  const { core40SDK } = useContext(ExtensionContext)

  return (
    <DataProvider sdk={core40SDK}>
      <Query query='4tQKzCBOwBGNWTskQyEpG8'>
        <CustomVis />
      </Query>
    </DataProvider>
  )
}

다음 단계