【Claude Code】 Copilot의 코드 리뷰 수정을 자동화하기
요약
본 기사는 GitHub Copilot의 코드 리뷰 결과를 자동화하여 개발 워크플로우를 개선하는 'Claude Code' 스킬을 소개합니다. 이 스킬은 Pull Request(PR)에 쌓인 Copilot의 미해결 지적 사항을 하나씩 가져와, 해당 코드 컨텍스트와 함께 사용자에게 제시합니다. 사용자는 '적용', '스킵', '추가 논의' 중 선택하여 판단하고, 필요한 수정사항만 작업 트리에 반영하며, 최종적으로 적용/스킵 목록 요약까지 받게 됩니다.
핵심 포인트
- GitHub Copilot 리뷰를 정밀하게 조사하고 흡수하는 대화형 워크플로우를 제공합니다.
- 사용자는 코멘트 1건마다 '적용', '스킵', '추가 논의' 중 선택하여 판단할 수 있습니다.
- 이 스킬은 커밋이나 푸시 같은 Git 작업을 수행하지 않고, 작업 트리(Working Tree)에 수정 반영까지만 담당합니다.
- 실행 전 인증 상태, 현재 브랜치, PR 연결 여부 등 여러 전제 조건을 철저히 검사하는 절차를 포함합니다.
이번에는 GitHub을 이용한 개발에서, GitHub Copilot에 의한 코드 리뷰를 도입하고 있는 경우에 추천할 만한 Skill을 만들어 보았습니다.
내용은 다음과 같습니다.
- 생성한 Pull Request에 대한 GitHub Copilot의 리뷰 결과를 가져온다
- 수정 제안을 Claude Code가 리뷰하고, 필요하다면 대응한다
입니다.
관련된 이 기사들도 참고해 보세요.
※skill-creator를 사용하고 있습니다.
이번에는 다음과 같이 요청했습니다.
모호한 내용은 역질문을 통해 물어봐 주므로 매우 도움이 됩니다.
일부 마크다운(Markdown) 사양상, [```] 앞에 #을 넣었습니다.
---
name: handle-copilot-review
description: 현재 git 브랜치에 연결된 Pull Request를 특정하고, GitHub Copilot 코드 리뷰(copilot-pull-request-reviewer[bot])가 남긴 미해결 지적 사항을 하나씩 추출하여, 코멘트 내용과 해당 코드·주변 컨텍스트를 확인한 후 「채택할지 여부」를 사용자와 대화적으로 판단해 나가는 스킬. 사용자가 `/handle-copilot-review`라고 입력했을 때, 또는 "Copilot의 리뷰를 처리해줘", "Copilot 리뷰를 반영해줘", "Copilot의 지적을 봐줘", "Copilot의 코멘트에 대응해줘", "PR의 Copilot 리뷰 좀 봐줄래?", "Copilot 리뷰 처리해줘" 등, PR 상에 쌓인 GitHub Copilot 리뷰를 정밀 조사·흡수·선택하고 싶은 상황에서 반드시 사용한다. 코멘트 1건마다 「적용 / 스킵 / 추가 논의」를 사용자의 판단으로 진행하는 대화형 워크플로우, 미해결 스레드만을 대상으로 하는 처리, Copilot 이외의 리뷰어를 대상에서 제외하는 방침이 필요한 모든 케이스에서 본 스킬을 우선적으로 호출할 것. 커밋(Commit)이나 푸시(Push)는 수행하지 않으며, 작업 트리(Working Tree)에 수정 반영하는 것까지 담당한다.
---
# handle-copilot-review
현재 git 브랜치에 연결된 GitHub Pull Request를 특정하고, GitHub Copilot 코드 리뷰(`copilot-pull-request-reviewer[bot]`)가 남긴 **미해결** 지적 코멘트를 하나씩 정밀 조사하여, 코드 컨텍스트를 바탕으로 사용자와의 대화를 통해 「채택할지」를 결정하고, 필요한 것은 작업 트리에 반영해 나가는 스킬.
## 이 스킬이 목표로 하는 것
GitHub Copilot 코드 리뷰는 편리한 반면, 기계적으로 나오는 지적 사항 중에는 「문맥을 고려하면 불필요함」, 「사실 오인」, 「스타일상의 취향」 등 그대로 채택해서는 안 되는 것도 섞여 있다. 반면 본질적인 버그나 간과한 부분을 찾아내기도 하므로 무시할 수 없다. 본 스킬의 역할은 다음과 같다:
1. 현재 브랜치에서 PR 번호를 특정하여 사용자에게 제시한다
2. Copilot의 미해결 리뷰 코멘트를 **미해결 스레드 단위**로 가져온다
3. 코멘트 1건마다 「지적 내용 + 해당 코드 + 주변 컨텍스트」를 제시하고, Claude로서 채택 여부에 대한 평가를 말한다
4. 사용자가 **적용 / 스킵 / 추가 논의**의 3가지 선택지 중 하나로 판단한다
5. 「적용」이면 작업 트리에 수정을 반영, 「스킵」이면 아무것도 하지 않고 다음으로, 「추가 논의」이면 그 자리에서 대화를 계속한다
6. 모든 건의 처리가 끝난 후, 무엇을 적용하고 무엇을 스킵했는지 목록을 요약(Summary) 표시한다
7. **커밋(Commit)・푸시(Push)・PR 스레드의 답장이나 해결 조작은 일절 수행하지 않는다**
사용자가 목표로 하는 골(Goal)은 「Copilot의 지적을 정중하게 하나씩 검토하여, 정말로 받아들여야 할 것만 반영된 상태의 작업 트리」이다. 스킬은 리뷰의 판단을 빼앗지 않고, 판단을 위한 준비에 철저히 집중한다.
## 실행 절차
### Step 1: 전제 조건과 현재 상태를 일괄 취득
이하를 **병렬로** 실행한다:
#```bash
git branch --show-current
#```
#```bash
git status --short
#```
#```bash
gh auth status
#```
#```bash
gh repo view --json nameWithOwner,defaultBranchRef --jq '{repo: .nameWithOwner, default: .defaultBranchRef.name}'
#```
이것으로 「현재 브랜치 / 작업 트리 상태 / gh CLI의 인증 상태 / 리포지토리 이름」이 한 번에 갖춰진다.
치명적인 전제가 충족되지 않는 경우
gh auth status가 인증 에러 발생 → "gh auth login으로 인증한 후 다시 실행해 주세요"라고 전달하고 종료.gh repo view가 실패 (GitHub 리포지토리가 아님,gh명령어가 없음 등) → 해당 내용을 전달하고 종료.- 현재 브랜치가
main/master등 기본 브랜치(Default branch)인 경우 → "기본 브랜치에는 PR이 연결되지 않습니다.feature브랜치로 전환해 주세요"라고 전달하고 종료.
커밋되지 않은 변경 사항이 있는 경우
git status --short에서 출력이 있는 경우, Copilot의 지적을 반영하기 전부터 작업 트리(Working tree)에 변경 사항이 남아 있는 상태이다. 다음과 같이 안내한다:
#```
작업 트리에 커밋되지 않은 변경 사항이 있습니다:
path/to/file.ts- ...
...
사용자가 "계속"을 선택한 경우에만 Step 2로 진행한다.
### Step 2: 현재 브랜치에 연결된 PR 번호 특정
#```bash
gh pr view --json number,title,url,headRefName,baseRefName,state
#```
이 명령은 현재 체크아웃(Checkout)하고 있는 브랜치에 연결된 PR을 반환한다. 반환된 정보를 다음 포맷으로 사용자에게 제시한다:
#```
## 대상 PR을 특정했습니다
- 브랜치: `feat/xxx`
- PR: #123 [제목]
...
PR을 찾을 수 없는 경우
gh pr view가 no pull requests found로 실패했을 경우:
#```
이 브랜치 feat/xxx에 연결된 Pull Request를 찾을 수 없습니다.
가능한 원인:
- 아직 PR을 생성하지 않음 (
/draft-pr로 PR을 생성한 후 다시 실행해 주세요)
...
#### PR이 CLOSED / MERGED인 경우
확인을 요청한다:
#```
이 PR은 이미 <CLOSED|MERGED> 상태입니다. 그래도 Copilot 리뷰를 처리하시겠습니까?
통상적으로 MERGED 이후의 리뷰 대응은 불필요합니다.
#```
사용자가 계속을 선택하지 않는 한 종료한다.
### Step 3: Copilot의 미해결 리뷰 코멘트 가져오기
PR의 **리뷰 스레드 (Review thread)** 를 GraphQL로 가져온다. `pulls/:number/comments` REST 엔드포인트에서는 `isResolved`를 가져올 수 없기 때문에 GraphQL을 사용한다.
#```bash
gh api graphql -f query='
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
...
<owner> <repo>는 Step 1의 gh repo view 결과에서, <pr-number>는 Step 2의 결과에서 가져온다.
필터링 방침
반환된 스레드에서 다음 조건으로 압축한다:
isResolved == false인 스레드만- 스레드 내의 첫 번째 코멘트
author.login이copilot-pull-request-reviewer또는 **copilot-pull-request-reviewer[bot]**로 시작하는 것 (GitHub Copilot 코드 리뷰의 bot 계정)
isOutdated == true(코멘트 이후에 코드가 변경되어 행 번호가 오래된 경우)인 것은 "Outdated"라고 명시하면서 처리 대상에 포함한다. Outdated라 하더라도 지적의 본질은 유효한 경우가 많으므로 임의로 스킵하지 않는다.
해당 코멘트가 0건인 경우
#```
이 PR에는 미해결된 Copilot 리뷰 코멘트가 없습니다.
(참고)
- Copilot 리뷰 총 코멘트 수: N건
...
### Step 4: 미해결 코멘트의 개요 제시
처리 대상 스레드가 1건 이상 있는 경우, 먼저 전체적인 모습을 사용자에게 제시한다:
#```
## 미해결된 Copilot 리뷰 코멘트
N건의 미해결 지적이 있습니다. 하나씩 확인해 나가겠습니다.
| # | 파일 | 행 | 개요 | Outdated |
...
사용자가 OK 하면 Step 5로 진행한다.
Step 5: 코멘트 1건씩 정밀 검토 루프
N건의 코멘트에 대해, 다음 사이클을 1건씩 순서대로 반복한다. 여러 건을 한꺼번에 처리하지 않는다.
5-1. 해당 코드 읽기
스레드의 path와 line / startLine을 사용하여, 대상 파일의 해당 부분과 전후 문맥(전후 10~20행 정도)을 Read 도구로 읽는다. 다중 행 코멘트(startLine ≠ line)인 경우에는 해당 범위를 모두 커버한다.
Outdated인 경우에는 코멘트 시점의 diffHunk를 보면서 현재의 파일 내용도 함께 읽는다. "Copilot이 보고 있던 시점 → 현재"의 차이가 평가의 핵심이 된다.
5-2. 코멘트 내용과 평가를 사용자에게 제시
다음 포맷으로 제시한다:
#```
코멘트 i/N
파일: <path> 의 <line>행째 <Outdated라면 "(⚠ Outdated)">
코멘트 URL: <url>
...
<path>:<startLine>-<endLine>
<전후 컨텍스트를 포함한 코드>
#```
### Claude의 평가
<다음 중 하나의 관점을 구체적인 근거와 함께 서술한다:>
- **지적은 타당함**: <왜 타당한지. 수정안의 개요>
...
평가는 판단을 강요하지 않는다. Copilot의 지적이라 하더라도, 코드베이스 전체의 문맥이나 기존 관습을 고려했을 때 불필요한 경우가 있다. Claude는 "자신은 이렇게 보인다"를 근거와 함께 제시하는 데 그치며, 최종 판단은 반드시 사용자에게 맡긴다.
특히 다음과 같은 지적은 **부적절함(Off-target)**에 가깝다고 판단하는 경향이 강하다:
- 이미 다른 곳에서 실시되고 있는 체크의 중복 제안
- 스타일상의 선호도 문제로, 리포지토리의 기존 패턴과 모순되는 제안
- 성능상의 우려사항이지만, 대상이 명확하게 핫 패스(Hot path)가 아닌 곳
- "코멘트를 추가해야 함" 계열이며, 코드가 자명한 경우
반대로 다음과 같은 지적은 채택에 가깝다고 판단하는 경향이 강하다:
- nullable/undefined/빈 배열의 경계 조건 누락
- 예외 경로(Exception path)·에러 핸들링(Error handling)의 결여
- 보안상 명확하게 문제가 있는 패턴 (SQLi, XSS, 인증 우회 등)
- 기존 타입 정의(Type definition)·스키마와 모순되는 구현
단, 위 내용은 어디까지나 경향일 뿐이며, 최종적으로는 코드와 문맥을 보고 판단한다.
5-3. 사용자 판단에 따른 처리
"적용"이라고 답변한 경우:
Edit 도구로 대상 파일을 수정한다. 여러 파일에 걸친 수정이 필요한 경우, 먼저 사용자에게 "이 수정은 <다른 파일>에도 변경이 필요합니다. 함께 반영할까요?"라고 확인한다. 수정 후에는 다음과 같이 보고한다:
#```
✅ 적용했습니다
<path>를 수정- <변경 개요(1행)>
...
**"스킵"이라고 답변한 경우:**
#```
⏭ 스킵했습니다.
이유를 메모해 두고 싶다면 말씀해 주세요 (최종 요약에 포함합니다).
다음 코멘트 (i+1/N)로 진행합니다.
#```
사용자가 이유를 말하면, 나중의 요약을 위해 메모해 둔다.
**"추가 논의"라고 답변한 경우:**
그 자리에서 자유로운 대화(Free-form dialogue)로 들어간다. Claude는 추가 코드 조사, 관련 부분 검색, 대안 제시 등 사용자의 논의에 응한다. 논의가 수렴되면 사용자에게 다시 한번 "적용 / 스킵" 중 어느 것으로 할지 확인하고, 확정된 후 다음 코멘트로 진행한다.
#### 5-4. 중단 시의 동작
사용자가 도중에 "여기서 일단 멈춰줘", "중단해줘"라고 말하는 경우에는, 그 시점까지의 결과를 Step 6의 요약 형식으로 정리하고, 미처리된 코멘트는 "미처리: i건"으로 남긴다. 작업 트리는 "적용"한 것들만 반영된 상태 그대로 유지하며, 아무것도 되돌리지 않는다.
### Step 6: 전체 처리 후의 요약
모든 코멘트를 처리(또는 중단)한 후, 다음을 출력한다:
#```
## Copilot 리뷰 대응 완료 요약
PR: #123 [제목]
처리 결과: 적용 P건 / 스킵 Q건 / 논의 후 적용 R건 / 논의 후 스킵 S건 / 미처리 T건
...
git status --short를 실행하여 결과를 포함한다 (사용자가 현재 변경 상태를 파악하기 쉽게 하기 위함).
에지 케이스 (Edge Case)
- Copilot 코드 리뷰를 활성화하지 않은 리포지토리: Step 3의 결과에서 Copilot bot 유래의 스레드가 단 하나도 얻어지지 않는 경우 (Step 3 끝부분의 "해당 코멘트가 0건인 경우"와 동일). 리포지토리 설정에서 활성화가 필요할 수 있다는 점을 보충 설명해도 좋다.
- bot 이름의 변형: Copilot의 bot 이름은 향후 변경될 가능성이 있다.
author.login에copilot을 부분 일치로 포함하는 bot 계정(끝에[bot]을 포함)을 폭넓게 포착해도 좋다. 단,copilotxxx와 같은 무관한 사용자 이름이 포함되지 않도록login.startsWith("copilot-pull-request-reviewer")또는login == "copilot[bot]"정도로 한정한다. - 동일 스레드에 여러 코멘트가 있는 경우: 스레드 내의 첫 번째 코멘트가 Copilot이라 하더라도, 그 뒤에 사람이 답글을 남기는 경우가 있다. 본 스킬은 스레드 단위로 평가하며, 스레드의 첫 번째 Copilot 코멘트를 주요 지적으로 삼고, 후속 코멘트는 "논의 과정"으로서 컨텍스트(Context)로 표시한다. 답글 코멘트 자체를 독립적인 처리 대상으로 삼지는 않는다.
isOutdated == true인 코멘트: 상술한 바와 같이 처리 대상에 포함한다. 제시할 때 Outdated 배지를 표시하고, Claude의 평가에서 "코멘트 시점과 현재 코드의 상태가 달라져 있다"는 점을 반드시 언급한다. 이미 수정되어 지적 사항 자체가 해소된 경우도 있으므로, 이 경우에는 "현재 코드에서는 이미 해소됨"이라고 평가하고 스킵(Skip)을 권장한다.- 코멘트 본문이 극도로 긴 경우: 그대로 전문을 인용한다 (요약하지 않는다). Copilot의 지적은 근거를 포함하여 읽는 것에 의미가 있으므로, 임의로 요약하여 정보를 누락시키지 않는다.
- 적용하려는 수정이 다른 부분과 모순되는 경우: 예를 들어 "null check를 추가하라"는 지적을 따르면, 다른 곳의 로직 전제(반드시 non-null로 들어온다는 계약)가 깨지는 경우. 수정 적용 전에 Claude가 이를 인지하면, "이 수정을 넣으면
<다른 곳>의 전제와 모순될 가능성이 있습니다"라고 명시하여 사용자에게 재확인을 요청한다. - 여러 코멘트가 동일한 부분을 지적하고 있는 경우: 1건씩 처리한다는 원칙은 바꾸지 않는다. 다만 2건째 이후를 제시할 때 "1건째에서
<행 X>를 수정 완료했습니다. 본 코멘트는 동일한 부분에 대한 다른 관점의 지적입니다"라고 서두를 붙인다. - 수정 반영 시 예기치 않은 빌드/타입 에러를 유발할 것 같은 경우: Claude가 사전에 인지할 수 있는 범위 내에서 경고를 보낸다. "이 수정으로 인해
<함수>의 시그니처(Signature)가 변경되므로, 호출부 N곳의 업데이트가 필요합니다. 동시에 반영하시겠습니까?"라고 확인한다. - PR의 리뷰 스레드 수가 극도로 많은 경우 (수십 건 초과): 모든 건을 처리하는 데 시간이 걸린다는 점을 사전에 알리고, "전체 처리 / 파일 단위로 좁히기 / 특정 파일만"을 선택할 수 있도록 한다. 파일 단위로 좁히기를 선택한 경우, 대상 파일 경로를 사용자에게 물어본 뒤 해당 스레드만 처리한다.
행동 원칙
-
승인 없이 작업 트리(Working Tree)를 변경하지 않는다: Edit 툴을 통한 수정은 반드시 사용자가 "적용"을 선택한 후에만 실행한다. Claude의 평가가 "타당"하더라도 임의로 수정을 넣지 않는다.
-
커밋(Commit)·푸시(Push)는 하지 않는다: 본 스킬은 작업 트리에 반영하는 단계까지만 담당한다. 커밋은
/auto-commit, 푸시는 사용자의 수동 조작에 맡긴다. 이는 "Copilot 대응", "기능 구현", "리팩토링" 등을 한꺼번에 커밋해 버리는 것을 방지하기 위함이다. -
PR 상의 스레드를 임의로 Resolve하지 않는다: "Resolve conversation" 조작(GraphQL의
resolveReviewThreadmutation)은 본 스킬에서 실행하지 않는다. 지적에 대한 대응이 완료되었는지 여부를 판단하는 것은 리뷰어(Reviewer) 측이 수행하는 것이 본래의 역할이며, 리뷰를 받는 측이 스스로 resolve하는 것은 관습적으로 피하고 싶기 때문이다. -
판단을 빼앗지 않는다: Claude의 평가는 어디까지나 판단 재료의 제시이다. "적용해야 함", "스킵해야 함"이라고 단정적으로 유도하지 않고, 근거를 서술한 뒤 "Claude는 이렇게 보고 있습니다"라는 톤을 유지한다.
-
Copilot 이외의 리뷰어는 대상 외: 사람이 남긴 코멘트에는 본 스킬이 관여하지 않는다. 사람과의 상호작용은 개별적으로 대응할 필요가 있으므로 기계적인 처리 대상으로 삼지 않는다.
-
전문을 인용하는 것을 마다하지 않음: Copilot 코멘트 본문 및 해당 코드는 아끼지 않고 전문을 제시한다. 스크롤하는 번거로움보다 「정보를 누락하여 잘못 판단할」 리스크가 더 높다.
-
대화 도중 중단되어도 정합성을 유지: 적용된 수정 사항은 그대로 남겨두고, 미처리 사항을 명시한다. 되돌리기는 하지 않는다 (다시 하고 싶은 경우에는 사용자가
git restore로 되돌린다).
참고: 왜 스레드(Thread) 단위로 처리하는가
GitHub의 리뷰 코멘트는 스레드(Thread) 구조를 가진다. Copilot이 남긴 첫 번째 코멘트에 대해, 나중에 사람이 "이것은 틀렸다, 이런 이유로 이대로 두는 것이 좋다"라고 답장을 남긴 케이스도 있다. 스레드 단위로 처리함으로써:
- 해결 상태를 정확하게 다룰 수 있음:
isResolved는 스레드 단위의 속성이다. 코멘트 단위로는 가져올 수 없다. - 논의의 경과를 반영할 수 있음: 이미 사람이 반론한 지적을 기계적으로 "채택"해 버리는 사고를 방지할 수 있다.
- PR 상의 UI와 일치함: GitHub UI에서도 스레드 단위로 표시 및 해결되므로, 사용자의 인지 모델과 일치한다.
따라서 본 스킬은pulls/:number/comments(코멘트 단위의 REST 엔드포인트)가 아니라, GraphQL의reviewThreads를 사용한다.
미리 풀 리퀘스트(Pull Request)를 생성하여 실행해 보았습니다.
/handle-copilot-review
(이하가 GitHub 상에서의 수정 제안입니다)
구체적인 수정 내용은 생략하지만, 기대한 대로 동작했습니다!
이번 내용은 여기까지입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기