μ—μ–΄ν”Œλ‘œμš° 없이, λŠκ²¨λ„ μžλ™μœΌλ‘œ μ΄μ–΄λ°›λŠ” μ‹ λ’°μ„± 높은 ETL을 μ„€κ³„ν•œ 이야기

1. λ§Œλ“€κ²Œ 된 이유

κ΅­λ¦½μ€‘μ•™λ„μ„œκ΄€(NLK) 은 μ„œμ§€μ •λ³΄APIλ₯Ό 톡해 ν•œκ΅­μ— λ“±λ‘λœ λͺ¨λ“  λ„μ„œμ˜ μ„œμ§€ 데이터λ₯Ό κ³΅κ°œν•˜κ³  μžˆλ‹€.

λ‚˜λŠ” 2000λ…„ 1μ›”λΆ€ν„° 2024λ…„ 12μ›”κΉŒμ§€, 즉 25λ…„μΉ˜ 전체 데이터λ₯Ό PostgreSQL(Supabase) 에 μ €μž₯ν•΄ 보고 μ‹Άμ—ˆλ‹€.

μ²˜μŒμ—” λ‹¨μˆœνžˆ β€œAPIλ₯Ό 루프 돌리면 λ˜κ² μ§€β€ μ‹Άμ—ˆλ‹€. ν•˜μ§€λ§Œ μ‹€μ œλ‘œλŠ” μ•„λž˜ λ¬Έμ œλ“€μ„ ν•΄κ²°ν•΄μ•Ό ν–ˆλ‹€.

  • 호좜 μ œν•œ(rate limit)κ³Ό μ—°κ²° λŠκΉ€(timeout)
  • 25λ…„ Γ— 12κ°œμ›” = 300κ°œμ›” λΆ„λŸ‰μ˜ 데이터
  • EC2 연결이 쀑간에 λŠκΈ°λŠ” 문제
  • 수백만 건 JSON의 쀑볡 μ‚½μž… λ°©μ§€

2. NLK API μ΄ν•΄ν•˜κΈ°

μ°Έκ³ : κ΅­λ¦½μ€‘μ•™λ„μ„œκ΄€ μ„œμ§€μ •λ³΄ API λ¬Έμ„œ

  • 인증킀(cert_key) ν•„μš”
  • μš”μ²­λ³€μˆ˜: PUBLISH_PREDATE
  • 일정 수의 page_size = 100 μš”μ²­ κ°€λŠ₯

즉, 전체 데이터λ₯Ό ν•œ λ²ˆμ— κ°€μ Έμ˜¬ 수 μ—†μ—ˆλ‹€. κ·Έλž˜μ„œ β€˜μ›” λ‹¨μœ„ νŒŒν‹°μ…”λ‹β€™ μ „λž΅μ„ μ„Έμ› λ‹€ β€” ν•œ 달씩 λͺ…ν™•ν•œ κΈ°κ°„μœΌλ‘œ λ‚˜λˆ  μˆ˜μ§‘ν•˜κΈ°.

3. 첫 번째 ν”„λ‘œν† νƒ€μž… β€” λ‹¨μˆœν•œ Fetcher

fetch_pages.pyλŠ” μ΄λ ‡κ²Œ λ™μž‘ν–ˆλ‹€.

  • νŽ˜μ΄μ§€λ₯Ό μˆœμ„œλŒ€λ‘œ μš”μ²­
  • JSON을 raw_nl_books ν…Œμ΄λΈ”μ— μ‚½μž…
  • μ½˜μ†”μ— μ§„ν–‰ 둜그 좜λ ₯

μž‘λ™μ€ λμ§€λ§Œ, μ•„λž˜μ™€ 같은 λ¬Έμ œκ°€ μžˆμ—ˆλ‹€.

  • 쀑간에 μ—λŸ¬ λ‚˜λ©΄ μ²˜μŒλΆ€ν„° λ‹€μ‹œ ν•΄μ•Ό 함
  • EC2 μž¬λΆ€νŒ… μ‹œ 진행상황 사라짐
  • 맀달 슀크립트λ₯Ό μˆ˜λ™μœΌλ‘œ μˆ˜μ •ν•΄μ•Ό 함

4. μˆ˜λ™μ—μ„œ μžλ™μœΌλ‘œ

μ›ν•˜λŠ” μ‹œμŠ€ν…œμ€ λ‹€μŒκ³Ό κ°™μ•˜λ‹€.

  1. 2000λ…„λΆ€ν„° 2024λ…„κΉŒμ§€ λͺ¨λ“  달 μžλ™ μˆ˜μ§‘
  2. λ„€νŠΈμ›Œν¬ λŠκΉ€μ—λ„ μž¬μ‹œμž‘
  3. 쀑볡 μ—†λŠ” μ•ˆμ „ν•œ μ‚½μž…
  4. λ©°μΉ κ°„ 무인으둜 μ•ˆμ • μž‘λ™

μ˜€μΌ€μŠ€νŠΈλ ˆμ΄μ…˜ 도ꡬλ₯Ό κ³ λ €ν•΄λ³΄μ•˜λ‹€. 가볍고 λ‹¨λ‹¨ν•œ ꡬ쑰가 ν•„μš”ν–ˆλ‹€.

5. κ³ λ €ν•œ μ˜€μΌ€μŠ€νŠΈλ ˆμ΄μ…˜ μ˜΅μ…˜

μ˜΅μ…˜ μž₯점 단점 선택
Cron μ„€μ • 간단 μž¬μ‹œλ„Β·λ°±μ˜€ν”„ μ—†μŒ, λͺ¨λ‹ˆν„°λ§ 뢈편 ❌
Airflow / Kestra UI, μ™„μ „ν•œ μ›Œν¬ν”Œλ‘œ 관리 단일 μž‘μ—…μ—” 과함 ❌
systemd μžλ™ μž¬μ‹œμž‘, 둜그 관리, μ™ΈλΆ€ μ˜μ‘΄λ„ μ—†μŒ Linux ν•œμ • βœ… 선택

systemd λ₯Ό 선택해닀. λ¦¬λˆ…μŠ€μ— κΈ°λ³Έ λ‚΄μž₯λ˜μ–΄ 있고, λ‹€μŒμ„ λͺ¨λ‘ μ§€μ›ν•œλ‹€.

  • μ‹€νŒ¨ μ‹œ μžλ™ μž¬μ‹œμž‘
  • journalctl 둜그 쑰회
  • μ„œλΉ„μŠ€ μ‹œμž‘Β·μ€‘μ§€ 간단
  • μΆ”κ°€ μ„€μΉ˜λ‚˜ DB μ„€μ • λΆˆν•„μš”

6. 체크포인트 섀계

κ°„λ‹¨ν•œ μƒνƒœ μ €μž₯ 폴더 ν•˜λ‚˜λ₯Ό λ§Œλ“€μ—ˆλ‹€. ~/nlk-state/

각 λ‹¬λ§ˆλ‹€ 두 개의 파일이 생긴닀.

2000-01.page   # λ§ˆμ§€λ§‰μœΌλ‘œ μ²˜λ¦¬ν•œ νŽ˜μ΄μ§€
2000-01.done   # ν•΄λ‹Ή 달 μ™„λ£Œ ν‘œμ‹œ

μ‹œμŠ€ν…œμ΄ 쀑간에 λ©ˆμΆ”λ”λΌλ„ λ‹€μŒ μ‹€ν–‰ μ‹œ .pageλ₯Ό 읽어 λ°”λ‘œ μ΄μ–΄μ„œ μ‹œμž‘ν•œλ‹€.

파일 기반으둜 ν•œ 이유:

  • DB 락(lock) 문제 μ—†μŒ
  • 눈으둜 λ°”λ‘œ 확인 κ°€λŠ₯
  • DB 연결이 μ•ˆ 돼도 μ§„ν–‰ μƒνƒœ μœ μ§€

7. 월별 λŸ¬λ„ˆ 섀계

fetch_pages_month.pyλ₯Ό ν™•μž₯ν•΄ λ‹€μŒ κΈ°λŠ₯을 λ„£μ—ˆλ‹€.

  • μ§€μ •ν•œ μ›” λ²”μœ„(start_publish_date β†’ end_publish_date) μš”μ²­
  • 성곡 μ‹œ .page κ°±μ‹ 
  • νƒ€μž„μ•„μ›ƒ μ‹œ 비정상 μ’…λ£Œ (systemdκ°€ μž¬μ‹œμž‘)
  • rec_hash = md5(source_record::text)둜 쀑볡 λ°©μ§€

이제 각 달이 κΉ”λ”νžˆ μ’…λ£Œλ˜λ©°, κ²°κ³Όκ°€ μ—†μœΌλ©΄ μžλ™μœΌλ‘œ λ‹€μŒ λ‹¬λ‘œ λ„˜μ–΄κ°„λ‹€.

8. 전체 κ΄€λ¦¬μž: run_all_months.py

이 μŠ€ν¬λ¦½νŠΈκ°€ 전체 25λ…„ 루프λ₯Ό λŒλ¦°λ‹€.

2000-01 β†’ 2000-02 β†’ … β†’ 2024-12

각 달에 λŒ€ν•΄:

  • .done 있으면 μŠ€ν‚΅
  • μ—†μœΌλ©΄ fetch_pages_month.py μ‹€ν–‰
  • 성곡 μ‹œ .done 생성
  • μ‹€νŒ¨ μ‹œ μ’…λ£Œ (systemdκ°€ μž¬μ‹œμž‘ν•˜μ—¬ 같은 달뢀터 재개)

μ™œ ν•œ νŒŒμΌμ— λ‹€ λ„£μ§€ μ•Šκ³  β€œλ§€λ‹ˆμ € + λŸ¬λ„ˆβ€λ‘œ λ‚˜λˆ΄λ‚˜? β†’ μ±…μž„ 뢄리가 λͺ…ν™•ν•΄μ§„λ‹€. 월별 μŠ€ν¬λ¦½νŠΈλŠ” APIΒ·DB에 μ§‘μ€‘ν•˜κ³ , λ§€λ‹ˆμ €λŠ” 흐름 μ œμ–΄μ™€ 체크포인트만 λ§‘λŠ”λ‹€.

9. μŠˆνΌλ°”μ΄μ €: systemd

nlk-history.service νŒŒμΌμ€ μ΄λ ‡κ²Œ κ΅¬μ„±ν–ˆλ‹€.

[Service]
User=ec2-user
WorkingDirectory=/home/ec2-user/kbook-data-pipeline
ExecStart=/home/ec2-user/kbook-data-pipeline/venv/bin/python /home/ec2-user/kbook-data-pipeline/scripts/run_all_months.py
Restart=on-failure
RestartSec=30s

μ™œ systemdλ₯Ό μ„ νƒν–ˆλ‚˜?

  • 비정상 μ’…λ£Œ μ‹œ μžλ™ μž¬μ‹œμž‘
  • UIλ‚˜ λ°μ΄ν„°λ² μ΄μŠ€ ν•„μš” μ—†μŒ
  • journalctl둜 둜그 확인 용이
  • μž₯μ‹œκ°„ EC2 μž‘μ—…μ— 졜적

λ§ˆμ§€λ§‰ 달이 λλ‚˜λ©΄ 정상 μ’…λ£Œ μ½”λ“œ(0)둜 μ’…λ£Œλ˜κ³ , systemd도 κΉ”λ”νžˆ μ •μ§€ν•œλ‹€.

10. νŒŒμ΄ν”„λΌμΈ 검증

정상 μž‘λ™ 확인법:

  • .page 파일이 주기적으둜 갱신됨
  • 달이 λλ‚˜λ©΄ .done 생성
  • EC2 μž¬λΆ€νŒ… 후에도 같은 μœ„μΉ˜μ—μ„œ 재개
  • rec_hash둜 쀑볡 μ‚½μž… μ—†μŒ 확인

전체 μ‹€ν–‰ μ‹œκ°„: μ•½ 30λΆ„ Γ— 300κ°œμ›” = μ•½ 6일 연속 μˆ˜μ§‘

11. ν•΄κ²°ν•œ λ¬Έμ œλ“€

문제 ν•΄κ²°μ±…
ID 쀑볡 ALTER SEQUENCE … RESTART둜 identity 리셋
ν–‰ 쀑볡 rec_hash 고유 컬럼 μΆ”κ°€
νƒ€μž„μ•„μ›ƒ λ°±μ˜€ν”„(backoff)와 κΈ΄ read timeout
EC2 λŠκΉ€ systemd둜 관리

12. κ²°κ³Ό

일주일 무인 μ‹€ν–‰ ν›„:

  • βœ… 2000–2024λ…„ 전체 데이터 Postgres μ €μž₯
  • βœ… 쀑볡 μ—†λŠ” μ‚½μž…
  • βœ… λ„€νŠΈμ›Œν¬ 였λ₯˜μ—λ„ μžλ™ 볡ꡬ
  • βœ… μ™„μ „ν•œ 무인 νŒŒμ΄ν”„λΌμΈ

총 μ•½ 600만 건의 λ„μ„œ 기둝을 μ•ˆμ •μ μœΌλ‘œ μˆ˜μ§‘ μ™„λ£Œν–ˆλ‹€.

13. 배운 점

  1. λ‹¨μˆœν•¨μ΄ ν™•μž₯성이닀. β€” ν•œ λ°©ν–₯으둜만 ν˜λŸ¬κ°€λŠ” μž‘μ—…μ—” κ°€λ²Όμš΄ μŠ€ν¬λ¦½νŠΈκ°€ λŒ€ν˜• ν”„λ ˆμž„μ›Œν¬λ³΄λ‹€ λ‚«λ‹€.
  2. λͺ…μ‹œμ  체크포인트 > λ¬΄ν•œ μž¬μ‹œλ„.
  3. systemdλŠ” κ³Όμ†Œν‰κ°€λœ 운영 μžλ™ν™” 도ꡬ닀.
  4. μ²˜μŒλΆ€ν„° β€œλ³΅κ΅¬ κ°€λŠ₯μ„±(resumability)” 을 염두에 둬라. μ΅œμ ν™”λŠ” κ·Έλ‹€μŒμ΄λ‹€.