개발자

슬랙∙깃허브∙구글 드라이브를 따로 검색하는 데 지쳤다면? "SQL로 한꺼번에"

Jon Udell  | Computerworld 2022.06.09
누구나 항상 겪는 일이다. 찾는 단어가 도대체 어디에 있는지 알 수 없어서 슬랙, 깃허브, 구글 드라이브, 구글 시트, 젠데스크 등 다양한 서비스를 일일이 다 뒤져 보는 일이다. 이때 스팀파이프(Steampipe) 대시보드를 사용하면 이 과정을 매끄럽게 실행할 수 있다.

이번이 첫 시도는 아니다. 필자는 1996년에 이 여정을 시작했고 이후 주기적으로 아이디어를 다듬었다. 2018년에는 실제 기능할 수 있는 가장 단순한 버전에 관한 기사를 썼다. 다양한 서비스에 대한 검색 URL을 몰아넣은 다음 각각의 자체 탭에서 각 서비스를 방문하는 형태의 웹 페이지다. 바보 같은 생각처럼 들릴 수도 있지만 도움이 되는 부분도 있어서 필자 외에도 사용하는 사람들이 생겼다.
 
ⓒ Getty Images Bank

물론 필자는 기반 API를 사용하고 결과를 정규화하고 이를 하나의 공통적인 뷰로 병합하고자 했다. 하지만 이렇게 하기 위해 온갖 API와 씨름해야 했는데 프로젝트가 가진 가치에 비해 난관이 너무 많았다. 이와 같은 작업을 해본 사람은 알겠지만 대부분의 서비스는 내가 선호하는 프로그래밍 언어를 위한 어댑터와 함께 검색 API를 제공한다. 그러나 각 서비스마다 API를 호출하고 결과를 페이지로 나누고 서식을 적용하는 각자의 방식이 있다. 이와 같은 차이점으로 인해 마찰이 발생한다. 결과를 일관적으로 다루기 위해서는 이 마찰을 극복해야 한다.

API 작업이 마찰 없이 된다면 많은 것이 가능해진다. 효과적인 메타검색이 그 중 하나다. 스팀파이프는 API 호출, 결과 페이지 구분, JSON 객체 압축 해제와 같은 작업에서 벗어날 수 있게 해준다. API를 대신 호출해주고 결과를 데이터베이스 테이블로 스트리밍해주므로 데이터 작업에만 온전히 집중할 수 있다. 즉, 메타검색 대시보드를 만들 때 직면하는 가장 큰 문제가 해결된다.
 

스키마로 수렴

다음 과제는 검색 결과를 공통된 스키마에 바인딩하는 것이다. SQL은 이를 위한 훌륭한 환경이다. 스크린캐스트의 대시보드를 만든 쿼리에는 SQL에 통달하지 않은 사람도 쓸 수 있는 3개의 스탠자가 포함된다. 모두 깃허브 문제를 검색하는 것과 동일한 패턴을 따른다.
select
   'github_issue' as type,
   repository_full_name || ' ' || title as source,
   to_char(created_at, 'YYYY-MM-DD') as date,
   html_url as link,
   substring(body from 1 for 200) || '...' as content
from
   github_search_issue
where
   $1 ~ 'github_issue'
   and query = 'in:body in:comments org:github ' || $2
   limit $3

 

 
파란색 항목은 데이터베이스 테이블의 열 이름이다(여기서는 스팀파이프의 깃허브 플러그인에 의해 만들어진 테이블 중 하나인 github_search_issue). 스팀파이프 허브는 테이블 열의 이름과 설명을 손쉽게 살펴볼 수 있게 해주며 테이블의 정보를 사용하는 방법에 대한 예를 보여준다.
 
이 데이터를 불러오는 데는 API 호출이나 JSON압축 해제가 불필요하므로 더 고차원의 검색 구문에만 집중할 수 있다. 여기에는 생각해야 할 부분이 많고, 소스 열을 공통 스키마로 맵핑하는 흥미롭고 재미있는(!) 과제도 있다.
 
빨간색 항목은 대시보드에 나타나는 열 이름이다. 이 대시보드의 경우 각 검색 결과가 type, source, date, link, content의 5개 열에 맵핑되도록 했다. SQL의 AS 절은 각 스탠자에서 스키마와 일치하도록 손쉽게 열 이름을 바꿀 수 있게 해준다.
 

전체 쿼리

이 대시보드의 전체 쿼리는 다음과 같다. 위와 같은 스탠자가 3개 있으며 각각은 CTE(공통 테이블 식)로 작성됐고 매개변수는 입력 변수에 해당한다. 그 외에는 거의 아무것도 없다! 각 스탠자는 API 기반 테이블을 쿼리하고(slack_search, github_search_issue, googleworkspace_drive_my_file) 열을 선택하고(변환할 수도 있음) 스키마에 맞게 결과에 별칭을 지정한다. 남은 것은 3개의 CTE에 UNION을 적용하는 것이 전부다. 이는 임시 테이블과 같은 역할을 하고 결과를 정렬한다.
 
with slack as (
  select
    'slack' as type,
    user_name || ' in #' || (channel ->> 'name')::text as source,
    to_char(timestamp, 'YYYY-MM-DD') as date,
    permalink as link,
    substring(text from 1 for 200) as content
  from
    slack_search
  where
    $1 ~ 'slack'
    and query = 'in:#steampipe after:${local.config.slack_date} ' || $2
  limit $3
),
github_issue as (
  select
    'github_issue' as type,
    repository_full_name || ' ' || title as source,
    to_char(created_at, 'YYYY-MM-DD') as date,
    html_url as link,
    substring(body from 1 for 200) || '...' as content
  from
    github_search_issue
  where
    $1 ~ 'github_issue'
    and query = ' in:body in:comments org:${local.config.github_org} ' || $2
  limit $3
),
gdrive as (
  select
    'gdrive' as type,
    replace(mime_type,'application/vnd.google-apps.','') as source,
    to_char(created_time, 'YYYY-MM-DD') as date,
    'https://docs.google.com/document/d/' || id as link,
    name as content
  from
    googleworkspace_drive_my_file
  where
    $1 ~ 'gdrive'
    and query = 'fullText contains ' || '''' || $2 || ''''
  limit $3
)

select * from slack
union 
select * from github_issue
union 
select * from gdrive

order by
  date desc
 

코드형 대시보드

많은 대시보드 시스템에서 이 쿼리를 사용할 수 있다. 예를 들어 메타베이스(Metabase)나 태블로(Tableau) 또는 다른 포스트그레스 클라이언트를 스팀파이프에 연결하고 여기서 보는 것과 같은 종류의 인터랙티브 대시보드를 만들 수 있다. 위젯과 설정이 사용자 인터페이스에서 처리되는 로우 코드 환경에서 이 작업을 할 수 있다. 스팀파이프의 대시보드 서브시스템은 코드형 인프라(IaC) 뿌리를 기반으로 하는 다른 접근 방식을 취한다. API에 대한 쿼리는 다른 모든 코드와 마찬가지로 버전 제어 리포지토리에서 관리되는 SQL 코드로 표현되어야 한다. 이러한 쿼리의 결과를 표시하는 대시보드 위젯도 마찬가지로 코드로 표현되어야 하는데, 여기서 언어는 테라폼의 HCL이다.
 
메타검색 대시보드의 HCL 정의는 다음과 같다. 3가지 종류의 input 블록(source(다중 선택), search_term(텍스트), max_per_source(단일 선택, 기본값))을 선언한다. input 블록으로 훨씬 더 많은 일을 할 수 있다. 가령 이 문서에서 볼 수 있듯이 SQL 쿼리의 결과를 사용해 채울 수 있다. 다만 여기서는 그렇게 할 필요가 없다.
 
table 블록은 위에 정의된 쿼리를 사용하며, 전달되는 매개변수를 정의한다. wrap 인수는 텍스트가 많은 열의 가독성을 높인다.
 
그 외에는 더 살펴볼 만한 부분이 없고, 있어서도 안 된다. 코드로 대시보드를 구축하는 작업에 다량의 복잡한 코드가 필요하면 안 된다.
 
dashboard "metasearch" {

  input "sources" {
    title = "sources"
    type = "multiselect"
    width = 2
    option "slack" {} 
    option "github_issue" {}
    option "gdrive" {}
  }  

  input "search_term" {
    type = "text"
    width = 2
    title = "search term"
  }

  input "max_per_source" {
    title = "max per source"
    width = 2
    option "2" {}
    option "5" {}
    option "10" {}   
    option "20" {}
  }  

  table {
    title = "search slack + github + gdrive"
    query = query.metasearch
    args = [
      self.input.sources,
      self.input.search_term,
      self.input.max_per_source
    ]
    column "source" {
      wrap = "all"
    }
    column "link" {
      wrap = "all"
    }
    column "content" {
      wrap = "all"
    }
  }

}
 

묘기는 필요 없다

새 서브쿼리를 만들기 위해 SQL의 달인이 될 필요는 없듯이, HCL 전문가가 아니더라도 대시보드에 HCL을 추가할 수 있다. 소스를 추가하려는 경우 수십 개의 다른 플러그인 중에서 선택하면 된다. 플러그인은 매월 더 추가되며 모두는 아니지만 상당수가 검색 기능을 제공한다. 당연히 스팀파이프 쿼리를 사용해 손쉽게 찾을 수 있다.
 
select
  name
  html_url
from
  github_search_code
where
  query = 'search org:turbot org:francois2metz org:ellisvalentiner org:theapsgroup'
  and name ~ 'table'
  and name ~ 'search'
order by
  name

스팀파이프 샘플 리포지토리에는 여기에 나온 대시보드를 위한 코드와 함께 평가판 계정이 만료될 때 삭제한 젠데스크를 위한 부가적인 검색 스탠자도 포함돼 있다. 이 대시보드를 확장하면서 즐거운 시간을 보내기 바란다. 필요한 검색 API를 찾을 수 없는 경우 슬랙 커뮤니티를 방문해서 문의하면 된다. 필요한 플러그인을 누군가 이미 만드는 중일 수도 있다. 물론 직접 해결할 수도 있다. 새로운 플러그인으로 기본적인 HCL과 SQL을 다룰 수 있는 누구나 자유롭게 API를 활용하고 실제 문제를 해결할 수 있을 것이다.
editor@itworld.co.kr 

회사명 : 한국IDG | 제호: ITWorld | 주소 : 서울시 중구 세종대로 23, 4층 우)04512
| 등록번호 : 서울 아00743 등록발행일자 : 2009년 01월 19일

발행인 : 박형미 | 편집인 : 박재곤 | 청소년보호책임자 : 한정규
| 사업자 등록번호 : 214-87-22467 Tel : 02-558-6950

Copyright © 2024 International Data Group. All rights reserved.