봇 자동 생성 콘텐츠를 CMS에 이중 기록하는 구조 도입
목차
어드민 DB dual-write 구조를 붙이면서 CMS 포스트 UPSERT 흐름을 정리한 작업이다.
왜 dual-write가 필요했나
봇이 콘텐츠를 자동 생성해서 내보내는 구조에서, 생성 결과물이 어디에 기록되는지가 운영 관점에서 꽤 중요한 문제다. 기존에는 봇이 생성 → 배포 채널로 직접 밀어넣는 단방향이었는데, 이렇게만 두면 나중에 "이거 언제 생성됐어?", "이 포스트 내용 다시 볼 수 있어?" 같은 요청이 왔을 때 추적이 안 된다.
운영자 입장에서 CMS는 진짜 레코드의 원천이 되어야 하고, 봇이 뭔가를 자동 생성했다면 그 결과물도 CMS에 반드시 찍혀 있어야 한다는 게 이번 작업의 전제였다. dual-write라는 표현을 쓴 이유는, 봇이 기존 배포 흐름을 유지하면서 동시에 admin_db에도 레코드를 남기는 구조이기 때문이다.
작업 내용
변경된 파일은 세 개다.
| 파일 | 역할 | 이번 변경 의미 |
|---|---|---|
bot/admin_db.py |
어드민 DB 커넥션 / 쿼리 레이어 | UPSERT 쿼리 및 연결 로직 신규 추가 |
bot/generate.py |
콘텐츠 생성 메인 로직 | 생성 완료 후 admin_db write 호출 삽입 |
bot/requirements.txt |
의존성 | DB 드라이버 등 신규 패키지 추가 |
admin_db.py를 별도 모듈로 뺀 건 의도적인 선택이다. generate.py에 DB 로직을 인라인으로 때려박으면 나중에 테스트하기도 불편하고, 다른 봇 모듈에서 같은 DB 레이어를 재사용할 때 복붙이 생긴다. 분리해두면 generate.py는 생성 책임만 가져가고, DB 기록은 admin_db.py가 담당하는 구조가 된다.
UPSERT를 선택한 이유도 짚고 넘어갈 필요가 있다. INSERT만 하면 봇이 재실행됐을 때(재시도, 스케줄 중복 등) 동일 포스트 키로 중복 레코드가 쌓인다. DELETE + INSERT는 히스토리 날아가는 문제가 있다. 결국 UPSERT가 가장 무난한 선택이고, cms_post 테이블 기준으로 포스트 식별 키가 이미 있으면 갱신, 없으면 삽입하는 흐름으로 정리했다.
# admin_db.py — UPSERT 패턴 (개념 예시)
def upsert_cms_post(conn, post_data: dict):
sql = """
INSERT INTO cms_post (post_key, title, body, generated_at)
VALUES (%(post_key)s, %(title)s, %(body)s, %(generated_at)s)
ON CONFLICT (post_key)
DO UPDATE SET
title = EXCLUDED.title,
body = EXCLUDED.body,
generated_at = EXCLUDED.generated_at
"""
with conn.cursor() as cur:
cur.execute(sql, post_data)
conn.commit()
generate.py에서는 생성 로직이 끝난 직후 이 함수를 호출하는 식으로 연결했다. 생성 실패 시에는 write도 하면 안 되기 때문에, 생성 성공 확인 이후 write 순서를 명확히 지키는 게 포인트였다.
팀 관점에서 이 변경이 갖는 의미
dual-write 구조를 붙이는 건 단순한 기능 추가가 아니다. 이제 봇이 DB 의존성을 갖게 되었기 때문에, 배포 환경에서 DB 연결 설정 / 시크릿 관리 / 연결 실패 시 fallback 여부 등을 팀이 함께 인지해야 한다.
특히 이런 지점들을 리뷰에서 짚었다:
- DB write 실패 시 생성 결과 배포 자체를 막을 건지, 아니면 write만 실패 처리하고 배포는 계속할 건지 — 현재는 write 실패가 전체를 막는 구조로 우선 갔다
requirements.txt에 추가된 패키지가 CI/CD 이미지 빌드에 영향을 주는지 — 확인 필요- 로컬 개발 환경에서 admin_db 연결 없이 봇 단독 실행이 가능한지 — 환경변수 미설정 시 graceful하게 skip할 수 있도록 후속 처리 예정
automation 카테고리 작업에서 DB 연동이 들어가는 순간, "봇이 가끔 실패해도 괜찮은 존재"에서 "DB 정합성을 다루는 존재"로 성격이 바뀐다. 이 무게감을 팀 전체가 공유하는 게 코드 변경 자체보다 더 중요했다.
다음은 write 실패 시 재시도 로직과 dead-letter 처리 방향을 정리할 것 같다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.