<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Andrew_la.log</title>
        <link>https://velog.io/</link>
        <description>같이 일하고 싶은 엔지니어가 되고 싶습니다</description>
        <lastBuildDate>Mon, 02 Feb 2026 20:26:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. Andrew_la.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gunho_la" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Data Engineering Zoomcamp Week 2 — Workflow Orchestration with Kestra]]></title>
            <link>https://velog.io/@gunho_la/Data-Engineering-Zoomcamp-Week-2-Workflow-Orchestration-with-Kestra</link>
            <guid>https://velog.io/@gunho_la/Data-Engineering-Zoomcamp-Week-2-Workflow-Orchestration-with-Kestra</guid>
            <pubDate>Mon, 02 Feb 2026 20:26:44 GMT</pubDate>
            <description><![CDATA[<h1>Data Engineering Zoomcamp Week 2 - Workflow Orchestration with Kestra</h1>

<hr/>

<h2>목차</h2>
<ul>
  <li><a href="#section-1">1. 워크플로우 오케스트레이션이란?</a></li>
  <li><a href="#section-2">2. Kestra 설치 방법, 핵심 개념</a></li>
  <li><a href="#section-3">3. 데이터 파이프라인 구축하기</a></li>
  <li><a href="#section-4">4. ETL: PostgreSQL 기반 파이프라인</a></li>
  <li><a href="#section-5">5. 스케줄링과 Backfill</a></li>
  <li><a href="#section-6">6. ETL vs ELT 비교</a></li>
  <li><a href="#section-7">7. BigQuery로 데이터 로드</a></li>
  <li><a href="#section-8">8. 워크플로우에 AI가 필요한 이유</a></li>
  <li><a href="#section-9">9. RAG(Retrieval Augmented Generation)란?</a></li>
</ul>

<hr/>

<p>
이번 주(Week 2)는 데이터 엔지니어링에서 “파이프라인을 만드는 것”보다 더 중요할 수 있는,
<strong>파이프라인을 안정적으로 운영하게 만드는 기술</strong>을 배운 주차였다.
</p>

<p>
주제는 Workflow Orchestration, 도구는 Kestra.
</p>

<p>
데이터 파이프라인은 보통 다음처럼 여러 도구가 얽혀 있다.
</p>

<ul>
  <li>데이터 다운로드(wget/HTTP)</li>
  <li>변환(Python)</li>
  <li>적재(PostgreSQL / BigQuery)</li>
  <li>스케줄 실행(매일/매월)</li>
  <li>실패 시 재시도 / 알림 / 로그</li>
  <li>과거 데이터 채우기(backfill)</li>
</ul>

<p>
이걸 사람이 수동으로 조율하면, 언젠가 반드시 삑난다.
그래서 “지휘자(Orchestrator)”가 필요해지고, Week 2는 그 지휘자를 Kestra로 구현해보는 과정이었다.
</p>

<hr/>

<h2 id="section-1">1. 워크플로우 오케스트레이션이란?</h2>

<h3>1) 개념: “파이프라인 지휘자”</h3>

<p>
워크플로우 오케스트레이션은 쉽게 말해:
</p>

<blockquote>
여러 작업(태스크)을 정해진 순서/조건/시간에 맞춰 실행하고,
실패를 추적하고, 자동화하는 시스템
</blockquote>

<p>
여기서 중요한 포인트는 “실행”만이 아니라 “운영”이다.
단순히 스크립트를 돌리는 건 누구나 할 수 있는데, 운영에서는 다음 문제가 항상 발생한다.
</p>

<ul>
  <li><strong>누락</strong>: 매일/매월 해야 하는데 사람이 잊는다.</li>
  <li><strong>실패</strong>: 네트워크, 권한, 스키마 변경, 리소스 부족으로 종종 터진다.</li>
  <li><strong>재실행</strong>: 실패한 시점부터 다시 돌려야 하는데 어디까지 처리됐는지 애매해진다.</li>
  <li><strong>가시성</strong>: “어디서 죽었는지” 로그/상태를 한 눈에 보고 싶다.</li>
  <li><strong>동시성</strong>: 동시에 여러 실행이 붙으면 DB에 중복 적재/락/충돌이 발생한다.</li>
</ul>

<p>
그래서 오케스트레이터는 단순 실행기가 아니라 다음 기능을 갖는다.
</p>

<ul>
  <li>워크플로우 실행(정의된 단계들을 순서대로)</li>
  <li>스케줄링(매일, 매월, 특정 시간)</li>
  <li>이벤트 기반 실행(특정 파일 도착, 특정 이벤트 발생 시)</li>
  <li>실패 감지 / 로깅 / 재시도 / 알림</li>
  <li>실행 이력(Execution) 관리</li>
  <li>동시성 제어(Concurrency)</li>
  <li>과거 데이터 재처리(Backfill)</li>
</ul>

<h3>2) Kestra와 Airflow 비교</h3>

<p>
둘 다 오케스트레이터지만 “사용 감각”이 다르다.
</p>

<h4>Kestra</h4>
<ul>
  <li><strong>YAML 기반</strong>으로 Flow를 정의한다. 코드 자산으로 관리하기 쉽다.</li>
  <li><strong>플러그인 중심</strong>이라 DB/클라우드/스크립트 실행을 “태스크로 붙이는 느낌”이 강하다.</li>
  <li>UI가 강력해서 실행 흐름(Gantt)과 로그를 보기 편하다. 초보자도 빠르게 감을 잡는다.</li>
  <li>스케줄/이벤트 기반 트리거를 자연스럽게 지원한다.</li>
</ul>

<h4>Apache Airflow</h4>
<ul>
  <li><strong>Python으로 DAG</strong>를 정의한다. 복잡한 로직은 강하지만 운영 난이도가 올라갈 수 있다.</li>
  <li>생태계가 크고 실무 레퍼런스가 많다. 업계 표준에 가깝다.</li>
  <li>개발자가 “오케스트레이션 로직 자체를 코드로 쥐고” 설계할 때 강점이 있다.</li>
</ul>

<p>
Week 2의 목표는 “개념 학습 + 빠르게 ETL/ELT 파이프라인 만들기”였고,
그 흐름에서 Kestra는 진입 장벽이 낮고, 눈으로 확인 가능한 부분이 많아서 학습에 잘 맞는다.
</p>

<hr/>

<h2 id="section-2">2. Kestra 설치 방법, 핵심 개념</h2>

<h3>1) 왜 Docker Compose로 설치하는가</h3>

<p>
오케스트레이터는 보통 단일 프로그램이 아니라, 메타데이터 DB, 실행 환경, UI 등이 함께 필요하다.
그래서 로컬 실습에서는 Docker Compose로 한 번에 올려 “재현 가능”하게 만든다.
</p>

<p>보통 다음 구성으로 띄운다.</p>

<ul>
  <li><strong>Kestra 서버</strong>: UI + 스케줄러 + 실행 엔진</li>
  <li><strong>PostgreSQL(메타데이터 DB)</strong>: 실행 이력, 상태, 설정 등을 저장</li>
  <li><strong>PostgreSQL(파이프라인 데이터 DB)</strong>: NYC taxi 같은 실제 데이터를 적재</li>
  <li><strong>pgAdmin</strong>: Postgres GUI</li>
</ul>

<p>
접속은 보통 Kestra UI <code>http://localhost:8080</code>.
포트 충돌(특히 8080)이 나면 docker-compose에서 포트 매핑을 바꿔야 한다.
</p>

<h3>2) Kestra 핵심 개념(실제로 YAML 읽을 때 중요)</h3>

<ul>
  <li><strong>Flow</strong>: 태스크 묶음. 오케스트레이션 단위(하나의 파이프라인).</li>
  <li><strong>Task</strong>: 실행 단계 하나. (wget, SQL 실행, 파일 삭제 등)</li>
  <li><strong>Inputs</strong>: Flow 실행 시 주입되는 파라미터(택시 타입, 년/월 등).</li>
  <li><strong>Variables</strong>: 문자열 조합/재사용. 파일명/테이블명 만드는 데 핵심.</li>
  <li><strong>Outputs</strong>: 태스크 결과. “다운로드 파일 경로” 같은 걸 다음 태스크가 사용.</li>
  <li><strong>Triggers</strong>: 자동 실행 장치. 스케줄/이벤트 기반 실행.</li>
  <li><strong>Execution</strong>: Flow가 실제로 한 번 실행된 기록(상태, 로그, 시작/종료 시간).</li>
  <li><strong>Plugin Defaults</strong>: 공통 설정(DB 접속 정보 등)을 반복하지 않게 기본값 지정.</li>
  <li><strong>Concurrency</strong>: 동시 실행 제한. 중복 적재나 DB 충돌을 막는 장치.</li>
  <li><strong>Namespace</strong>: Flow를 그룹으로 묶어 관리하는 논리적 경로.</li>
</ul>

<p>
특히 Variables / Outputs / Triggers를 이해하면, Kestra YAML이 “왜 이렇게 생겼는지”가 보인다.
</p>

<hr/>


<h2 id="section-3">3. 데이터 파이프라인 구축하기</h2>

<p>
Week 2에서 우리가 만든 데이터 파이프라인은 단순히 “CSV를 DB에 넣는 코드”가 아니다.
목표는 <strong>반복 실행 가능하고, 실패해도 다시 돌릴 수 있는 파이프라인</strong>을 만드는 것이다.
</p>

<p>
뉴욕 택시 데이터 파이프라인의 기본 흐름은 다음과 같다.
</p>

<pre>
1) 입력값 받기 (taxi type, year, month)
2) 파일명 / 테이블명 동적 생성
3) GitHub에서 CSV 다운로드
4) 스테이징 테이블 적재
5) 고유 키 생성
6) 메인 테이블에 병합(MERGE)
7) 임시 파일 정리
</pre>

<p>
이 흐름에서 중요한 점은 “각 단계가 독립적인 태스크(Task)”라는 것이다.
Kestra는 이 단계들을 순서대로 실행하면서,
어디서 실패했는지, 어떤 입력값으로 실행됐는지를 모두 기록한다.
</p>

<p>
즉, 이 파이프라인은 단순 ETL 코드가 아니라
<strong>운영 가능한 워크플로우</strong>다.
</p>

<hr/>

<h2 id="section-4">4. ETL: PostgreSQL 기반 파이프라인</h2>

<p>
04_postgres_taxi 흐름은 로컬 Postgres를 대상으로 한 ETL 파이프라인이다.
여기서 가장 중요한 설계 포인트는 <strong>Staging Table + Merge 전략</strong>이다.
</p>

<h3>1) 왜 바로 메인 테이블에 넣지 않을까?</h3>

<p>
CSV를 바로 메인 테이블에 넣으면 다음 문제가 생긴다.
</p>

<ul>
  <li>같은 파일을 다시 실행하면 중복 데이터가 쌓인다</li>
  <li>월별 데이터를 부분 재처리하기 어렵다</li>
  <li>변환 로직을 적용하기가 까다롭다</li>
</ul>

<p>
그래서 실무에서는 거의 항상 다음 패턴을 쓴다.
</p>

<pre>
CSV → Staging Table → 변환 → Main Table
</pre>

<h3>2) 스테이징 테이블의 역할</h3>

<ul>
  <li>CSV 원본을 그대로 적재</li>
  <li>실행 단위(월별)로 깨끗하게 초기화(TRUNCATE)</li>
  <li>변환/검증/고유 키 생성 작업 수행</li>
</ul>

<h3>3) 고유 키(unique_row_id)가 핵심인 이유</h3>

<p>
택시 데이터에는 명시적인 primary key가 없다.
그래서 여러 컬럼을 조합해 <code>md5 해시</code>로 고유 ID를 만든다.
</p>

<p>
이 고유 ID는 사실상 “이 행의 지문(fingerprint)”이다.
</p>

<p>
MERGE 시 이 키를 기준으로:
</p>

<ul>
  <li>이미 있는 데이터 → 무시</li>
  <li>없는 데이터 → INSERT</li>
</ul>

<p>
이 덕분에 같은 파일을 여러 번 실행해도
데이터가 중복되지 않는다.
</p>

<hr/>

<h2 id="section-5">5. 스케줄링과 Backfill</h2>

<h3>1) 스케줄링이 필요한 이유</h3>

<p>
데이터 파이프라인은 “한 번 돌리고 끝”이 아니다.
</p>

<ul>
  <li>매일 새 데이터가 쌓인다</li>
  <li>매월 집계가 필요하다</li>
  <li>사람이 기억해서 실행하면 반드시 빠진다</li>
</ul>

<p>
그래서 Kestra에서는 <strong>Trigger</strong>를 이용해 자동 실행한다.
</p>

<pre>
cron: "0 9 1 * *"
→ 매월 1일 오전 9시 실행
</pre>

<h3>2) trigger.date는 언제 생기나?</h3>

<p>
scheduled flow에서 자주 쓰는 표현식이 <code>{{trigger.date}}</code>다.
</p>

<p>
중요한 점은 이것이다:
</p>

<ul>
  <li>Trigger로 실행될 때만 trigger.date가 존재한다</li>
  <li>수동 실행(Manual Execute)에는 trigger가 없다</li>
</ul>

<p>
그래서 trigger.date를 쓰는 flow를 수동 실행하면
에러가 나는 게 정상이다.
</p>

<h3>3) Backfill이란?</h3>

<p>
Backfill은 “과거 데이터를 한꺼번에 채워넣는 것”이다.
</p>

<p>
예를 들어:
</p>

<ul>
  <li>2020년 전체 데이터를 지금 다시 적재</li>
  <li>워크플로우가 만들어지기 전의 기간 재처리</li>
</ul>

<p>
Kestra UI에서는 날짜 범위를 지정해
트리거 실행을 과거로 소급(backfill)할 수 있다.
</p>

<p>
이 기능 덕분에:
</p>

<ul>
  <li>12개월을 수동으로 실행할 필요가 없다</li>
  <li>실패한 달만 골라서 재실행 가능하다</li>
</ul>

<hr/>

<h2 id="section-6">6. ETL vs ELT 비교</h2>

<p>
Week 2의 중요한 전환점은
“ETL만이 답이 아니다”라는 인식이다.
</p>

<h3>ETL</h3>

<ul>
  <li>Extract → Transform → Load</li>
  <li>로컬 DB, 온프레미스 환경에 적합</li>
  <li>변환 후 적재 → 저장 공간 절약</li>
  <li>대규모 데이터에서는 변환이 병목</li>
</ul>

<h3>ELT</h3>

<ul>
  <li>Extract → Load → Transform</li>
  <li>클라우드 DW(BigQuery 등)에 적합</li>
  <li>원본 데이터를 그대로 보존</li>
  <li>변환은 DW의 연산 성능을 활용</li>
</ul>

<p>
Zoomcamp 흐름도 이 차이를 명확히 보여준다.
</p>

<ul>
  <li>로컬 Postgres → ETL</li>
  <li>GCP(BigQuery) → ELT</li>
</ul>

<hr/>

<h2 id="section-7">7. BigQuery로 데이터 로드</h2>

<p>
GCP 환경에서는 파이프라인 구조가 바뀐다.
</p>

<h3>GCP ELT 흐름</h3>

<pre>
Extract → GCS 업로드 → BigQuery 테이블 생성 → BigQuery에서 변환
</pre>

<p>
여기서 GCS는 데이터 레이크 역할을 한다.
즉, <strong>원본 CSV를 보관하는 저장소</strong>다.
</p>

<h3>KV Store와 Secrets의 중요성</h3>

<p>
GCP 설정에는 민감한 정보가 많다.
</p>

<ul>
  <li>서비스 계정 JSON</li>
  <li>프로젝트 ID</li>
  <li>버킷 이름</li>
</ul>

<p>
이걸 YAML에 하드코딩하면:
</p>

<ul>
  <li>보안 문제</li>
  <li>환경별 관리 지옥</li>
</ul>

<p>
그래서 Kestra는 KV Store와 Secrets를 제공한다.
</p>

<hr/>

<h2 id="section-8">8. 워크플로우에 AI가 필요한 이유</h2>

<p>
데이터 엔지니어링 워크플로우는 생각보다 반복 작업이 많다.
</p>

<ul>
  <li>YAML 보일러플레이트</li>
  <li>플러그인 옵션 기억</li>
  <li>사소한 오타로 인한 실패</li>
</ul>

<p>
AI는 이 반복 작업을 줄이는 데 특화되어 있다.
</p>

<p>
하지만 중요한 전제가 있다.
</p>

<blockquote>
AI는 컨텍스트가 없으면 반드시 틀린다.
</blockquote>

<p>
범용 ChatGPT는:
</p>

<ul>
  <li>플러그인 이름을 틀리거나</li>
  <li>존재하지 않는 옵션을 만들어내거나</li>
  <li>옛 문법을 섞어 쓴다</li>
</ul>

<p>
그래서 Kestra는 “자기 문서 기준”의 AI Copilot을 제공한다.
</p>

<hr/>

<h2 id="section-9">9. RAG(Retrieval Augmented Generation)란?</h2>

<p>
RAG는 AI의 환각(hallucination)을 줄이기 위한 핵심 기술이다.
</p>

<p>
개념은 단순하다.
</p>

<pre>
질문 → 관련 문서 검색 → 문서를 포함해 AI에게 질문
</pre>

<p>
즉, AI가 “기억”이 아니라
<strong>실제 문서</strong>를 근거로 답하게 만드는 방식이다.
</p>

<p>
Kestra 예제에서는:
</p>

<ul>
  <li>릴리즈 노트 ingest</li>
  <li>문서 임베딩 생성</li>
  <li>KV Store에 저장</li>
  <li>질문 시 관련 문서 검색</li>
</ul>

<p>
이 덕분에:
</p>

<ul>
  <li>존재하지 않는 기능을 말하지 않고</li>
  <li>버전 기준으로 정확한 답변을 한다</li>
</ul>

<hr/>

<h2>마무리</h2>

<p>
Week 2는 Kestra를 “써본 주차”가 아니라,
</p>

<ul>
  <li>왜 오케스트레이션이 필요한지</li>
  <li>ETL을 운영 가능한 구조로 만드는 법</li>
  <li>스케줄링과 백필의 실전 의미</li>
  <li>클라우드에서 ELT로 사고 전환</li>
  <li>AI를 쓸 때 왜 컨텍스트와 RAG가 중요한지</li>
</ul>

<p>
이걸 한 번에 연결해 이해하게 만든 주차였다.
</p>

]]></description>
        </item>
    </channel>
</rss>