
Slack 스탬프 하나로 Claude가 수정 PR을 만드는 Bot을 만들었다
요약
Slack 스탬프 클릭 한 번으로 GitHub Issue 생성부터 Claude Code를 이용한 PR 생성까지 자동화하는 워크플로우를 구축한 사례를 소개합니다. Slack, GitHub, Claude Code를 연동하여 의뢰 접수부터 코드 수정 완료까지의 과정을 하나의 스레드에서 완결합니다.
핵심 포인트
- Slack 스탬프를 트리거로 GitHub Issue 자동 생성
- Claude Code Action을 활용한 자동 코드 수정 및 PR 생성
- Webhook을 통한 Slack 스레드 내 작업 완료 알림 자동화
- 의뢰부터 PR 생성까지의 전 과정을 단일 워크플로우로 통합
1. 서론
안녕하세요!!
주식회사 우구이스 솔루션즈(うぐいすソリューションズ)에서 엔지니어를 소박하게 하고 있는 나카에입니다.
Slack으로 날아오는 "이것 좀 고쳐주세요", "여기 좀 조사해주세요"라는 메시지를 흘려보내고 잊어버리는 상황을 방지하기 위해,
Slack에서 /post
→ 의뢰 게시 → 관리자가 승인 스탬프 클릭 → GitHub Issue 자동 생성 → Claude Code Action이 실행되어 PR이 날아옴 → 완성 알림이 원래 스레드로 돌아옴
이라는 일련의 의뢰 접수 Bot을 만들었습니다.
Slack ─스탬프→ meyasubako ─Issue 생성→ GitHub ─@claude→ Claude Code Action ─PR→ GitHub
↑ │
└──────────── Webhook을 통해 완성 알림 ←────────────────────┘
본 기사에서는 이 Bot에 구현된 7가지 기능을 순서대로 해설합니다.
2. 동작 데모
먼저 의뢰 → 스탬프 → PR이 날아오기까지의 흐름을 각 단계의 스크린샷으로 한 번에 살펴보겠습니다.
/post로 모달(Modal)을 열기
① Slack의 임의의 채널에서 /post를 실행하면, 카테고리와 내용을 입력하는 모달이 나타납니다.

② 의뢰 카드가 채널에 게시됨
모달 전송 후, 헤더·본문·게시자 표기가 정돈된 카드가 채널에 게시됩니다.

③ 관리자가 승인 스탬프를 누르면 게시 내용으로 GitHub에 Issue를 생성함
의뢰 카드를 본 관리자가 트리거로 설정된 스탬프를 누릅니다.
스탬프를 누른 후, bot이 GitHub Issue를 만들고 URL을 원래 스레드에 답장합니다.

④ GitHub에 Issue가 생성되어 있음
링크를 열면, @claude 멘션·의뢰 내용·필수 제약 사항·Slack 정보가 정리된 Issue가 만들어져 있습니다.

⑤ Claude Code Action이 기동
Issue 본문의 @claude를 트리거로, 대상 리포지토리의 GitHub Actions에서 Claude Code 워크플로우가 자동으로 기동합니다.

⑥ Claude가 Pull Request를 생성
Claude가 develop 브랜치를 기점으로 브랜치를 생성하고, 변경 사항을 커밋(Commit)·푸시(Push)하며, Closes #N을 포함한 PR을 생성합니다.

⑧ 원래 Slack 스레드로 PR 완성 알림이 돌아옴
GitHub Webhook을 통해 meyasubako가 PR 생성을 감지하고, 최초의 의뢰 스레드에 "Pull Request가 생성되었습니다"라고 답장합니다.

의뢰자는 Slack 스탬프를 한 번 누르는 것만으로, 마지막 PR URL까지 계속 같은 스레드 안에서 완결됩니다.
3. 왜 만들었는가
저는 사내 시스템 개발/유지보수 담당을 하면서, 병행하여 수탁 프로젝트에도 참여하고 있습니다. 수탁 업무에 집중하고 싶은 한편, 사내 시스템에 대한 수정 의뢰나 문의 대응에도 일정 시간이 소요되는 상황이 있었습니다.
사내 태스크 대응을 가급적 자동화하여, 내가 직접 손을 움직이는 시간을 수탁 업무로 돌리고 싶다 — 이것이 이번 Bot을 만든 직접적인 동기입니다.
기본적인 운영 방식으로서, 사내 시스템에 대한 수정 의뢰는 전용 Slack 채널에 게시하도록 하는 형태를 이전부터 취하고 있었습니다. 이를 기점으로,
- 의뢰의 가시화(Slack에서 카드로 구현, 누구의 어떤 의뢰인지 명시)
- 의뢰의 트리아지 (Triage)(승인 스탬프를 누르기 전까지는 Issue화 하지 않음)
- 채택된 의뢰의 착수(Claude가 PR을 내줌)
- 완성의 추적(PR이 완료되면 원래 스레드로 알림)
까지를 일련의 과정으로 연결하여, 인간은 "PR을 리뷰하는" 단계부터 시작할 수 있는 상태로 만들고 싶다고 생각했습니다.
4. 시스템 구성
4.1. 컴포넌트 구성도
meyasubako는 Node.js + Express로 작성된 작은 Web Service입니다. AI 추론 기능은 없으며, Slack과 GitHub 사이의 가교 역할에 집중합니다.
4.2. 시퀀스 다이어그램 (시계열)
의뢰 1건당 실제 움직임을 시계열로 나열하면 다음과 같습니다. Slack의 3초 타임아웃 대책으로서, 모든 수신 엔드포인트는 즉시 200을 반환한 후 본 처리를 비동기적으로 실행한다는 점에 주목해 주세요.
5. 기능 상세
/post
슬래시 커맨드(Slash Command)로 의뢰 게시 모달을 호출합니다.
기능 1: Slack의 어떤 채널에서든 /post를 입력하면, 의뢰 게시용 모달이 나타납니다.
입력 항목은 두 가지뿐입니다.
| 항목 | 내용 |
|---|---|
| 카테고리 | 드롭다운에서 1개 선택 (신청 관리 시스템 개수 의뢰 / 조사 의뢰 / 작성 의뢰) |
| 내용 | 자유 기술 텍스트 |
전송하면 해당 채널에 정형화된 카드 형식으로 게시됩니다. 헤더에 카테고리·본문·게시자 표기가 나열되어, 의뢰의 종류와 내용을 한눈에 파악할 수 있습니다.
「의뢰 템플릿용 채널」을 따로 만드는 운영 방식과 달리, 대화의 흐름 속에서 의뢰를 생성할 수 있다는 점이 은근히 편리하며, 문맥을 끊지 않고 의뢰를 남길 수 있습니다.
기능 2: 승인 스탬프를 통한 Issue화 트리거
게시된 의뢰 카드에 허가된 사용자가 특정 스탬프를 누르면, 해당 의뢰가 GitHub Issue로 변환됩니다.
이를 통해,
- 프리 포맷(Free format)의 상담·푸념이 실수로 GitHub에 흘러 들어가는 것을 방지
- 스탬프를 누르는 행위 = "이것은 정식으로 착수할 의뢰입니다"라는 트리아지(Triage) 성립
- 누를 수 있는 사람은 환경 변수로 제어 (
ALLOWED_REACTION_USERS)할 수 있으므로, 누구나 마음대로 Issue화할 수 없음
현재는 「신청 관리 시스템 개수 의뢰」 카테고리의 의뢰만 Issue화 대상입니다. 그 외의 카테고리는 Slack 내에서 완결되도록 설계되었습니다. "지금 GitHub로 만들고 싶은 의뢰"와 "Slack 내 대화로 끝낼 의뢰"의 경계를 카테고리와 승인이라는 2단계 필터로 설정할 수 있습니다.
기능 3: 사용자 이름·채널 이름 자동 해결 (Resolution)
GitHub Issue에 Slack의 생 ID(<@U06MKT2897V> / C0ABFD1DS1M)가 그대로 나오면 아무도 읽을 수 없습니다.
그래서 Issue 생성 시 Slack API를 호출하여,
- 게시자·승인자의 표시 이름(@kouki 등)
- 의뢰가 게시된 채널 이름(#general 등)
을 자동으로 해결(Resolve)하여 Issue 본문에 삽입합니다.
사양으로서 해결에 실패한 경우에는 ID를 그대로 노출하지 않고 해당 행을 생략하도록 했습니다. 읽기 어려운 정보를 어중간하게 남기느니 차라리 없는 편이 낫다는 판단입니다.
@claude 멘션이 포함된 Issue 자동 생성
기능 4: 승인되면 meyasubako가 GitHub REST API를 통해 Issue를 생성합니다. 내용은 다음과 같은 구성입니다.
- 제목 — 의뢰 본문의 첫 60자를 요약으로 채택
- 본문 서두 —
@claude 이 Issue의 의뢰 내용을 구현해 주세요.를 반드시 삽입 - 의뢰 내용 — Slack에 게시된 카드를 통째로 전재 (mention은 이름 해결 완료됨)
- 필수 제약 사항 —
develop을 기점으로 브랜치(branch) 생성, main / master로 PR을 만들지 말 것, 불필요한 대규모 리팩토링을 피할 것 등 - 완료 조건 — 테스트 통과, PR 본문에 변경 내용 작성, 리뷰 대기 등
- Slack 정보 — 채널 이름·승인자 (해결 가능한 경우에만)
- HTML 코멘트 (비표시) —
<!-- meyasubako:thread channel=... ts=... -->형태로 PR 완성 알림에 사용할 정보를 삽입 - 라벨 (Label) —
from-slack,ai-task
이때 서두의 @claude 멘션이 다음 기능 5의 트리거가 됩니다.
기능 5: Claude Code Action을 통한 자동 PR 생성
대상 리포지토리에는 anthropics/claude-code-action@v1을 포함한 workflow가 하나 배치되어 있으며, Issue 본문에 @claude가 포함되어 있으면 자동으로 기동합니다.
workflow 내에서는 Claude에게 다음과 같이 지시하고 있습니다.
mode: agent
claude_args: >-
--allowed-tools "Edit,Write,Read,Glob,Grep,MultiEdit,
...
이를 통해 Claude는 GitHub의 Ubuntu Runner 상에서
develop브랜치를 기점으로claude/<요약>브랜치 생성- Issue 내용을 읽고
Read/Grep/Glob으로 대상 코드 파악 Edit/Write로 구현git commit&git pushgh pr create --base develop --body "...Closes #<번호>"로 Pull Request 생성
까지를 자율적으로 실행합니다.
API 키 방식이 아니라 Claude Code Max 구독의 OAuth 토큰 (claude setup-token)으로 구동하기 때문에, 추가적인 API 과금은 발생하지 않습니다.
기능 6: PR 생성 시 Slack 스레드 알림
Claude가 PR을 생성하면, 요청자는 원래 스레드에서 그대로 알림을 받을 수 있도록 했습니다. 이것이 의외로 중요한데, "요청 → 방치 → 어떻게 됐지?"와 같은 지연을 방지합니다.
구현 방법:
- 대상 리포지토리에 GitHub Webhook을 설정하고,
pull_request.opened이벤트를 meyasubako의/github/webhook엔드포인트로 POST - meyasubako는 HMAC SHA-256으로 서명 검증 (
GITHUB_WEBHOOK_SECRET을 공유 비밀로 가짐) - PR 본문의
Closes #N으로부터 원래 Issue 번호를 역추적 - Issue 본문에 삽입해 두었던 HTML 코멘트에서 원래 Slack 스레드의
channel_id와ts를 추출 chat.postMessage로 "Pull Request가 생성되었습니다: <URL>"라고 원래 스레드에 답장
요청자는 Slack을 한 발짝도 벗어나지 않고,
요청 카드
├─ 스레드: GitHub Issue를 생성했습니다 <Issue URL>
└─ 스레드: Pull Request가 생성되었습니다 <PR URL>
이라는 동선을 통해 최종 결과물까지 도달할 수 있습니다.
기능 7: 설정의 유연성
모든 주요 파라미터는 환경 변수로 교체 가능합니다.
| 환경 변수 | 용도 |
|---|---|
TARGET_REACTION | 승인 스탬프의 이모지 이름 |
ALLOWED_REACTION_USERS | 승인 가능한 Slack 사용자 ID (쉼표 구분) |
GITHUB_OWNER / GITHUB_REPO | Issue 생성 대상 리포지토리 |
ISSUE_BASE_BRANCH | Claude에게 "이곳을 base로 하여 PR을 만들어줘"라고 전달할 브랜치 이름 (기본값 develop) |
GITHUB_WEBHOOK_SECRET | PR 알림 Webhook용 공유 비밀 |
다른 팀에 전개할 때는 환경 변수를 교체하는 것만으로 재사용할 수 있습니다.
6. 구현 중 막혔던 부분
개발 중에 빠졌던 함정을 두 가지만 공유합니다.
permission_denials_count: 3 — 작동하는 것처럼 보이지만 작동하지 않음
Claude Code Action을 처음 실행했을 때, 작업(job)은 초록색으로 완료되었음에도 PR이 오지 않았습니다. 로그를 파헤쳐 보니:
{ "is_error": false, "num_turns": 5, "permission_denials_count": 3 }
Claude가 3번의 도구 호출(tool call)을 거부당하고 있었습니다. 기본적으로 Bash(git:*)나 Edit / Write가 허용되어 있지 않았던 것입니다. claude_args: --allowed-tools "..."로 필요한 도구를 나열하자 한 번에 PR까지 도달하게 되었습니다.
num_turns: 1 — Claude가 코멘트만 남기고 끝남
또 다른 경우에는 permission_denials_count: 0임에도 PR이 나오지 않고, num_turns: 1
패턴에 직면했습니다. 원인은 Claude가 "구현하겠습니다"라고 답변만 남기고 그대로 멈춰 있었기 때문이었습니다.
대책은 workflow에서 mode: agent를 명시하고, 프롬프트에 "댓글만 남기고 끝내서는 안 된다. 구현까지 완수할 것"이라고 명시하는 것뿐입니다. 이렇게 하니 단번에 구현에 착수하게 되었습니다.
"작동하는 것처럼 보이지만 작동하지 않는" 유형의 문제는 알아차리기 어렵기 때문에, num_turns와 permission_denials_count 두 가지 메트릭 (Metrics)은 항상 가장 먼저 체크하는 것을 추천합니다.
7. 요약
의뢰 → 승인 → 구현 → 알림의 4단계를 의뢰자는 Slack 스탬프 한 번만으로 실행할 수 있습니다. Bot 자체는 AI 추론을 전혀 수행하지 않고, Issue를 생성하는 단계에서 책임을 마치는 심플한 설계로 구성했기에, Claude의 쿼터 (Quota)·토큰 (Token)·모델 선택은 대상 리포지토리 (Repository) 측에서 완결되며 필요에 따라 교체하기 쉽습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기