[Control Tower] Day 4: 깃허브 이슈 & PR, 노션에 '무료로' 자동 동기화하기 (삽질기 포함)

2026. 1. 9. 15:32·Projects/Team Projects

프로젝트 진행 중 GitHub의 Issue와 PR을 Notion에서 한눈에 보고 싶었다. 노션의 기본 기능을 쓰려다가 유료화의 벽에 부딪히고, 결국 GitHub Actions를 이용해 무료로 자동화 시스템을 구축한 과정을 정리한다.

1. 문제 상황: "왜 안 되지?"

처음에는 노션의 /github 명령어를 사용해 동기화하려고 했다.

  1. Notion AI의 방해: /git만 쳐도 AI가 먼저 튀어나와서 명령어를 치기 힘들었다.
  2. 유료화의 벽: 막상 연동하려니 "GitHub 동기화 데이터베이스는 비즈니스 요금제 기능입니다"라는 메시지가 떴다. (팀 워크스페이스 제한)
  3. Gist 임베드 착각: 급한 마음에 Gist 입력창에 이슈 링크를 넣었으나 당연히 작동하지 않았다.

결국, 외부 툴 없이 GitHub Actions + Notion API를 사용해 직접 파이프라인을 구축하기로 결정했다.

2. 해결 방법: GitHub Actions 도입

구조 설계

  • Trigger: 깃허브에서 Issue나 PR이 opened(생성)되면 실행.
  • Logic: GitHub Actions(Javascript)가 Notion API를 호출.
  • Destination: 이슈는 '이슈 DB'로, PR은 'PR DB'로 분기 처리하여 저장.

준비물

  1. Notion Integration: 내 통합 설정에서 새 통합(봇) 생성 → API Key 발급.
  2. Notion DB: 이슈용, PR용 데이터베이스 2개 생성 후 봇 연결(Connect) 필수.
  3. GitHub Secrets: 레포지토리 Settings에 API Key와 DB ID 등록.

3. 트러블 슈팅 (삽질 포인트 ⛏️)

구현 과정에서 겪은 주요 에러와 해결책이다.

이슈 1: Status 속성 불일치 (400 Error)

노션에는 '상태(Status)'라는 전용 속성이 있다. 하지만 API로 데이터를 보낼 때 단순히 Select(선택) 형식으로 보냈더니 타입이 맞지 않아 에러가 발생했다.

  • 원인: 코드(select) != 노션 속성(status)
  • 해결: 코드를 수정하는 것보다 노션 DB의 속성 유형을 '상태'에서 '선택(Select)'으로 변경하는 것이 훨씬 간단하고 확실했다.

이슈 2: 환경변수 이름 오타 (undefined)

분명 Secrets를 등록했는데 코드가 ID를 못 읽어왔다.

  • 원인: GitHub Secrets에는 NOTION_DATABASE_ID로 저장하고, 코드에서는 NOTION_DB_ID로 호출함.
  • 해결: 변수명을 하나로 통일하여 해결.

4. 최종 코드 (.github/workflows/main.yml)

이슈와 PR을 구분하여 서로 다른 데이터베이스에 저장하는 최종 스크립트다.

YAML
name: Sync Issues/PR to Notion

on:
  issues:
    types: [opened]
  pull_request:
    types: [opened]

jobs:
  add-to-notion:
    runs-on: ubuntu-latest
    steps:
      - name: Send to Notion
        uses: actions/github-script@v6
        env:
          NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }}
          NOTION_ISSUE_ID: ${{ secrets.NOTION_DATABASE_ID }}    # 이슈 DB ID
          NOTION_PR_ID: ${{ secrets.NOTION_PR_DATABASE_ID }}    # PR DB ID
        with:
          script: |
            // 1. 이벤트 감지 (이슈 vs PR)
            const isIssue = context.eventName === 'issues';
            const payload = context.payload;
            const item = isIssue ? payload.issue : payload.pull_request;
            
            const title = `[${isIssue ? 'Issue' : 'PR'}] #${item.number} ${item.title}`;
            const url = item.html_url;

            // 2. 저장할 타겟 DB 설정
            const targetDatabaseId = isIssue ? process.env.NOTION_ISSUE_ID : process.env.NOTION_PR_ID;
            
            // 3. Notion API 호출
            const response = await fetch('https://api.notion.com/v1/pages', {
              method: 'POST',
              headers: {
                'Authorization': `Bearer ${process.env.NOTION_API_KEY}`,
                'Content-Type': 'application/json',
                'Notion-Version': '2022-06-28'
              },
              body: JSON.stringify({
                parent: { database_id: targetDatabaseId },
                properties: {
                  "Name": { 
                    title: [ { text: { content: title } } ]
                  },
                  "URL": { 
                    url: url
                  },
                  "Status": { 
                    select: { name: "Not Started" } // 노션에 'Not Started' 옵션(선택 속성)이 있어야 함
                  }
                }
              })
            });
            
            if (!response.ok) {
              const errorText = await response.text();
              core.setFailed(`Notion API Error: ${response.status} ${errorText}`);
            } else {
              console.log(`Successfully added to ${isIssue ? 'Issue' : 'PR'} Database!`);
            }

5. 결론

  • 비용 절감: 유료 플랜 업그레이드 없이 무료로 연동 성공.
  • 커스터마이징: 노션의 동기화 DB는 수정이 제한적인데, 이 방식은 내가 원하는 대로 속성을 추가하거나 입맛대로 바꿀 수 있어 더 좋다.
  • Git/Release 담당자의 삶: 이제 팀원들이 이슈를 만들면 자동으로 노션에 꽂힌다. 편안하다.

'Projects > Team Projects' 카테고리의 다른 글

[Control Tower] 프론트엔드 혼자서 이틀 만에 백엔드 API 명세서 역설계하기 (feat. AI)  (0) 2026.01.22
[Control Tower] Git 충돌 해결부터 사이드바 권한 분리까지  (0) 2026.01.21
[Control Tower] Cursor와 Figma를 활용한 효율적인 UI/UX 프로토타이핑  (0) 2026.01.13
[Control Tower] Day 1-2: 팀 빌딩, 항공사 HR SaaS 기획, 그리고 협업 환경 구축기  (0) 2026.01.07
세미프로젝트(2025.10.23 ~ 11.20) 회고록  (0) 2025.11.21
'Projects/Team Projects' 카테고리의 다른 글
  • [Control Tower] Git 충돌 해결부터 사이드바 권한 분리까지
  • [Control Tower] Cursor와 Figma를 활용한 효율적인 UI/UX 프로토타이핑
  • [Control Tower] Day 1-2: 팀 빌딩, 항공사 HR SaaS 기획, 그리고 협업 환경 구축기
  • 세미프로젝트(2025.10.23 ~ 11.20) 회고록
tlsgkstj
tlsgkstj
짱구의 성장 일기
  • tlsgkstj
    코딩하는 짱구
    tlsgkstj
  • 전체
    오늘
    어제
    • 분류 전체보기 (159)
      • About (1)
      • Projects (35)
        • Personal Projects (21)
        • Team Projects (14)
      • Engineering (20)
        • CS & Tools (0)
        • Backend Core (15)
        • Frontend (1)
        • Infra & Cloud (2)
        • AI & Tools (1)
      • Trouble Shooting & Issues (0)
      • Growth & Career (38)
        • Interview Prep (0)
        • Retrospectives (38)
      • Archive (65)
        • TIL (8)
        • Daily Dev Q&A (56)
  • 블로그 메뉴

    • 홈
    • About
    • Projects
    • Tech Stack
    • Dev Log
    • GitHub
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    DevFestIncheon2025
    경기기후바이브코딩
    데브페스트
    OrphanRemova
    backend
    devlog
    jpa
    java
    til
    클로드코드
    network
    프로덕트개발자
    spring
    REACT
    커리어리셋
    SpringBoot
    프로젝트회고
    Spring비교
    Project_Review
    aws_s3
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
tlsgkstj
[Control Tower] Day 4: 깃허브 이슈 & PR, 노션에 '무료로' 자동 동기화하기 (삽질기 포함)
상단으로

티스토리툴바