Skip to content

minyeamer/linkmerce

Repository files navigation

LinkMerce

이컀머슀 데이터 μˆ˜μ§‘, λ³€ν™˜, 적재 톡합 ν”„λ ˆμž„μ›Œν¬

λ‹€μ–‘ν•œ 이컀머슀 API와 μ›Ήλ¬Έμ„œ 응닡을 μˆ˜μ§‘ν•˜κ³  DuckDB 기반 λ³€ν™˜μ„ 거쳐, BigQuery, PostgreSQL, Google Sheets λ“± μ™ΈλΆ€ μ‹œμŠ€ν…œμ— 데이터λ₯Ό μ μž¬ν•˜λŠ” μ›Œν¬ν”Œλ‘œμš°λ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•œ ν”„λ‘œμ νŠΈλ₯Ό μ•ˆλ‚΄ν•œλ‹€.

λͺ©μ°¨

ν”„λ‘œμ νŠΈ κ°œμš”

LinkMerce ν”„λ‘œμ νŠΈλŠ” 이컀머슀 ν”Œλž«νΌμœΌλ‘œλΆ€ν„° μ‡Όν•‘λͺ° μš΄μ˜μ— ν•„μš”ν•œ 데이터λ₯Ό μˆ˜μ§‘ν•˜κΈ° μœ„ν•œ λͺ©μ μ„ κ°€μ§„λ‹€.

Python μŠ€ν¬λž˜ν•‘ λ‘œμ§μ„ κ΅¬ν˜„ν•œ PyPI νŒ¨ν‚€μ§€κ°€ ν”„λ‘œμ νŠΈμ˜ 쀑심이 되며, μž‘μ—… μŠ€μΌ€μ€„λ§μ„ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ Apache Airflowλ₯Ό 적극적으둜 ν™œμš©ν•œλ‹€.

ν˜„μž¬ linkmerce νŒ¨ν‚€μ§€ 버전은 1.0.6이닀.

ν”„λ‘œμ νŠΈμ—μ„œ μ£Όλͺ©ν•  뢀뢄은 λ‹€μŒ 5κ°€μ§€λ‹€.

  1. src/linkmerce: μ›Ή μŠ€ν¬λž˜ν•‘ 및 μ™ΈλΆ€ μ‹œμŠ€ν…œκ³Όμ˜ 데이터 연동을 μ§€μ›ν•˜λŠ” 핡심 Python νŒ¨ν‚€μ§€
  2. src/tests: Python νŒ¨ν‚€μ§€μ˜ λ™μž‘μ„ κ²€μ¦ν•˜κ³  쀑간 μ‹€ν–‰ κ²°κ³Όλ₯Ό μ €μž₯ν•˜λŠ” ν…ŒμŠ€νŠΈ λͺ¨μŒ
  3. airflow: μž‘μ—… μŠ€μΌ€μ€„λ§, μ˜€μΌ€μŠ€νŠΈλ ˆμ΄μ…˜, Playwright 기반 λΈŒλΌμš°μ € μžλ™ν™”κ°€ λ“€μ–΄ μžˆλŠ” Dag λͺ¨μŒ
  4. postgres: 둜컬 PostgreSQL 18 적재 ν™˜κ²½κ³Ό 초기 μŠ€ν‚€λ§ˆ, νŒŒν‹°μ…˜, Parquet ν™•μž₯을 κ΄€λ¦¬ν•˜λŠ” μ‹€ν–‰ ν™˜κ²½
  5. airflow_trigger: μ‡Όν•‘λͺ° 운영 λ‹΄λ‹Ήμžκ°€ Airflow Dag을 μˆ˜λ™μœΌλ‘œ νŠΈλ¦¬κ±°ν•  λ•Œ μ‚¬μš©ν•˜λŠ” Streamlit UI와 Google Sheets 링크/λ²„νŠΌμš© FastAPI μ„œλ²„

linkmerce νŒ¨ν‚€μ§€κ°€ μ§€μ›ν•˜λŠ” 이컀머슀 κ΄€λ ¨ ν”Œλž«νΌμ˜ μˆ˜μ§‘ λ²”μœ„λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

ν”Œλž«νΌ ꡬ뢄 μˆ˜μ§‘ λ²”μœ„
CJλŒ€ν•œν†΅μš΄ eFLEXs 재고
쿠팑 κ΄‘κ³ μ„Όν„° κ΄‘κ³ 
쿠팑 νŒλ§€μžμ„Όν„° μƒν’ˆ, 맀좜
이카운트 API μƒν’ˆ, 재고
ꡬ글 API κ΄‘κ³ 
메타 API κ΄‘κ³ 
넀이버 메인 검색
넀이버 μ˜€ν”ˆ API 검색
사방넷 μ‹œμŠ€ν…œ μ£Όλ¬Έ, μƒν’ˆ
넀이버 검색광고 API κ΄‘κ³  λ³΄κ³ μ„œ, κ΄‘κ³  계약, κ²€μƒ‰λŸ‰
넀이버 κ΄‘κ³ μ£Όμ„Όν„° (검색광고) κ΄‘κ³  λ³΄κ³ μ„œ
넀이버 μ„±κ³Όν˜• λ””μŠ€ν”Œλ ˆμ΄ κ΄‘κ³  κ΄‘κ³  λ³΄κ³ μ„œ, κ΄‘κ³  μˆœμœ„
넀이버 컀머슀 API μ£Όλ¬Έ, μƒν’ˆ, 톡계
넀이버 μ‡Όν•‘νŒŒνŠΈλ„ˆμ„Όν„° 맀좜, λ°©λ¬Έ 톡계, μΉ΄νƒˆλ‘œκ·Έ/μƒν’ˆ

핡심 μ•„ν‚€ν…μ²˜

linkmerce νŒ¨ν‚€μ§€λ₯Ό μ΄ν•΄ν•˜κΈ° μœ„ν•΄ μ£Όλͺ©ν•΄μ•Ό ν•  것은 Extractor β†’ Transformer 연결이닀.

linkmerce νŒ¨ν‚€μ§€λŠ” ETL ν”„λ‘œμ„ΈμŠ€λ₯Ό [ μΆ”μΆœ(Extract), λ³€ν™˜(Transform), 적재(Load) ] 3κ°€μ§€ λΆ€λΆ„μœΌλ‘œ κ΅¬λΆ„ν•œλ‹€. Extractor와 TransformerλŠ” 각각 μΆ”μΆœκ³Ό λ³€ν™˜ 역할을 λ‹΄λ‹Ήν•œλ‹€.

  1. ExtractorλŠ” 동기 λ˜λŠ” 비동기 HTTP μ„Έμ…˜μ„ ν™œμš©ν•œ HTTP μš”μ²­μ„ λ‹΄λ‹Ήν•œλ‹€. 일뢀 μž‘μ—…μ€ λ§€κ°œλ³€μˆ˜ λͺ©λ‘μ— λŒ€ν•΄ 반볡 μš”μ²­ν•˜λŠ”λ°, Taskλ₯Ό ν™œμš©ν•΄ μ΄λŸ¬ν•œ λ™μž‘μ„ μΆ”μƒν™”ν•œλ‹€.
  2. TransformerλŠ” HTTP 응닡 κ²°κ³Όλ₯Ό JSON ν˜•μ‹μœΌλ‘œ νŒŒμ‹±ν•˜κ³ , DuckDB ν…Œμ΄λΈ”μ˜ μŠ€ν‚€λ§ˆμ— 맞게 λ³€ν™˜ν•΄ μ μž¬ν•œλ‹€. DuckDB 연결을 톡해 BigQuery, PostgreSQL, Google Sheets 같은 μ™ΈλΆ€ μ‹œμŠ€ν…œμ— 데이터λ₯Ό μ μž¬ν•˜λŠ” ν™•μž₯ κΈ°λŠ₯을 λ³„λ„λ‘œ μ œκ³΅ν•œλ‹€.

μ§€κΈˆκΉŒμ§€μ˜ μ„€λͺ…을 μ•„λž˜ ν‘œλ‘œ 정리할 수 μžˆλ‹€.

계측 ꡬ뢄 μ±…μž„ κ΅¬ν˜„ 경둜
Extractor HTTP μ„Έμ…˜ 관리, μš”μ²­ λ©”μ‹œμ§€ λΉŒλ“œ src/linkmerce/common/extract.py
Task 반볡 μš”μ²­, μž¬μ‹œλ„, νŽ˜μ΄μ§€λ„€μ΄μ…˜ src/linkmerce/common/tasks.py
ResponseTransformer 응닡 κ²°κ³Ό νŒŒμ‹±, JSON ν˜•μ‹μœΌλ‘œ λ³€ν™˜ src/linkmerce/common/transform.py
DuckDBTransformer DuckDB ν…Œμ΄λΈ” 생성 및 적재 src/linkmerce/common/transform.py
DuckDBConnection DuckDB μ—°κ²° 관리, CRUD μž‘μ—… 지원 src/linkmerce/common/load.py
API Endpoint Extractor와 Transformer μ—°κ²° src/linkmerce/api/common.py
Extensions DuckDB ν…Œμ΄λΈ”μ„ μ™ΈλΆ€ μ‹œμŠ€ν…œκ³Ό 연동 src/linkmerce/extensions/*.py

계측 ꡬ뢄에 λ”°λ₯Έ μ›Œν¬ν”Œλ‘œμš°λŠ” λ˜ν•œ λ‹€μŒμ˜ νλ¦„λ„λ‘œ 정리해볼 μˆ˜λ„ μžˆλ‹€.

[API Endpoint] (api/...)
    ↓   μ‚¬μš©μž νŒŒλΌλ―Έν„° 전달
Extractor (core/.../extract.py)
    ↓   HTTP μš”μ²­ 및 응닡 μˆ˜μ‹ 
ResponseTransformer (core/.../transform.py)
    ↓   JSON ν˜•μ‹μœΌλ‘œ λ³€ν™˜
DuckDBTransformer (core/.../transform.py)
    ↓   DuckDB ν…Œμ΄λΈ”μ— 적재
DuckDBConnection (common/load.py)
    ↓   μ™ΈλΆ€ μ‹œμŠ€ν…œμ— 적재
BigQueryClient / PostgresClient / WorksheetClient (extensions/...)

ν‘œμ€€ ETL λͺ¨λ“ˆ ꡬ쑰

ETL ν”„λ‘œμ„ΈμŠ€λ₯Ό ν”Œλž«νΌ, 호슀트λͺ…, μΉ΄ν…Œκ³ λ¦¬λ‘œ κ΅¬μ„±λœ 3단계 ν•˜μœ„ 경둜둜 κ΅¬λΆ„ν•œλ‹€.

core/{platform}/{hostname}/{category}/
β”œβ”€β”€ extract.py
β”œβ”€β”€ transform.py
└── models.sql

이 κ΅¬μ‘°λŠ” ν•˜λ‚˜μ˜ μΉ΄ν…Œκ³ λ¦¬ μ•ˆμ—μ„œ μ±…μž„μ„ λͺ…ν™•νžˆ λΆ„λ¦¬ν•œλ‹€.

  • extract.py: HTTP μš”μ²­ 방식 κ΅¬ν˜„
  • transform.py: 응닡 νŒŒμ‹± 및 DuckDB 적재 방식 κ΅¬ν˜„
  • models.sql: CREATE, INSERT λ“± SQL 쿼리문 섀계

이 νŒ¨ν„΄ 덕뢄에 ν”Œλž«νΌ λ˜λŠ” ν˜ΈμŠ€νŠΈλ³„λ‘œ κ΅¬ν˜„μ΄ 달라도, μ‹€ν–‰ 방식과 ν…ŒμŠ€νŠΈ 방식은 비ꡐ적 μΌκ΄€λ˜κ²Œ μœ μ§€λœλ‹€.

ETL ν”„λ‘œμ„ΈμŠ€λ₯Ό μ‹€ν–‰ν•  λ•ŒλŠ” νŽΈμ˜μ„±μ„ μœ„ν•΄ core/ λͺ¨λ“ˆμ„ μˆœμ„œλŒ€λ‘œ ν˜ΈμΆœν•˜μ§€ μ•Šκ³ , ν•˜λ‚˜μ˜ API ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.

linkmerce νŒ¨ν‚€μ§€μ™€ κ΄€λ ¨λœ src/linkmerce/ κ²½λ‘œμ— λŒ€ν•œ 상세 μ„€λͺ…은 λ³„λ„μ˜ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•œλ‹€.

ν…ŒμŠ€νŠΈ μ•ˆλ‚΄

src/tests κ²½λ‘œμ—μ„  단일 Extractor λ˜λŠ” Transformer λ‹¨μœ„λ‘œ 정상 λ™μž‘ν•˜λŠ”μ§€ ν…ŒμŠ€νŠΈλ₯Ό λ‹΄λ‹Ήν•œλ‹€.

  • test_extract.py: Extractor.extract() μ‹€ν–‰ κ²°κ³Ό μ €μž₯
  • test_transform.py: DuckDBTransformer.parse() 및 bulk_insert() μ‹€ν–‰ κ²°κ³Ό μ €μž₯
  • conftest.py: 곡용 λ¦¬μ†ŒμŠ€ Fixture μ •μ˜, Transformer ν…ŒμŠ€νŠΈλ₯Ό μ§€μ›ν•˜λŠ” TransformerHarness 제곡
  • results/: 각 ν…ŒμŠ€νŠΈμ˜ μ‹€ν–‰ κ²°κ³Όκ°€ μ €μž₯λ˜λŠ” 경둜. Git 버전 κ΄€λ¦¬μ—μ„œλŠ” μ œμ™Έλœλ‹€.

ν…ŒμŠ€νŠΈμ™€ κ΄€λ ¨λœ src/linkmerce/tests/ κ²½λ‘œμ— λŒ€ν•œ 상세 μ„€λͺ…은 λ³„λ„μ˜ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•œλ‹€.

Airflow μ„œλΉ„μŠ€ ꡬ쑰

Airflow μ‹œμŠ€ν…œμ„ κ΅¬μ„±ν•˜λŠ” μ„œλΉ„μŠ€ λͺ©λ‘μ€ docker-compose.yaml μ„€μ •μ—μ„œ μ •μ˜ν•œλ‹€. 운영 ν™˜κ²½μ—μ„œ Airflow Celery Executor ꡬ성을 κΈ°μ€€μœΌλ‘œ λ‹€μŒ μ„œλΉ„μŠ€λ“€μ΄ μ‹€ν–‰λ˜κ³  μžˆλ‹€.

  • postgres
  • redis
  • playwright
  • airflow-apiserver
  • airflow-scheduler
  • airflow-dag-processor
  • airflow-worker
  • airflow-triggerer
  • airflow-init

λ‚˜λ¨Έμ§€λŠ” Apache Airflowμ—μ„œ 기본으둜 μ •μ˜ν•œ μ„œλΉ„μŠ€λ“€μ΄μ§€λ§Œ, playwright μ„œλΉ„μŠ€λ₯Ό μ˜ˆμ™Έμ μœΌλ‘œ μΆ”κ°€ν–ˆλ‹€. 일뢀 μž‘μ—…μ—μ„œλŠ” playwright μ„œλΉ„μŠ€λ₯Ό ν™œμš©ν•΄ λΈŒλΌμš°μ € λ Œλ”λ§μ„ ν™œμš©ν•œ μ›Ή μŠ€ν¬λž˜ν•‘μ„ μˆ˜ν–‰ν•œλ‹€.

Docker Compose 싀행은 λ‹€μŒ λͺ…λ Ήμ–΄ λ˜λŠ” init.sh 슀크립트λ₯Ό μ‹€ν–‰ν•œλ‹€.

cd airflow
docker compose up airflow-init
docker compose up -d

Airflow μž‘μ—… μŠ€μΌ€μ€„λ§

linkmerce νŒ¨ν‚€μ§€λ₯Ό ν™œμš©ν•˜λŠ” Airflow Dag은 κ³΅ν†΅μ μœΌλ‘œ λ‹€μŒ 흐름을 κ°€μ§„λ‹€.

  1. airflow_utils ν”ŒλŸ¬κ·ΈμΈμ˜ read_config() λ˜λŠ” read_credentials() ν•¨μˆ˜λ‘œ μ„€μ •κ³Ό 인증 정보λ₯Ό λΆˆλŸ¬μ˜¨λ‹€.
  2. linkmerce.api.* ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•΄ μΆ”μΆœ(extract)κ³Ό λ³€ν™˜(transform) 과정을 μˆ˜ν–‰ν•œλ‹€.
  3. DuckDBConnection을 톡해 ν…Œμ΄λΈ”μ— 적재된 API μ‹€ν–‰ κ²°κ³Όλ₯Ό 뢈러올 수 μžˆλ‹€.
  4. dual_load ν”ŒλŸ¬κ·ΈμΈμœΌλ‘œ DuckDB ν…Œμ΄λΈ”μ„ PostgreSQL에 λ¨Όμ € μ μž¬ν•œ λ’€ BigQuery에도 μ μž¬ν•œλ‹€.
  5. Task κ²°κ³Όλ₯Ό {params: {...}, results: {...}} λ”•μ…”λ„ˆλ¦¬λ‘œ λ°˜ν™˜ν•œλ‹€.

μ΄λŸ¬ν•œ 흐름을 Dag으둜 κ΅¬ν˜„ν•  경우 λ‹€μŒκ³Ό 같은 μ½”λ“œλ‘œ λ‚˜νƒ€λ‚Ό 수 μžˆλ‹€.

with DAG(dag_id="...") as dag:

    PATH = "platform.hostname.category"

    @task(task_id="...")
    def read_configs() -> dict:
        from airflow_utils import read_config
        return read_config(PATH, tables=True)

    @task(task_id="...")
    def read_credentials() -> list:
        from airflow_utils import read_config
        return read_config(PATH, credentials=True)["credentials"]

    @task(task_id="...", map_index_template="{{ credentials['id'] }}")
    def etl_task(credentials: dict, configs: dict, **kwargs) -> dict:
        return main(**credentials, **configs)

    def main(tables: dict[str, str], **kwargs) -> dict:
        from linkmerce.common.load import DuckDBConnection
        from linkmerce.api.platform.hostname import example_api
        from dual_load import load_table_from_duckdb

        with DuckDBConnection(tzinfo="Asia/Seoul") as conn:
            example_api(**kwargs, connection=conn)

            return {
                "params": {},
                "results": {
                    tables["table"]: load_table_from_duckdb(
                        connection = conn,
                        source_table = "table",
                        target_table = tables["table"],
                    ),
                },
            }

    (etl_task
    .partial(configs=read_configs())
    .expand(credentials=read_credentials()))

Airflow와 κ΄€λ ¨λœ airflow/ κ²½λ‘œμ— λŒ€ν•œ 상세 μ„€λͺ…은 λ³„λ„μ˜ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•œλ‹€.

PostgreSQL 적재 ν™˜κ²½

postgres/ κ²½λ‘œλŠ” Airflow MetaDB와 λ³„κ°œλ‘œ ETL κ²°κ³Όλ₯Ό μ μž¬ν•˜κΈ° μœ„ν•œ 둜컬 PostgreSQL 18 ν™˜κ²½μ„ μ œκ³΅ν•œλ‹€.

ꡬ성 μš”μ†ŒλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • Dockerfile: PostgreSQL 18, pg_partman, Apache Arrow / Parquet λŸ°νƒ€μž„, 자체 parquet_io ν™•μž₯을 ν¬ν•¨ν•œ 이미지 λΉŒλ“œ
  • init.sql: ν”Œλž«νΌλ³„ μŠ€ν‚€λ§ˆμ™€ ν…Œμ΄λΈ”, 일별 νŒŒν‹°μ…˜ μ΄ˆκΈ°ν™”
  • partman_maintenance.sql: 운영 쀑 미래의 νŒŒν‹°μ…˜μ„ μƒμ„±ν•˜κΈ° μœ„ν•œ μœ μ§€λ³΄μˆ˜ SQL
  • resources/bq_schemas.json: PostgreSQL init.sql κΈ°μ€€μœΌλ‘œ μƒμ„±ν•œ BigQuery μŠ€ν‚€λ§ˆ μ°Έκ³  파일
  • resources/parquet_io.md: parquet_io ν™•μž₯의 SQL μΈν„°νŽ˜μ΄μŠ€μ™€ νƒ€μž… λ³€ν™˜ μ •μ±… μ„€λͺ…

Airflow Dag은 λŒ€λΆ€λΆ„ dual_load ν”ŒλŸ¬κ·ΈμΈμ„ 톡해 PostgreSQL 적재λ₯Ό λ¨Όμ € μˆ˜ν–‰ν•˜κ³ , μ„±κ³΅ν•˜λ©΄ BigQuery 적재λ₯Ό μ΄μ–΄μ„œ μˆ˜ν–‰ν•œλ‹€. PostgreSQL은 더 μ—„κ²©ν•œ 기본킀와 νƒ€μž… μ œμ•½μ„ κ²€μ¦ν•˜λŠ” 1μ°¨ 적재 λŒ€μƒμœΌλ‘œ μ‚¬μš©ν•˜κ³ , BigQueryλŠ” κΈ°μ‘΄ 뢄석 ν…Œμ΄λΈ” 적재 λŒ€μƒμœΌλ‘œ μœ μ§€ν•œλ‹€.

PostgreSQL μ‹€ν–‰ ν™˜κ²½μ— λŒ€ν•œ 상세 μ„€λͺ…은 λ³„λ„μ˜ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•œλ‹€.

Airflow Trigger

airflow_trigger/ κ²½λ‘œλŠ” Airflow REST APIλ₯Ό ν˜ΈμΆœν•˜μ—¬ Dag을 μˆ˜λ™μœΌλ‘œ μ‹€ν–‰ν•˜κΈ° μœ„ν•œ 보쑰 UIλ₯Ό μ œκ³΅ν•œλ‹€. Airflow UI에 직접 μ ‘κ·Όν•˜μ§€ μ•ŠλŠ” 운영 λ‹΄λ‹Ήμžλ‚˜ Google Sheets 기반 μž‘μ—…μ—μ„œ ν•„μš”ν•œ Dag만 μ•ˆμ „ν•˜κ²Œ μ‹€ν–‰ν•˜λ„λ‘ λΆ„λ¦¬ν•œ 도ꡬ닀.

FastAPI

airflow_trigger/fastapi/app.pyλŠ” 둜컬 λ„€νŠΈμ›Œν¬μ˜ λ‹€λ₯Έ μ»΄ν“¨ν„°λ‚˜ Google Sheets 링크/λ²„νŠΌμ—μ„œ νŠΉμ • Dag을 μ‹€ν–‰ν•˜κΈ° μœ„ν•œ μž‘μ€ HTTP μ„œλ²„λ‹€. λΈŒλΌμš°μ €μ—μ„œ /trigger?dag_id=... μ£Όμ†Œλ‘œ μ ‘μ†ν•˜λ©΄ λ‘œλ”© 화면을 ν‘œμ‹œν•˜κ³ , Dag run이 μ’…λ£Œλ˜λ©΄ μ•ˆλ‚΄ νŒμ—…μ„ 보여쀀 λ’€ νŽ˜μ΄μ§€ μ’…λ£Œλ₯Ό μ‹œλ„ν•œλ‹€.

화면에 λ…ΈμΆœν•  μž‘μ—…λͺ…은 dag_name νŒŒλΌλ―Έν„°λ‘œ μ§€μ •ν•  수 있으며, μ‹€μ œ μ‹€ν–‰ λŒ€μƒμ€ 항상 dag_id둜 κ²°μ •ν•œλ‹€.

http://localhost:16160/trigger?dag_id=gsheets_opex&dag_name=μš΄μ˜λΉ„μš©%20μ—…λ°μ΄νŠΈ

ν”„λ‘œκ·Έλž¨μ—μ„œ JSON 응닡이 ν•„μš”ν•˜λ©΄ /api/triggerλ₯Ό μ‚¬μš©ν•œλ‹€. ν™˜κ²½λ³€μˆ˜, ν—ˆμš© DAG λͺ©λ‘, Docker μ‹€ν–‰ 방법은 λ³„λ„μ˜ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•œλ‹€.

cd airflow_trigger/fastapi
docker compose up -d --build

Streamlit

airflow_trigger/streamlit/app.pyλŠ” μž‘μ—… μΌμ‹œ λ²”μœ„μ™€ 차수λ₯Ό μ„ νƒν•΄μ„œ sabangnet_order Dag run을 μƒμ„±ν•˜λŠ” 운영용 UIλ‹€. Streamlit secretsμ—μ„œ Airflow 접속 정보λ₯Ό 읽고, /auth/token으둜 access token을 λ°œκΈ‰λ°›μ€ λ’€ /api/v2/dags/{dag_id}/dagRuns μ—”λ“œν¬μΈνŠΈλ₯Ό ν˜ΈμΆœν•œλ‹€.

같은 logical_date의 κΈ°μ‘΄ Dag run이 있으면 μ‚­μ œν•œ λ’€ μƒˆλ‘œ μ‹€ν–‰ν•˜λ―€λ‘œ, 같은 쑰건의 μˆ˜λ™ 싀행을 λ‹€μ‹œ μš”μ²­ν•  수 μžˆλ‹€. Docker μ‹€ν–‰, secrets μ„€μ •, μž…λ ₯κ°’κ³Ό Dag run 생성 κ·œμΉ™μ€ λ³„λ„μ˜ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•œλ‹€.

cd airflow_trigger/streamlit
docker compose up -d --build

λΉ λ₯Έ μ‹œμž‘

1. νŒ¨ν‚€μ§€ 개발 ν™˜κ²½

pip install -e .

νŒ¨ν‚€μ§€ λ©”νƒ€λ°μ΄ν„°λŠ” pyproject.toml에 μ •μ˜λ˜μ–΄ μžˆλ‹€.

2. PostgreSQL 둜컬 μ‹€ν–‰

cd postgres
./build.sh
docker compose up -d

3. Airflow 둜컬 μ‹€ν–‰

cd airflow
docker compose up airflow-init
docker compose up -d

Airflowμ—μ„œ dual loadλ₯Ό μ‹€ν–‰ν•˜λ €λ©΄ postgres, bigquery Airflow Connection을 λ¨Όμ € 등둝해야 ν•œλ‹€.

4. ν…ŒμŠ€νŠΈ μ‹€ν–‰

pytest src/tests/test_extract.py -m extract -v -s
pytest src/tests/test_transform.py -m transform -v -s
pytest src/tests/test_load.py -m load -v -s

μ˜ˆμ‹œ μ½”λ“œ

from linkmerce.api.smartstore.api import marketing_channel
from linkmerce.common.load import DuckDBConnection

with DuckDBConnection(tzinfo="Asia/Seoul") as conn:
    rows = marketing_channel(
        client_id="...",
        client_secret="...",
        channel_seq="...",
        start_date="2025-01-01",
        end_date="2025-01-31",
        connection=conn,
        return_type="json",
    )

print(len(rows))

이 ν˜ΈμΆœμ€ API ν•¨μˆ˜λ₯Ό 톡해 Extractor와 DuckDBTransformerλ₯Ό μ—°μ‡„μ μœΌλ‘œ μ‹€ν–‰ν•˜κ³ ,
κ²°κ³Όλ₯Ό DuckDB ν…Œμ΄λΈ”μ— μ μž¬ν•œ λ’€, ν…Œμ΄λΈ” 행을 μ‘°νšŒν•˜μ—¬ list[dict] ν˜•μ‹μœΌλ‘œ λ°˜ν™˜ν•œλ‹€.

About

E-commerce API integration management

Topics

Resources

License

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors