Metabase를 Supabase에 안전하게 연결하기 – Read-Only Role, RLS Policy

🧭 상황 Supabase에 저장된 데이터를 시각화하기 위해 Metabase 대시보드를 셋업 🔒 Metabase 연결 시 보안 옵션 옵션 1. EC2 보안 그룹에서 IP 화이트리스트 설정 집 또는 사무실 IP만 인바운드 접근 허용 효과: 오직 본인만 Metabase 로그인 페이지에 접근 가능 단점: 이동 중이거나 다른 네트워크를 사용할 때마다 규칙을 갱신해야 함 옵션 2. 리버스 프록시 + 기본 인증 (Basic Auth) 적용 같은 EC2 인스턴스에 Nginx 리버스 프록시를 실행 Nginx에서 다음을 처리: HTTPS (TLS 인증서) Metabase 진입 전 추가 비밀번호 게이트 외부에는 443(HTTPS) 포트만 노출하고, 3000 포트는 비공개 ...

10월 29, 2025

Postgres FTS와 dbt 인덱스로 빠르고 정확한 검색 만들기

이번 글에서는 내가 KBooks 사이트에 검색 기능을 처음 추가했던 과정을 공유한다. 국립중앙도서관 API에서 수집한 모든 도서 데이터는 이미 dbt 파이프라인을 통해 Supabase(Postgres) 로 들어오고 있었다. 이제 검색을 붙여보겠다. 아래는 데이터베이스 수준에서 검색을 준비한 단계별 과정과, 각각의 결정이 왜 타당했는지에 대한 설명이다. 1. 깨끗한 출발점: silver_books 원본 데이터는 raw_nl_books에 JSON 형태로 저장된다. 여기서 dbt를 통해 silver_books 테이블을 만들었고, 각 행은 ISBN-13으로 식별되는 고유한 책 하나를 나타낸다. 유효한 ISBN만 남겨 중복을 제거했다. ...

10월 23, 2025

📚 국립중앙도서관 API로 25년치 도서 데이터 백필 파이프라인 만들기

에어플로우 없이, 끊겨도 자동으로 이어받는 신뢰성 높은 ETL을 설계한 이야기 1. 만들게 된 이유 국립중앙도서관(NLK) 은 서지정보API를 통해 한국에 등록된 모든 도서의 서지 데이터를 공개하고 있다. 나는 2000년 1월부터 2024년 12월까지, 즉 25년치 전체 데이터를 PostgreSQL(Supabase) 에 저장해 보고 싶었다. 처음엔 단순히 “API를 루프 돌리면 되겠지” 싶었다. 하지만 실제로는 아래 문제들을 해결해야 했다. 호출 제한(rate limit)과 연결 끊김(timeout) 25년 × 12개월 = 300개월 분량의 데이터 EC2 연결이 중간에 끊기는 문제 수백만 건 JSON의 중복 삽입 방지 2. NLK API 이해하기 참고: 국립중앙도서관 서지정보 API 문서 ...

10월 22, 2025

🚀 핀테크 배치 ETL 파이프라인 — 모듈식으로 구축하기

👉 Code, Portfolio, Blog, and LinkedIn 🎯 거래 데이터 배치 파이프라인 어느날 K-pop 데몬 헌터들이 핀테크 스타트업을 만들었다고 가정하자. 이제 그들은 매일 수백만 건의 신용카드 거래를 처리하고 이해해야 한다. ...

9월 12, 2025

📚 SQL Struct: 데이터베이스가 책장처럼 생각하는 법을 배울 때

SQL Struct: 데이터베이스가 책장처럼 생각하는 법을 배울 때 데이터베이스를 거대한 도서관이라고 생각해보자. 지금까지 수십 년 동안, 우리는 전통적인 도서관 목록 시스템처럼 데이터를 정리해왔다. 모든 책의 정보를 각각 다른 카드에 적어서 별도의 서랍에 보관하는 방식 말이다. 제목 카드는 여기, 저자 카드는 저기, 출판연도는 또 다른 곳에. 이게 바로 데이터베이스에서 말하는 **정규화(normalization)**다. 그런데 만약 도서관이 책의 모든 정보를 하나의 똑똑한 봉투에 담을 수 있다면 어떨까? 제목, 저자, 출판 정보, 심지어 리뷰까지 모두 깔끔하게 한 곳에 모아둘 수 있다면? 이게 바로 현대 SQL 데이터베이스에서 struct가 해주는 일이다. ...

6월 17, 2025

💯 dbt 테스트 이해하기: 기본부터 중급까지

dbt로 데이터를 변환하고 있다면 이미 잘하고 있는 겁니다. 🙌 그런데 dbt에는 데이터를 더 깨끗하고 신뢰할 수 있게 유지하는 강력한 테스트 기능도 있다는 거, 알고 계셨나요? 이번 글에서는 다음을 다룹니다: ✅ 기본 dbt 테스트 — 빠르게 적용 가능한 기본기 🚀 중급 테스트 — 사용자 정의 로직과 재사용 가능한 매크로 ✅ 기본 dbt 테스트 (Built-in) dbt는 모델의 .yml 파일 안에서 바로 사용할 수 있는 기본 테스트들을 제공합니다. 예시: version: 2 models: - name: customers description: 고객 마스터 테이블 columns: - name: customer_id tests: - not_null - unique - name: email tests: - not_null 🔧 각 테스트가 하는 일: not_null: 컬럼에 NULL 값이 없는지 확인 unique: 값이 고유한지 검증 accepted_values: 허용된 값만 포함되어 있는지 체크 relationships: 외래 키가 참조 대상 테이블과 매칭되는지 확인 accepted_values 예시 - name: status tests: - accepted_values: values: ['active', 'inactive', 'suspended'] 이런 기본 테스트들은 생산 대시보드에 오류가 반영되기 전에 단순한 데이터 이상을 빠르게 잡을 수 있습니다. ...

6월 10, 2025

PostgreSQL의 의외의 함정: Boolean, 텍스트 I/O, 그리고 ETL 이슈

PostgreSQL은 강력하고 표준을 잘 따르는 데이터베이스입니다. 하지만 의외의 작은 함정들도 있죠. 그중 하나는 바로 boolean 값을 다루는 방식, 특히 데이터를 텍스트 형식으로 내보낼 때의 이야기입니다. 🧠 PostgreSQL의 Boolean 처리 방식: 생각과 다르다 PostgreSQL은 boolean 값을 내부적으로 1비트(bit) 만으로 효율적으로 저장합니다. 예상한 대로죠. 하지만 그 값을 텍스트로 변환하거나 COPY 같은 방식으로 내보내면, 결과는 좀 다릅니다: SELECT true::text; -- 결과: 't' SELECT false::text; -- 결과: 'f' 맞습니다 — true는 't', false는 'f' 로 표현됩니다. 이건 PostgreSQL의 텍스트 I/O 기본 동작 방식인데, 이 동작 때문에 시스템 간 데이터를 주고받을 때 미묘한 버그가 생기기도 합니다. ...

6월 10, 2025

Spark → Kafka → Postgres 파이프라인에서 UUID가 터트린 지뢰

Kafka와 Spark Structured Streaming을 이용해서 데이터 파이프라인을 구축하고 있었습니다. 완전히 컨테이너화된 시스템. 스택 구성은 이렇습니다: Kafka → 거래 데이터를 스트리밍으로 전송 Spark Structured Streaming → 실시간 처리 및 이상 거래 탐지 Postgres → 데이터 웨어하우스 모든 게 순조로웠습니다. 그런데 갑자기 등장한 한 놈. UUID 필드. 맞습니다 — UUID. 이제 어떤 일이 벌어졌는지 정확히 보여드릴게요. ✅ 원래 설계 Postgres 테이블을 이렇게 설계했죠: CREATE TABLE fact_transaction ( transaction_id UUID PRIMARY KEY, customer_id UUID REFERENCES dim_customer(customer_id), merchant_id UUID REFERENCES dim_merchant(merchant_id), ... ); Kafka는 UUID를 문자열로 직렬화해서 이벤트를 잘 뿌려주고 있었습니다 (JSON은 원래 UUID 타입이 없으니까요). ...

6월 7, 2025

🛡️ Spark Docker 스트리밍에서 Kerberos 사용자 인증 문제 해결

Spark, Kafka, Docker를 사용하여 실시간 스트리밍 파이프라인을 구축하던 중, Kerberos를 사용하지도 않았는데 Kerberos 인증과 관련된 Spark 오류가 발생했습니다. org.apache.hadoop.security.KerberosAuthException: failure to login: javax.security.auth.login.LoginException: java.lang.NullPointerException: invalid null input: name ❓ 문제가 발생한 원인은? 공식 apache/spark:3.5.0 Docker 이미지를 사용하고 있었습니다. Docker 내부의 Spark가 Hadoop의 기본 인증 메커니즘을 해결하려고 시도했습니다. Hadoop은 다음을 통해 현재 OS 사용자를 검색하려고 했습니다: UnixPrincipal(name) Docker 컨테이너 내부에서 앱이 적절한 사용자명 매핑이 없는 UID/GID로 실행되고 있었습니다. 이로 인해 다음과 같은 오류가 발생했습니다: invalid null input: name UnixPrincipal()이 null을 받았기 때문입니다. ...

6월 7, 2025

🫙 Spark Streaming의 마지막 장애물: 왜 --jars 만으로 Kafka가 안 될까

데이터 엔지니어로서 복잡한 분산 시스템을 구축하면서 마지막 장애물을 넘었을 때 느끼는 성취감은 정말 특별합니다. 오늘은 Apache Spark Structured Streaming + Kafka를 사용할 때 굉장히 답답한 문제를 공유하려고 합니다: 👉 바로 악명 높은 Failed to find data source: kafka 에러입니다. 🧨 문제: 모든 게 정상인데 Kafka만 안 된다 상황을 떠올려봅시다: Spark 클러스터 정상 구동 Postgres 연결도 문제 없음 Kafka에서 이벤트도 잘 발행됨 코드에서 .readStream.format("kafka") 호출 그런데 갑자기 다음과 같은 에러가 발생: ...

6월 7, 2025