[DE] Metabase 도입 후기
서버 개발 및 DW 가공 과정
[DE] Metabase 도입 후기


metabase.com
Table of Contents
1. 도입 배경
2. Metabase 개발하기
3. 비정규화 테이블 개발하기
3.1. 배경
3.2. All Events Fact 테이블
3.3. Users Dimension 테이블
4. 회고
1. 도입 배경

아이오트러스트 데이터 파이프라인
제가 근무하고 있는 아이오트러스트에서는 BigQuery와 Redash를 중심으로 DW/BI 환경을 운영하고 있으며, 동료 분들이 이를 업무에 활용하고 있습니다. 특히, Redash를 통해 주요 지표의 변화를 모니터링하고, 이를 팀원들과 공유하며 인사이트를 도출하고 있어요.
[embed]Redash 대시보드 활용 방법과 후기 Redash 도입 배경과 구체적인 활용 방법medium.com
약 2년 전, Redash를 도입한 가장 큰 이유는 “데이터 추출의 자유도”가 높았기 때문이었어요. 출력된 쿼리 결과를 기반으로 시각화할 수 있어 복잡한 지표를 다루는 데에도 유용했죠. 따라서 조직의 데이터 기반 의사결정이 심화될수록 Redash를 지속적으로 활용할 수 있다는 점이 큰 매력이었습니다.
하지만 시간이 지나면서 Redash는 중요한 한 가지 문제를 드러내기 시작했어요. 즉, 쿼리 작성 역량을 가진 동료들만 Redash를 적극적으로 활용할 수 있었고, 그렇지 않은 동료들에게는 접근성이 현저히 낮았던 것이죠. 실제로, 아주 간단한 데이터 확인조차 쿼리를 직접 작성해야 했기 때문에 데이터 활용 속도가 떨어졌고, 소수의 동료들에게 Ad-hoc 요청 부담이 집중되는 문제가 발생하기도 했어요.
BI 도구는 “자유도”와 “접근성” 사이에서 균형을 맞춰야 하지만, Redash는 자유도는 높되 접근성이 낮은 구조였습니다. 이러한 한계를 해결하기 위해, 몇 개월 전부터 “제2의 대시보드 도입”을 논의하기 시작했어요. 즉, Redash와는 별도로, 자유도는 다소 낮더라도 노코드 기반으로 누구나 쉽게 활용할 수 있는 새로운 BI 도구를 도입하는 방안을 검토했어요.
수많은 BI 도구 중에서 Metabase를 선택한 이유는 다음과 같아요.
- 쿼리 역량이 없어도 노코드 기반으로 데이터를 추출하고 자유롭게 시각화할 수 있는 환경을 제공해요.
- 마케팅팀의 한 동료가 과거 Metabase를 활용한 경험이 있었고, 여러 측면에서 추천해주셨어요.
- 별도의 비용 없이, Docker 이미지를 활용해 오픈 소스 기반으로 Metabase를 쉽게 구축할 수 있어요.
이러한 이유로, 저희는 Redash와 병행하여 Metabase를 도입했고, 보다 많은 동료들이 쉽고 빠르게 데이터를 활용할 수 있는 환경을 구축하는 것을 목표로 두었어요.
2. Metabase 개발하기
먼저 Metabase를 서비스하기 위해 GCP Compute Engine의 VM Instance를 하나 생성했어요. 그런 후, Docker 애플리케이션을 설치했어요.
# Docker 설치하기
sudo apt-get install docker.io
# Sudo 없이 Docker 실행 환경 만들기
sudo groupadd docker
sudo gpasswd -a $USER docker
# Instance 재접속하기
logout
ssh metabase
sudo service docker restart
# 잘 설치 되었는지 확인하기
docker run hello-world
이제, Metabase 이미지를 담은 docker-compose.yaml 파일을 생성한 후, 이미지를 빌드하여 컨테이너를 실행했어요.
services:
metabase:
image: metabase/metabase
container_name: metabase
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./metabase-data:/metabase-data # 로컬 디렉토리 → 컨테이너 디렉토리
environment:
- MB_DB_FILE=/metabase-data/metabase.db # SQLite 데이터베이스 경로 설정
metabase_data디렉토리를 volume으로 설정함으로써, 컨테이너가 중단되더라도 Metabase의 사용자 계정 정보, 각 차트 정보 등 메타데이터가 안전하게 저장될 수 있도록 했어요.
docker-compose build
docker-compose up -d
VM Instance 호스트 머신이 Metabase 컨테이너와 통신하는 포트 3000에 대해, 방화벽 정책을 하나 추가했어요.

- Name:
metabase-3000 - Tag:
metabase - Direction:
Ingress - Source filters:
사내에서 사용하는 IP Address Range - Ports:
TCP:3000
그런 후, 운영 중인 VM Instance의 네트워크 태그에 metabase 정책을 추가한 후, 아래 URL에 접속해봤어요.

http://{VM Instance Public IP}:3000

아이오트러스트 Metabase
3. 비정규화 테이블 개발하기

비정규화 테이블 개발 과정 요약
3.1. 배경
Metabase 도입 과정에서 또 다른 중요한 과제가 있었어요. Core Layer와 Mart Layer에 존재하는 수많은 테이블을 하나의 통합된 뷰로 제공해야 한다는 점이었어요. 만일, Metabase에 수많은 테이블들을 연동시킬 경우, 동료 분들이 다음과 같은 어려움을 겪을 수 있겠죠.
“
purchase이벤트를 수행한 사용자들을country로 분류해서 보고 싶어. 그런데,purchase이벤트와country를 보려면 어떤 테이블을 봐야할 지 도저히 모르겠어.”
따라서, 모든 테이블들을 연동하는 것보다, 완전히 정리된 테이블 세트가 필요했어요. 저는 이를 해결하기 위해 다음과 같이 “비정규화 테이블”을 만들었어요.
- All Events Fact 테이블: 모든 이벤트 및 이벤트 파라미터가 존재하는 테이블
- All Users Dimension 테이블: 모든 사용자 및 사용자 정보가 존재하는 테이블
3.3. All Events Fact 테이블

All Events Fact 테이블 가공 과정
All Events Fact 테이블은 기존 Core Layer에 존재하는 각 이벤트 별 Fact 테이블들을 하나의 통합된 이벤트 테이블로 통합한 것이에요. 즉, 수많은 NULL이 존재하는 비정규화된 테이블인 것이죠.

All Events Fact 테이블 레코드 사례
이를 위해, 각 이벤트 전용 Core Layer 테이블들을 UNION ALL을 통해 하나의 Fact 테이블로 통합하는 dbt 모델을 만들었어요. BI 환경에서는 이 테이블만 일관적으로 사용할 수 있을 거예요.
models:
- name: all_events
description: Metabase 전용 전체 이벤트 테이블
config:
materialized: incremental
incremental_strategy: insert_overwrite
on_schema_change: append_new_columns
partition_by:
field: date
data_type: date
granularity: day
time_ingestion_partitioning: true
copy_partitions: true
columns:
- name: event_name
description: 이벤트 이름
- name: date
description: 발생 일자 (한국 시간 기준)
- name: datetime
description: 발생 일시 (한국 시간 기준)
- name: user_id
description: User ID
... (생략)
SELECT
'event_name_a' AS event_name,
date, datetime,
user_id,
... (생략)
FROM
{{ ref('core_fct_evt_event_name_a') }}
WHERE
1=1
{% if var('start_date', None) is not none and var('end_date', None) is not none %}
AND date BETWEEN
PARSE_DATE('%Y%m%d', '{{ var("start_date") }}')
AND PARSE_DATE('%Y%m%d', '{{ var("end_date") }}')
{% endif %}
UNION ALL
SELECT
'event_name_b' AS event_name,
date, datetime,
user_id,
... (생략)
FROM
{{ ref('core_fct_evt_event_name_b') }}
WHERE
1=1
{% if var('start_date', None) is not none and var('end_date', None) is not none %}
AND date BETWEEN
PARSE_DATE('%Y%m%d', '{{ var("start_date") }}')
AND PARSE_DATE('%Y%m%d', '{{ var("end_date") }}')
{% endif %}
UNION ALL
... (생략)
3.4. Users Dimension 테이블

Users Dimension 테이블 가공 과정
Fact 테이블과 함께 활용할 Users Dimension 테이블은 사용자 정보를 관리하는 테이블이에요. 사용자별 최초 방문일, 기기 유형, 최근 접속 국가, 앱 버전 등의 정보를 포함하고 있어요. 이 테이블을 All Events 테이블과 조인하여 코호트 분석을 편하게 할 수 있도록 설계했어요.
이미 기존 Core Layer에 존재하는 Users Dim 테이블이 잘 구축되었기 때문에, 몇 가지 간단한 가공만 한 채 그대로 복제해오도록 dbt 모델을 만들었어요.
models:
- name: all_users
description: Metabase 전용 전체 사용자 Dimension 테이블
config:
materialized: table
on_schema_change: append_new_columns
columns:
- name: user_id
description: User ID
- name: first_date
description: 사용자가 처음 앱을 열었거나 사이트를 방문한 일자
- name: first_datetime
description: 사용자가 처음 앱을 열었거나 사이트를 방문한 일시
- name: country
description: 국가
... (생략)
SELECT
user_id,
NULLIF(first_date, '1900-01-01') AS first_date, -- Null 치환 로직 되돌리기
NULLIF(first_datetime, '1900-01-01 00:00:00') AS first_datetime, -- Null 치환 로직 되돌리기
NULLIF(country, 'UNK') AS country, -- Null 치환 로직 되돌리기
... (생략)
FROM
{{ ref('core_dim_users') }}
4. 회고
Metabase를 도입한 후 사내 베타 테스트를 운영하는 동안, 한 마케터 동료 분이 능숙하게 몇 가지 차트를 만드는 모습을 보고 큰 보람을 느꼈어요. 데이터를 빠르게 조회하고, 문제를 식별하며, 방향을 설정하는 과정에서 Metabase가 또 하나의 강력한 도구가 될 수 있겠다는 확신이 들었죠. “제2의 대시보드” 도입이 현실화되는 과정을 보며 개인적으로도 뿌듯함을 느꼈습니다.
다만, 비정규화된 테이블을 서빙하는 과정에서 다음과 같은 단점도 고려해야 했어요.
- 데이터량 증가에 따른 쿼리 비용 문제: 대량의 레코드를 포함한 테이블이므로, 시간이 지날수록 쿼리 비용이 증가할 가능성이 있어요.
- 이벤트 파라미터 구조의 복잡성: 모든 이벤트 파라미터가 개별 칼럼으로 존재하기 때문에, 각 이벤트의 구조를 이해하려면 이벤트 정의서를 숙지해야 하는 부담이 따를 수 있어요.
모든 기술 스택에는 자유도와 접근성, 비용과 유지보수성 같은 상충 관계가 존재하기 마련입니다. 따라서, 현재의 설계 방향이 절대적인 정답은 아닐 수도 있어요. 향후 특정 프로젝트나 팀이 특정 토픽과 관련된 데이터에 대해 심층 분석이 필요할 경우, 연관된 Mart Layer 테이블을 추가로 연동하는 것이 더 적절한 해결책이 될 수도 있을 거예요.
이번 Metabase 도입 과정은 BI 도구의 “자유도”와 “접근성” 사이에서 균형을 찾는 고민을 풀어내기 위한 하나의 실험이자, 흥미로운 도전이었습니다. 무엇보다도, 제가 속한 조직이 조금 더 쉽게 데이터에 접근하고, 보다 명확하게 방향을 설정하여, 유저들에게 사랑받는 프로덕트를 만들어가는 데 기여할 수 있기를 진심으로 바라요.
Follow Us
D’CENT Wallet WEPIN Wallet Career

메타데이터
- post_id
- bbc70419a2b0
- slug
- metabase-bbc70419a2b0
- url
- https://medium.com/iotrustlab/metabase-bbc70419a2b0
- canonical_url
- https://medium.com/iotrustlab/metabase-bbc70419a2b0
- author_url
- https://medium.com/@joshua-data
- status
- ok
- fetched_at
- 2026-06-10 21:21:38