Backend
home
🚍

Notion 업무일지 자동생성

유형
업무일지

세팅 순서 (3단계)

1. Notion Integration 생성

1.
https://www.notion.so/my-integrations 접속 → + New integration 생성
2.
API Key 복사
3.
Notion 업무일지 최상위 페이지 → ···Connections → Integration 연결

2. GitHub Secrets 등록

Settings → Secrets and variables → Actions → New repository secret
Secret 이름
설명
NOTION_API_KEY
위에서 복사한 API Key
NOTION_PARENT_PAGE_ID
업무일지 최상위 페이지 URL 끝 32자리
NOTION_TEMPLATE_PAGE_ID
복사 기준 날짜 페이지 ID (선택, 비우면 자동탐색)

3. 파일 업로드 후 Push

bash 로 실행
git add notion_weekly_diary.py .github/workflows/notion_weekly_diary.yml git commit -m "feat: Notion 주간 업무일지 자동 생성" git push

Notion Integreation 생성

생성 후 화면
페이지 사용 권한 관리 세팅

Notion 주간 업무일지 자동 생성 자동화

개요

매주 금요일 오후 6시(KST) GitHub Actions가 자동으로 다음 주 업무일지를 Notion 데이터베이스에 생성한다.

구조

📄 2026년 4월 1주차 ← 데이터베이스 행 (주차 페이지) 📄 2026-03-30 ← 서브페이지 (월) 📄 2026-03-31 ← 서브페이지 (화) 📄 2026-04-01 ← 서브페이지 (수) 📄 2026-04-02 ← 서브페이지 (목) ← 주간보고 포함 📄 2026-04-03 ← 서브페이지 (금)
Plain Text
복사

날짜 서브페이지 양식

월/화/수/금
## 전날 To-do ☐ 1 ☐ 2 ☐ 3 ## To-do (우선순위 반영) ☐ 1 ☐ 2 ☐ 3
Plain Text
복사
목요일 (주간보고 추가)
## 전날 To-do ☐ 1 ☐ 2 ☐ 3 ## To-do (우선순위 반영) ☐ 1 ☐ 2 ☐ 3 ## 주간보고 > (내용 입력)
Plain Text
복사

주차 페이지 속성

속성
아이콘
날짜
해당 주 월요일 자동 입력
상태
시작 전
Place
비워두기

파일 구조

레포지토리/ ├── notion_weekly_diary.py └── .github/ └── workflows/ └── notion_weekly_diary.yml
Plain Text
복사

세팅 방법

1단계 — Notion Integration 생성

1.
https://www.notion.so/my-integrations 접속
2.
+ New integration 클릭 후 생성
3.
API Key 복사
4.
Notion 업무일지 데이터베이스 열기 → ···Connections → Integration 연결

2단계 — 데이터베이스 ID 확인

업무일지 데이터베이스를 전체 페이지로 열기 ( 버튼) → URL에서 추출
https://www.notion.so/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx?v=... ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 이 32자리가 DATABASE_ID
Plain Text
복사

3단계 — GitHub Repository Secrets 등록

Settings → Secrets and variables → Actions → New repository secret
Secret 이름
설명
NOTION_API_KEY
Notion Integration API Key
NOTION_DATABASE_ID
업무일지 데이터베이스 ID
NOTION_TEMPLATE_PAGE_ID 는 등록 불필요 (양식이 코드에 내장됨)

4단계 — 파일 push

git add notion_weekly_diary.py .github/workflows/notion_weekly_diary.yml git commit -m "feat: Notion 주간 업무일지 자동 생성" git push
Bash
복사

5단계 — 수동 실행 테스트

GitHub → Actions 탭 → Notion 주간 업무일지 자동 생성Run workflow

스케줄

자동 실행: 매주 금요일 오후 6시 (KST)
수동 실행: Actions 탭 → Run workflow

주의사항

중복 방지

동일한 주차 페이지가 이미 존재하면 자동으로 종료된다. 수동 실행 후 스케줄링이 돌아도 중복 생성되지 않는다.

휴지통 주의

Notion에서 잘못 생성된 주차 페이지를 휴지통에 버리면, API 조회 시 휴지통 항목도 포함되어 중복으로 인식할 수 있다. → 반드시 영구 삭제 처리할 것

속성명 주의

스크립트 내 속성명(날짜, 상태)이 Notion 데이터베이스의 실제 속성명과 일치해야 한다. 다를 경우 스크립트에서 해당 부분 수정 필요.

지원되지 않는 블록

Notion API는 일부 블록(linked database, embed 등)을 지원하지 않는다.

주차 계산 기준

해당 월의 첫 번째 월요일 = 1주차
월이 바뀌는 주(예: 3/30~4/3)는 월요일 날짜 기준3월 5주차

트러블슈팅

증상
원인
해결
400 Bad Request
DATABASE_ID가 잘못됨
URL에서 DB ID 재확인
페이지가 사이드바에 생성됨
PARENT_PAGE_ID를 사용한 구버전
NOTION_DATABASE_ID로 교체
생성은 됐는데 속성 오류
속성명 불일치
스크립트 내 속성명 수정
실행됐는데 생성 안 됨
휴지통에 동일 주차 페이지 존재
휴지통에서 영구 삭제 후 재실행
주차 계산 오류 (6주차 등)
구버전 로직
최신 스크립트로 교체

구성 스크립트 및 yml 파일 정리

""" Notion 업무일지 자동 생성 스크립트 - 다음 주 평일(월~금) 업무일지 페이지를 데이터베이스에 자동 생성 - 월/화/수/금: 전날 To-do + To-do (우선순위 반영) - 목요일: 전날 To-do + To-do (우선순위 반영) + 주간보고 - GitHub Actions로 매주 금요일 자동 실행 """ import os import re import json import requests from datetime import date, timedelta # ── 환경변수 ──────────────────────────────────────────────── NOTION_API_KEY = os.environ["NOTION_API_KEY"] DATABASE_ID = os.environ["NOTION_DATABASE_ID"] HEADERS = { "Authorization": f"Bearer {NOTION_API_KEY}", "Content-Type": "application/json", "Notion-Version": "2022-06-28", } # ── 문서 양식 ──────────────────────────────────────────────── def make_todo_block(text: str) -> dict: """체크박스(to_do) 블록 생성""" return { "object": "block", "type": "to_do", "to_do": { "rich_text": [{"type": "text", "text": {"content": text}}], "checked": False, }, } def make_heading2_block(text: str) -> dict: """heading_2 블록 생성""" return { "object": "block", "type": "heading_2", "heading_2": { "rich_text": [{"type": "text", "text": {"content": text}}], }, } def make_paragraph_block(text: str = "") -> dict: """빈 줄(paragraph) 블록 생성""" return { "object": "block", "type": "paragraph", "paragraph": { "rich_text": [{"type": "text", "text": {"content": text}}], }, } def make_quote_block(text: str = "") -> dict: """주간보고용 quote 블록 생성""" return { "object": "block", "type": "quote", "quote": { "rich_text": [{"type": "text", "text": {"content": text}}], }, } def build_page_blocks(target_date: date) -> list[dict]: """ 날짜에 따라 문서 양식 블록 리스트 반환 - 목요일(weekday==3): 주간보고 섹션 추가 - 그 외: 기본 양식 """ blocks = [ # ── 전날 To-do ── make_heading2_block("전날 To-do"), make_todo_block("1"), make_todo_block("2"), make_todo_block("3"), make_paragraph_block(), # ── To-do (우선순위 반영) ── make_heading2_block("To-do (우선순위 반영)"), make_todo_block("1"), make_todo_block("2"), make_todo_block("3"), make_paragraph_block(), ] # 목요일에만 주간보고 섹션 추가 if target_date.weekday() == 3: blocks += [ make_heading2_block("주간보고"), make_quote_block(), ] return blocks # ── 주차 계산 ─────────────────────────────────────────────── def get_next_week_weekdays() -> list[date]: """다음 주 월~금 날짜 리스트 반환""" today = date.today() days_until_monday = (7 - today.weekday()) % 7 or 7 next_monday = today + timedelta(days=days_until_monday) return [next_monday + timedelta(days=i) for i in range(5)] def get_week_label(monday: date) -> str: """ 월요일 날짜 기준으로 'YYYY년 M월 N주차' 레이블 생성 - 해당 월의 첫 번째 월요일 = 1주차 - 월이 바뀌는 주(예: 3/30~4/3)는 월요일(3/30) 기준 → '3월 5주차' """ year = monday.year month = monday.month first_of_month = date(year, month, 1) first_weekday = first_of_month.weekday() days_to_first_monday = (7 - first_weekday) % 7 first_monday = first_of_month + timedelta(days=days_to_first_monday) if monday < first_monday: prev_month = month - 1 if month > 1 else 12 prev_year = year if month > 1 else year - 1 first_of_prev = date(prev_year, prev_month, 1) fw = first_of_prev.weekday() d2fm = (7 - fw) % 7 fm_prev = first_of_prev + timedelta(days=d2fm) wn = (monday - fm_prev).days // 7 + 1 return f"{prev_year}{prev_month}{wn}주차" week_num = (monday - first_monday).days // 7 + 1 return f"{year}{month}{week_num}주차" # ── Notion API 유틸 ───────────────────────────────────────── def get_db_page_title(page: dict) -> str: props = page.get("properties", {}) for key in ("title", "이름", "Name"): if key in props: title_list = props[key].get("title", []) if title_list: return title_list[0].get("plain_text", "") return "" def query_database(database_id: str) -> list[dict]: url = f"https://api.notion.com/v1/databases/{database_id}/query" res = requests.post(url, headers=HEADERS, data=json.dumps({})) res.raise_for_status() return res.json().get("results", []) def create_db_page(database_id: str, title: str, monday: date) -> dict: """데이터베이스에 주차 페이지 생성 (날짜=월요일, 상태=시작 전)""" payload = { "parent": {"type": "database_id", "database_id": database_id}, "icon": {"type": "emoji", "emoji": "📄"}, "properties": { "title": { "title": [{"type": "text", "text": {"content": title}}] }, "날짜": { "date": {"start": monday.strftime("%Y-%m-%d")} }, "상태": { "status": {"name": "시작 전"} }, }, } res = requests.post( "https://api.notion.com/v1/pages", headers=HEADERS, data=json.dumps(payload), ) res.raise_for_status() return res.json() def create_sub_page(parent_page_id: str, title: str, blocks: list[dict]) -> dict: """날짜 서브페이지 생성 (📝 이모지 + 양식 블록 포함)""" payload = { "parent": {"type": "page_id", "page_id": parent_page_id}, "icon": {"type": "emoji", "emoji": "📄"}, "properties": { "title": { "title": [{"type": "text", "text": {"content": title}}] } }, "children": blocks, } res = requests.post( "https://api.notion.com/v1/pages", headers=HEADERS, data=json.dumps(payload), ) res.raise_for_status() return res.json() def week_page_exists(database_id: str, week_label: str) -> bool: """중복 방지: 동일 주차 페이지 존재 여부 확인""" for page in query_database(database_id): if get_db_page_title(page) == week_label: return True return False # ── 메인 로직 ─────────────────────────────────────────────── DAY_NAMES = ["월", "화", "수", "목", "금"] def main(): weekdays = get_next_week_weekdays() monday = weekdays[0] week_label = get_week_label(monday) print(f"📅 생성 대상 주차: {week_label}") print(f" 월요일: {monday.strftime('%Y-%m-%d')}") print(f" 날짜: {[d.strftime('%Y-%m-%d') for d in weekdays]}") # 중복 방지 if week_page_exists(DATABASE_ID, week_label): print(f"\n⚠ '{week_label}' 페이지가 이미 존재합니다. 종료합니다.") return # 1. 데이터베이스에 주차 페이지 생성 print(f"\n📁 데이터베이스에 주차 페이지 생성: {week_label}") week_page = create_db_page(DATABASE_ID, week_label, monday) week_page_id = week_page["id"] print(f" ✅ 생성 완료 (ID: {week_page_id})") # 2. 날짜별 서브페이지 생성 print("\n📝 날짜별 서브페이지 생성 중...") for d in weekdays: title = d.strftime("%Y-%m-%d") day_name = DAY_NAMES[d.weekday()] is_thursday = d.weekday() == 3 blocks = build_page_blocks(d) label = f"{title}({day_name})" + (" 📋 주간보고 포함" if is_thursday else "") print(f" → {label} 생성 중...") create_sub_page(week_page_id, title, blocks) print(f" ✅ 완료") print(f"\n🎉 {week_label} 업무일지 생성 완료! (총 {len(weekdays)}개 페이지)") if __name__ == "__main__": main()
Python
복사
workflow 파일
name: 📅 Notion 주간 업무일지 자동 생성 on: # 매주 금요일 오후 6시 (KST) = UTC 09:00 schedule: - cron: "0 9 * * 5" # 수동 실행도 가능하도록 workflow_dispatch: jobs: create-weekly-diary: runs-on: ubuntu-latest steps: - name: 📥 코드 체크아웃 uses: actions/checkout@v4 - name: 🐍 Python 세팅 uses: actions/setup-python@v5 with: python-version: "3.12" - name: 📦 의존성 설치 run: pip install requests - name: 🚀 업무일지 생성 실행 env: NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }} NOTION_DATABASE_ID: ${{ secrets.NOTION_DATABASE_ID }} # NOTION_TEMPLATE_PAGE_ID: ${{ secrets.NOTION_TEMPLATE_PAGE_ID }} # 선택사항 run: python notion_weekly_diary.py
YAML
복사