주제: 매일 밤, 마음의 방향을 찾는 질문 — 위로와 성찰의 카드뉴스 (config.yaml에서 변경 가능)
매일 자동으로
- Gemini(무료 티어)가 아직 다루지 않은 세부 소재를 골라 카드뉴스 텍스트 초안을 생성하고
- 전문가·크리에이티브 디렉터·투자자 세 관점에서 초안을 비판적으로 검증·개선하고
- HTML 템플릿을 카드 이미지(PNG)로 렌더링해 초안 상태로 커밋합니다.
이후 사람이 초안을 검토·수정하고 나서 수동으로 발행 워크플로를 실행해야 실제로 Instagram에 게시됩니다 (완전 자동 발행이 아닙니다 — 아래 5번 참고).
GitHub Actions에서 스케줄 실행되므로 개인 PC를 켜둘 필요가 없습니다.
발행 주기, 카테고리 체계, 1개월차 주제 리스트, 3~6개월 성장 프레임은 CONTENT_STRATEGY.md 참고.
[매일 자동 — Generate Card News Draft 워크플로]
├─ scripts/generate_content.py → Gemini API로 카드뉴스 텍스트(JSON) 초안 생성
├─ scripts/review_content.py → 전문가/크리에이티브 디렉터/투자자 관점으로 비판적 검증 후 개선판으로 교체
├─ scripts/render_cards.py → Playwright로 HTML→PNG 카드 이미지 생성 (docs/posts/...)
└─ git commit & push → GitHub Pages가 이미지를 공개 URL로 서빙 (아직 미발행 상태)
[사람이 검토/수정 — GitHub 웹/앱에서 이미지·문구 확인, 필요시 JSON 직접 편집]
[수동 트리거 — Publish Card News to Instagram 워크플로]
├─ scripts/resolve_draft.py → 발행할 초안 결정
├─ scripts/render_cards.py → 수정사항 반영해 재렌더링
├─ scripts/publish_instagram.py → Instagram Graph API로 캐러셀 게시
└─ scripts/mark_published.py → history.json에 발행 완료 + media_id 표시
[매일/매주 자동 — 성과 분석 루프, 7번 섹션 참고]
fetch_insights.py(매일) → learn_insights.py(매주) → 다음 generate_content.py 호출에 반영
콘텐츠 이력은 content/history.json에 카테고리별로 누적되어, 다음 생성 시 모델에게 "카테고리별 누적 발행 건수 + 이미 다룬 세부 소재 목록"으로 전달됩니다. 이를 바탕으로 (1) 아직 적게 다룬 카테고리를 우선 고려하고, (2) 이미 많이 다룬(포화) 카테고리를 다시 다뤄야 할 경우 기존 게시물에 없던 새로운 각도나 놓치기 쉬운 주의사항·실전 팁을 반드시 추가하도록 지시해, 콘텐츠가 쌓일수록 단순 재탕이 아닌 실질적으로 다른 가치를 갖도록 합니다. 발행된 게시물의 실제 반응(좋아요/저장/공유)도 자동으로 수집·분석되어 다음 콘텐츠 생성에 반영됩니다 (7번 섹션 참고) — Notion 연동(선택, 6번 섹션)까지 설정하면 매일의 콘텐츠를 Notion에서도 폴더처럼 정리된 형태로 볼 수 있습니다.
Graph API로 자동 게시하려면 Instagram 비즈니스(또는 크리에이터) 계정 + 연결된 Facebook 페이지 + Meta 개발자 앱 + Access Token이 필요합니다. 순서대로 진행하세요.
- 인스타그램 앱 → 설정 → 계정 유형 전환 → "비즈니스 계정" (또는 "크리에이터 계정") 선택
- Facebook 페이지가 없다면 새로 하나 만드세요 (개인 프로필과 별개).
- 인스타그램 앱 → 설정 → 계정 센터(Accounts Center) → 연결된 계정에서 방금 만든 Facebook 페이지와 연결합니다.
- https://developers.facebook.com/apps 에서 "앱 만들기"
- 앱 유형: Business 선택
- 생성된 앱의 대시보드에서 제품 추가 → Instagram Graph API 추가 (또는 "Instagram" 제품)
- https://developers.facebook.com/tools/explorer 접속
- 우측 상단에서 방금 만든 앱 선택
- "User or Page" → User Token 선택 후 아래 권한(permission) 체크:
instagram_basicinstagram_content_publishinstagram_manage_insights(성과 지표 자동 수집용 — 7번 섹션 참고. 당장 안 쓸 거면 생략 가능)pages_show_listpages_read_engagementbusiness_management
- "Generate Access Token" 클릭 → 로그인/권한 승인
- 발급된 토큰을 복사 (일단 단기 토큰, 1~2시간 유효)
터미널에서 (앱 ID/시크릿은 앱 대시보드 > 설정 > 기본 설정에서 확인):
curl -X GET "https://graph.facebook.com/v21.0/oauth/access_token?grant_type=fb_exchange_token&client_id={앱ID}&client_secret={앱시크릿}&fb_exchange_token={2-4에서받은토큰}"응답의 access_token이 60일짜리 장기 토큰입니다. 이것을 IG_ACCESS_TOKEN으로 사용합니다.
권장: 60일마다 수동 갱신하기 번거로우면, Business Manager(business.facebook.com)에서 System User를 만들고 System User 토큰을 발급하면 만료 없이 사용할 수 있습니다 (Business Manager > 설정 > 사용자 > 시스템 사용자 > 토큰 생성, 동일한 권한 부여). 장기 운영 시 이 방식을 강력히 권장합니다.
# 1) 내 계정에 연결된 페이지 목록
curl "https://graph.facebook.com/v21.0/me/accounts?access_token={장기토큰}"
# 2) 위에서 얻은 page id로 연결된 IG 비즈니스 계정 ID 조회
curl "https://graph.facebook.com/v21.0/{page-id}?fields=instagram_business_account&access_token={장기토큰}"instagram_business_account.id 값이 IG_USER_ID입니다.
- 앱이 "개발 모드"여도 본인이 관리자/개발자/테스터로 등록된 계정에는 심사 없이 게시할 수 있습니다. 자기 계정에만 쓸 계획이라면 지금 단계로 충분합니다.
- 만약 이 에이전트로 다른 사람 소유의 인스타그램 계정에 게시하려면
instagram_content_publish에 대한 Advanced Access 심사를 통과해야 합니다.
- 이 폴더를 GitHub 새 저장소로 push 하세요.
git init git add . git commit -m "init: instagram cardnews agent" git branch -M main git remote add origin https://github.com/<your-username>/<repo-name>.git git push -u origin main
- GitHub Pages 활성화: 저장소 Settings → Pages → Source를 "Deploy from a branch"로, Branch를
main/docs로 설정 → 저장.- 몇 분 후
https://<your-username>.github.io/<repo-name>/가 열리는지 확인하세요.
- 몇 분 후
config.yaml의github_pages.base_url을 위에서 확인한 실제 URL로 수정 후 커밋/푸시.- Secrets 등록: 저장소 Settings → Secrets and variables → Actions → New repository secret
GEMINI_API_KEY(Google Gemini API 키, 무료 티어 — aistudio.google.com/apikey 에서 발급)IG_USER_IDIG_ACCESS_TOKEN(성과 지표 조회까지 하려면 이 토큰에instagram_manage_insights권한이 포함되어 있어야 합니다 — 2-4 참고)CARDNEWSAGENTNOTION(선택 — Notion 연동을 쓸 경우만, 6번 섹션 참고)
python -m venv .venv
.venv\Scripts\activate # Windows
pip install -r requirements.txt
playwright install chromium
copy .env.example .env # 값 채워넣기 (GEMINI_API_KEY만 있으면 dry-run 가능)
python scripts\main.py --dry-rundocs/posts/<날짜>_<slug>/ 폴더에 카드 이미지 PNG들이 생성되고, IG 실제 게시 없이 콘솔에 이미지 URL과 캡션만 출력됩니다. 이미지가 마음에 드는지 직접 열어서 확인하세요.
파이프라인은 두 개의 워크플로로 나뉘어 있습니다. 초안 생성은 매일 자동으로 실행되지만, 실제 인스타그램 발행은 항상 사람이 수동으로 트리거해야만 일어납니다.
Generate Card News Draft— 매일 정해진 시각(기본 21:00 KST)에 자동 실행되어 콘텐츠 JSON을 생성하고, 전문가·크리에이티브 디렉터·투자자 세 관점의 비판적 검증을 거쳐 개선판으로 교체한 뒤, 카드 이미지를 렌더링해docs/posts/<post_id>_<slug>/에 커밋·푸시합니다. Instagram에는 아직 게시되지 않습니다.- 무엇이 지적되고 어떻게 고쳐졌는지는
content/generated/<post_id>_<slug>.review.json에서 확인할 수 있습니다. - 수동으로 즉시 실행하려면: Actions 탭 → "Generate Card News Draft" → "Run workflow"
- 무엇이 지적되고 어떻게 고쳐졌는지는
- 초안 검토/수정 — PC 없이도 휴대폰 GitHub 앱(또는 웹)에서 확인 가능합니다.
- 이미지 확인: 저장소의
docs/posts/<post_id>_<slug>/폴더를 열면 슬라이드 PNG가 바로 미리보기됩니다. (또는 GitHub Pages URL로 직접 접속) - 문구 수정:
content/generated/<post_id>_<slug>.json파일을 GitHub 웹/앱에서 직접 열어 편집(✏️) 후 커밋하면 됩니다. (slides[].title/body,cover,outro,caption,hashtags등)
- 이미지 확인: 저장소의
Publish Card News to Instagram— 검토가 끝나면 Actions 탭 → "Publish Card News to Instagram" → "Run workflow"를 수동으로 실행합니다.post_id를 비워두면 가장 최근에 생성된, 아직 발행되지 않은 초안을 자동으로 찾아 발행합니다. 특정 날짜의 초안을 지정하려면content/history.json에서post_id값을 확인해 입력하세요.- 이 워크플로는 발행 직전에 콘텐츠 JSON을 다시 렌더링하므로, 2번에서 문구를 수정했다면 그 내용이 이미지에 반영된 채로 게시됩니다.
- 먼저
dry_run을true로 실행해 로그에서 이미지 URL/캡션이 의도대로 나오는지 확인한 뒤,dry_run을false로 다시 실행하면 실제로 인스타그램에 게시됩니다. - 발행이 끝나면 해당 초안은
content/history.json에published: true로 표시되어, 다음번에post_id를 비워두고 실행해도 중복 발행되지 않습니다.
생성되는 카드뉴스를 매일 Notion 데이터베이스에도 하루 단위 페이지로 자동 기록합니다 (초안 생성 시 페이지 생성, 발행 완료 시 상태 갱신). PC/앱 어디서든 캘린더·갤러리 뷰로 콘텐츠 이력을 한눈에 보고 싶을 때 유용합니다. 설정하지 않아도 나머지 파이프라인은 정상 동작합니다 (건너뜁니다).
-
통합(integration) 만들기: https://www.notion.so/my-integrations → "New integration" → 이름 입력(예: "카드뉴스 봇") → 연결할 워크스페이스 선택 → 생성.
-
생성된 통합의 **"Internal Integration Secret"**을 복사합니다. 이 값을 저장소 Secrets에 **
CARDNEWSAGENTNOTION**이라는 이름으로 등록하세요 (지난번 API 키들과 동일하게, GitHub 웹사이트에서 직접 등록 — 절대 대화창에 붙여넣지 마세요). -
아무 페이지나 만들어서 연결: Notion에서 빈 페이지를 하나 만듭니다 (데이터베이스일 필요 없음 — 그냥 일반 페이지). 우측 상단
⋯메뉴 → "연결 추가(Add connections)" → 1번에서 만든 통합을 선택합니다. (이 단계를 빠뜨리면 API가 403 오류를 반환합니다.) -
자동 설정 실행: Actions 탭 → "Setup & Clean Notion Database" → "Run workflow".
scripts/setup_notion_db.py가:- 통합에 이미 연결된 데이터베이스가 있으면 그것을 사용하고,
- 없으면 3번에서 연결한 페이지 안에 "카드뉴스 콘텐츠"라는 데이터베이스를 필요한 속성을 모두 갖춰서 자동으로 새로 만들고 (제목/날짜/카테고리/상태/post_id/소재/슬라이드수/해시태그/GitHub Pages 링크/발행일시/media_id/좋아요/댓글/저장/공유/도달/참여점수),
- 찾았거나 새로 만든 데이터베이스의 id를
config.yaml의notion.database_id에 자동으로 기록해 커밋합니다.
즉 데이터베이스나 속성을 직접 만들 필요 없이, 빈 페이지 하나 연결하고 이 워크플로 한 번 실행하면 끝입니다. 몇 번을 다시 실행해도 안전합니다 (이미 있는 데이터베이스/속성은 건드리지 않음). 같은 워크플로가
content/history.json에 더 이상 없는(주제 전환 등으로 정리된) 옛 초안의 Notion 페이지도 자동으로 보관 처리(archive)합니다.
이후 초안이 생성될 때마다 Notion에 새 페이지가 만들어지고, 슬라이드 이미지·소재·해시태그·GitHub Pages 링크가 함께 기록됩니다. 발행이 완료되면 "상태"가 "발행 완료"로 바뀌고 발행일시·media_id가 채워지며, 매일 성과 지표가 수집될 때마다 좋아요/댓글/저장/공유/도달/참여점수도 함께 갱신됩니다 — 데이터베이스 뷰에서 참여점수 기준으로 정렬하거나 카테고리별로 필터링해 어떤 콘텐츠가 잘 되는지 바로 확인할 수 있습니다.
무료 플랜 한계? 개인(1인) Notion 워크스페이스는 2022년부터 페이지·블록 수 제한이 없습니다 (2인 이상 팀 워크스페이스에만 1,000블록 제한 적용). 매일 콘텐츠가 쌓여도 문제없습니다.
발행된 게시물의 실제 반응(좋아요/댓글/저장/공유)을 자동으로 수집하고, 어떤 콘텐츠가 왜 반응이 좋았는지 분석해 다음 콘텐츠 생성에 반영합니다. 별도의 외부 에이전트 없이 이 저장소의 GitHub Actions만으로 동작합니다.
[Fetch Instagram Insights — 매일 자동]
scripts/fetch_insights.py → 발행 후 48시간 이상 지난 게시물의 성과 지표를
Graph API에서 조회해 history.json의 "performance" 필드에 기록
[Learn From Content Performance — 매주 자동]
scripts/learn_insights.py → 성과 데이터가 있는 게시물들을 저장·공유 가중치
기반 점수로 정렬해 Gemini에게 분석시키고, "숫자가 커버 제목에 들어간 글의
저장률이 높았다" 같은 구체적 패턴을 content/insights.json에 기록
[Generate Card News Draft — 매일 자동]
scripts/generate_content.py → content/insights.json이 있으면 프롬프트에
포함해, 다음 콘텐츠가 과거에 효과가 검증된 패턴을 반영하도록 함
- 최소 3건 이상 성과 데이터가 쌓이기 전까지
learn_insights.py는 분석을 건너뜁니다 (데이터 부족 상태에서 억지로 패턴을 만들어내지 않도록). - 무엇이 학습됐는지는
content/insights.json에서 직접 확인할 수 있습니다. - 성과 조회에는
IG_ACCESS_TOKEN에instagram_manage_insights권한이 필요합니다 (2-4 참고). 권한이 없으면fetch_insights.py가 해당 게시물만 건너뛰고 경고를 남깁니다 (파이프라인 전체가 멈추지 않음). - 수동으로 즉시 실행하려면: Actions 탭 → "Fetch Instagram Insights" 또는 "Learn From Content Performance" → "Run workflow"
- 주제/카테고리:
config.yaml의topic_theme,category_pool수정 - 디자인:
config.yaml의brand(색상),templates/card.html.jinja(레이아웃) - 슬라이드 수:
config.yaml의slides.min/slides.max - 초안 생성 시각:
.github/workflows/generate-draft.yml의cron값 (UTC 기준, KST = UTC+9). 실제 발행 시각은 스케줄이 아니라 사용자가 "Publish Card News to Instagram" 워크플로를 수동 실행하는 시점입니다. - 다른 주제로 완전 교체:
topic_theme,audience,category_pool만 바꾸면 나머지 파이프라인은 그대로 재사용됩니다.
- 콘텐츠 성격에 대한 주의: 이 계정은 심리 상담이나 치료가 아닙니다. 진단·처방하는 어투, 특정 질환/증상 언급, 전문 치료를 대체할 수 있다는 인상을 주지 않도록 프롬프트를 설계했습니다. 그래도 정기적으로 실제 게시물을 직접 검수하는 것을 권장합니다.
- 토큰 만료: 장기 토큰(60일) 방식이면 만료 전 갱신 필요. System User 토큰 사용을 권장합니다 (2-5 참고).
- 게시 빈도: Instagram Graph API는 24시간당 최대 25건 게시 제한이 있습니다. 현재 이틀에 1건(안정화 후 매일 1건으로 전환 예정) 스케줄이면 문제없습니다.
- Meta 정책 준수: 스팸성/오해 소지 콘텐츠, 저작권 침해 이미지 사용 금지. 이 템플릿은 순수 코드 생성 이미지만 사용합니다.
- 첫 실행 전 반드시 dry-run으로 이미지/캡션 품질을 확인하세요.
instagram-cardnews-agent/
├── config.yaml # 주제, 브랜드, 스케줄 등 전체 설정
├── .env.example # 로컬 테스트용 환경변수 템플릿
├── requirements.txt
├── content/
│ ├── history.json # 이미 다룬 소재 이력 + 카테고리/발행/성과(performance) 추적
│ ├── insights.json # learn_insights.py가 성과 데이터에서 뽑아낸 학습 인사이트
│ └── generated/ # 생성된 카드뉴스 텍스트(JSON) 아카이브 — 발행 전 여기서 직접 수정 가능
│ └── <id>.review.json # 전문가/크리에이티브 디렉터/투자자 검토 노트 (review_content.py 생성)
├── templates/
│ └── card.html.jinja # 카드 슬라이드 HTML 템플릿
├── scripts/
│ ├── generate_content.py # Gemini로 텍스트 초안 생성 (무료 티어)
│ ├── review_content.py # 전문가/크리에이티브 디렉터/투자자 관점 교차 검증 후 개선판으로 교체
│ ├── render_cards.py # HTML→PNG 렌더링
│ ├── resolve_draft.py # 발행할 초안(content JSON) 결정
│ ├── mark_published.py # history.json에 발행 완료 + 발행시각/media_id 표시
│ ├── publish_instagram.py # IG Graph API 발행
│ ├── fetch_insights.py # 발행된 게시물의 성과 지표(좋아요/저장/공유 등) 수집
│ ├── learn_insights.py # 성과 데이터 분석 → content/insights.json 갱신
│ ├── sync_notion.py # Notion 데이터베이스에 초안/발행/성과 동기화 (선택)
│ ├── setup_notion_db.py # Notion DB 자동 탐색 + 스키마 확장 (최초 1회 실행용)
│ └── main.py # 로컬 테스트용 오케스트레이터 (생성→검증→렌더링→발행 전체 실행)
├── docs/ # GitHub Pages 루트 (이미지 공개 호스팅)
│ └── posts/<post_id>_<slug>/ # 슬라이드 PNG + manifest.json
└── .github/workflows/
├── generate-draft.yml # 매일 자동: 초안 생성 + 검증 + 렌더링 + Notion 동기화 (발행 안 함)
├── publish.yml # 수동 트리거 전용: 재렌더링 + 실제 IG 발행 + Notion 상태 갱신
├── fetch-insights.yml # 매일 자동: 발행된 게시물 성과 지표 수집 + Notion 동기화
├── learn-insights.yml # 매주 자동: 성과 데이터 분석 → 인사이트 갱신
└── setup-notion.yml # 수동 트리거 전용(최초 1회): Notion DB 자동 탐색 + 스키마 확장