GitHub Copilot 코드 리뷰는 2026년 9월 1일부터 풀 리퀘스트를 직접 승인할 수 있습니다. 다만 이 직접 승인은 기본적으로 꺼져 있고, 관리자가 켜면 Copilot 의 승인이 저장소의 필수 승인 수에 그대로 집계됩니다. 그래서 켜기 전에 정할 것은 봇의 정확도가 아니라, 자동 승인과 자동 머지를 어느 경로까지 허용할지의 경계입니다. 아래는 Claude 기반 자동 리뷰 워크플로를 운영하는 저장소의 실제 구성과, 거기서 사람에게 남겨 둔 지점입니다.

Copilot 코드 리뷰 PR 승인, 기본값과 설정 위치

GitHub은 2026년 9월 1일 체인지로그에서 Copilot 코드 리뷰가 풀 리퀘스트를 승인할 수 있게 됐다고 알렸습니다(Copilot code review can now approve pull requests). 동작은 두 갈래예요. 하나는 모든 리뷰의 개요 코멘트에 “승인해도 되는 상태인지”를 평가해 적는 승인 평가이고, 이 평가만으로는 머지 요건에 집계되지 않습니다. 다른 하나는 관리자가 켜 줬을 때 Copilot 이 직접 승인을 제출하는 것이고, 이 승인은 저장소의 필수 승인 규칙에 집계됩니다. 같은 공지에 따르면 직접 승인은 기본적으로 꺼진 상태로 제공됩니다.

설정은 세 층에서 겹쳐 걸립니다. 엔터프라이즈는 승인을 끄거나 조직에 맡길 수 있고, 조직은 전체 켜기·저장소별 위임·특정 저장소만 켜기·전체 끄기 중에서 고르며, 저장소는 켜고 끄는 것과 함께 Copilot 이 승인할 수 있는 파일 경로를 지정할 수 있습니다. 승인 뒤에 새 커밋이 올라오면 사람 리뷰어의 승인과 똑같이 기각됩니다. 제공 범위는 퍼블릭 프리뷰이고, 대상은 Copilot Pro·Pro+·Max·Business·Enterprise 플랜입니다.

이 “기본 꺼짐”이 이 발표에서 가장 많은 정보를 담고 있는 부분입니다. 기능을 만든 쪽도 이걸 모두에게 켜 둘 만한 동작으로 보지 않았다는 뜻이니까요. 승인 권한을 여는 결정을 기본값으로 대신 내려 주지 않고, 각 저장소가 자기 사정에 맞게 켜라고 넘긴 것입니다.

코멘트와 머지 게이트는 무엇이 다른가

리뷰 코멘트는 정보입니다. 틀려도 사람이 읽고 무시하면 그만이고, 비용은 읽는 시간뿐이에요. 반면 승인은 권한입니다. 브랜치 보호 규칙에서 “승인 N개”를 요구하도록 걸어 두었다면, 그 숫자를 채우는 주체가 곧 게이트 자체입니다. 자동 승인을 켜는 순간 브랜치 보호는 “사람 N명이 봤다”가 아니라 “모델이 통과시켰다”로 의미가 바뀝니다.

여기서 흔한 착각이 하나 있습니다. 자동 리뷰의 품질이 충분히 좋아지면 승인을 맡겨도 된다는 생각인데요, 승인의 본질은 정확도가 아니라 책임 소재입니다. 잘못된 코드가 프로덕션에 나갔을 때 “누가 통과시켰는가”에 답할 수 있어야 하고, 그 답이 봇이면 남는 선택지는 대체로 “다음부터 봇을 끄자” 하나뿐입니다. 그래서 이 저장소는 자동 리뷰를 붙이되, 그 리뷰가 닿는 범위와 승인 이후의 경로를 워크플로에 못박아 두는 쪽을 택했습니다.

GitHub Actions 자동 리뷰 워크플로 트리거·permissions 구성

사내 게임 프로젝트 저장소에는 pull_request 이벤트로 도는 리뷰 워크플로가 있습니다. 리뷰어는 Copilot이 아니라 Claude Code를 GitHub Actions에서 실행하는 방식이고, 인증은 API 종량 과금이 아니라 구독 토큰을 씁니다(Claude Code GitHub Actions). 트리거 조건은 이렇게 걸려 있어요.

on:
  workflow_dispatch:
    inputs:
      pr_number:
        description: "리뷰할 PR 번호"
        required: true
  pull_request:
    types: [opened, reopened, ready_for_review, labeled]
    branches: [dev]
    paths:
      - "src/lib/**"
      - "src/data/**"
      - "supabase/**"
      - "ios/**"
      - "android/**"
      - "scripts/**"
      - "capacitor.config.ts"
      - "vite.config.ts"
      - ".github/workflows/**"

세 가지가 의도적입니다. 첫째, branches: [dev]로 개발 브랜치로 들어오는 PR만 리뷰합니다. 프로덕션 승격 PR은 이미 리뷰를 거친 코드의 재머지라 다시 볼 필요가 없습니다. 둘째, paths 필터로 장애가 났던 영역(결제·데이터·서버 함수·네이티브·빌드 스크립트·워크플로 정의)만 겁니다. UI 컴포넌트나 문서, 에셋 변경은 리뷰를 돌리지 않습니다. 구독 사용량이 터미널 작업과 공유되고 Actions 무료 분도 한정이라, “무엇을 리뷰하지 않을지”를 정하는 게 곧 비용 설계입니다. 셋째, workflow_dispatch로 과거 PR 번호를 넣어 수동 재리뷰를 돌릴 수 있게 열어 두었는데, 이 통로가 뒤에 나올 구조적 한계를 우회하는 열쇠가 됩니다.

권한은 필요한 만큼만 열되, 자동 머지까지 하려면 생각보다 넓어집니다.

permissions:
  contents: write        # 승인 시 dev 자동 머지
  pull-requests: write   # 리뷰 코멘트·라벨
  issues: write          # 연결 이슈 라벨·close
  id-token: write        # 액션 토큰 교환
  actions: write         # CI 결과 읽기 + 후속 워크플로 dispatch

리뷰 한 번은 8분에서 15분이 걸려서 timeout-minutes를 30으로 두고, 모델이 무한히 돌지 않도록 실행 턴 상한도 함께 걸어 두었습니다. 리뷰가 판정을 남기는 형식도 고정돼 있습니다. 코멘트의 첫 줄은 반드시 ## 판정: 승인 또는 조건부, 보류 중 하나로 시작하는데, 뒤이은 자동화 스텝이 이 문자열을 읽어 라벨을 바꾸기 때문입니다.

판정 코멘트와 상태 라벨, 침묵을 실패로 만드는 가드

리뷰가 돌면 PR에는 두 가지가 남습니다. 하나는 판정으로 시작하는 리뷰 코멘트, 다른 하나는 상태 라벨이에요. 라벨은 리뷰:요청에서 시작해 리뷰가 시작되면 리뷰:진행중으로 바뀌고, 판정에 따라 리뷰:승인이나 리뷰:반려로 끝납니다. 다섯 개의 라벨은 상호 배타라 한 시점에 하나만 붙습니다. PR 목록만 훑어도 지금 무엇이 사람 손을 기다리는지 한 줄로 보입니다.

가장 중요한 장치는 침묵을 막는 가드입니다.

Claude Code 액션은 인증 토큰이 없거나 봇 발화가 허용 목록에 없으면 내부 스텝을 전부 건너뛰고도 잡 자체는 성공으로 끝나는 성질이 있습니다. “리뷰되는 것처럼 보이는데 실제로는 한 번도 안 돈” 상태가 가장 위험합니다. 그래서 리뷰가 끝난 뒤 판정 코멘트가 실제로 올라왔는지를 별도 스텝이 확인하고, 없으면 원인과 무관하게 잡을 실패로 떨어뜨립니다. 초록불이 곧 통과를 뜻하지 않도록, 초록불의 조건을 “판정 문자열의 존재”로 바꿔 둔 것입니다.

dungeon-company PR #520에 자동 리뷰가 남긴 코멘트. "판정: 보류" 제목과 보류 사유 첫 항목 전문

리뷰가 남기는 판정 코멘트입니다. 첫 줄이 판정: 보류로 고정돼 있어 목록에서 훑어도 상태가 바로 읽힙니다. 근거로 파일과 행 번호, 해당 코드 블록을 인용하고 실패 시나리오와 영향 범위까지 적습니다.

synchronize 트리거로 커밋마다 다시 돌리면 생기는 문제

pull_requestsynchronize 타입을 트리거에 넣으면 커밋을 새로 밀 때마다 최신 코드를 다시 리뷰합니다. 문제는 동시성 제어와 만날 때 생겨요. 같은 PR에 재리뷰가 연달아 오면 앞 실행을 취소하고 최신 코드만 보도록 cancel-in-progress를 걸어 두면(Using concurrency), 커밋을 밀 때마다 발화한 재리뷰가 9분 넘게 돌던 앞 리뷰를 통째로 중단시킵니다. 판정을 코앞에 둔 리뷰가 반복해서 취소되며 구독 사용량만 소모되고, 이 저장소에서는 짧은 기간에 취소된 실행이 수십 건 쌓였습니다.

그래서 이 저장소는 synchronize를 트리거에서 뺐습니다(Events that trigger workflows). 대신 커밋을 추가한 뒤의 재리뷰는 두 경로로만 돕니다. 하나는 반려 후 수정을 반영하는 자동화 단계가 반영 직후 명시적으로 재리뷰를 dispatch하는 경로, 다른 하나는 사람이 리뷰:요청 라벨을 다시 붙이는 경로예요. 리뷰가 필요한 시점은 빠짐없이 덮으면서, 취소는 “커밋마다”가 아니라 “재리뷰를 요청할 때마다”로 줄었습니다. 자동 트리거는 취소·재시도 정책과 함께 설계해야 합니다.

워크플로 파일을 수정한 PR 에서 Claude Code 액션이 스킵되는 이유

리뷰 워크플로 파일 자체를 수정하는 PR에서는 리뷰 액션이 항상 건너뛰어집니다. Claude Code 액션의 토큰 교환 보안 검증이 “실행되는 워크플로 파일이 기본 브랜치의 것과 동일할 것”을 요구하는데, 파일을 고치는 PR은 정의상 기본 브랜치와 다르기 때문입니다. 액션 스텝은 5초 남짓 만에 성공으로 끝나면서 워크플로 검증 때문에 건너뛴다는 짧은 안내만 남깁니다. 판정 코멘트의 존재를 확인하는 가드가 없으면 이 스킵도 초록불로 표시됩니다.

이건 버그가 아니라 의도된 보안 동작이라 정면으로 없앨 수 없고, 절차로 우회합니다. 워크플로 파일을 고치는 PR은 먼저 머지한 다음, workflow_dispatch로 재리뷰를 돌립니다. 수동 실행은 기본 브랜치의 파일로 돌기 때문에 검증을 통과해요. 이 저장소는 한 걸음 더 나가, 워크플로 파일을 건드린 PR에서 리뷰가 스킵으로 끝나면 자동으로 리뷰:수동 라벨을 붙이고 스스로 재리뷰를 dispatch하도록 해 두었습니다. 다만 이 자기 재실행은 최초의 pull_request 실패에서만 한 번 발화합니다. dispatch 실행이 또 실패했을 때 다시 dispatch하면 무한 루프가 되니까, 그 경우는 리뷰:수동 상태로 남겨 사람이 보게 합니다.

PR 리뷰 워크플로의 실행 목록. 대부분 3~10분대인데 48초에 끝난 실행 하나에 경고 아이콘이 붙어 있다

같은 워크플로의 실행 목록입니다. 리뷰가 실제로 돌면 3분에서 10분이 걸리는데, 48초에 끝난 실행이 섞여 있습니다. 소요 시간만 봐도 성공 표시가 곧 리뷰는 아니라는 것이 드러납니다.

자동 머지 범위: 개발 브랜치·안전 경로·테스트 통과

이 저장소는 개발 브랜치에 한해 자동 승인을 켜 두었습니다. 판정이 승인이면 액션이 dev 브랜치로 자동 머지까지 합니다. 다만 그 승인이 곧바로 모든 것을 통과시키지는 못하도록 경계를 여러 겹 그어 두었습니다.

첫째, 프로덕션 승격은 자동화하지 않습니다. dev에서 main으로 올리는 머지는 오직 사람만 누릅니다. main은 머지하는 순간 실유저가 보는 웹으로 즉시 배포되고, 되돌리려면 또 배포해야 하며 그 사이는 유저가 겪습니다. 이 한 줄이 자동화 전체의 바깥 울타리예요.

둘째, 민감한 경로는 승인이 나도 자동 머지에서 제외합니다. 결제·인앱 구매 검증·광고 설정·개인정보·확률 데이터를 건드리는 PR은 판정이 승인이어도 액션이 머지하지 않고 needs-human 라벨을 붙여 사람에게 넘깁니다. 광고 설정 파일이 자동으로 머지된 장애 뒤에 이 목록을 코드에 넣었습니다. 자동 머지 제외 경로 목록은 프로젝트 규칙 문서와 워크플로 정규식 두 곳에서 같은 값을 유지합니다.

셋째, 머지 자체는 서버 규칙이 다시 한번 막습니다. 개발 브랜치에는 PR 필수·테스트 체크 통과 필수·강제 푸시 금지가 걸린 보호 규칙이 있어서, 액션이 승인을 내려도 테스트가 초록이 아니면 머지가 성립하지 않습니다. 승인 판정과 실제 머지 사이에 기계적인 관문이 하나 더 있는 셈이죠.

정리하면 이 구성에서 자동에 맡긴 것은 “개발 브랜치에서, 안전한 경로의, 테스트를 통과한 변경”의 승인과 머지까지입니다. 그 바깥, 즉 프로덕션 승격과 민감 경로는 사람이 쥐고 있습니다. GitHub이 저장소 설정에서 “Copilot 이 승인할 수 있는 파일 경로”를 따로 고르게 한 것도 같은 방향이에요. 기능을 켜느냐 마느냐가 아니라, 켜되 어디까지를 자동의 영역으로 인정할지 경계를 먼저 정하는 문제입니다.

봇 승인을 켜기 전에 정할 것 4가지

자동 승인을 켜기 전에 정해야 하는 것은 네 가지입니다.

  • 범위를 경로로 좁힙니다. 되돌리기 쉬운 변경에만 자동 리뷰·자동 머지를 적용하고, 인증·결제·마이그레이션·워크플로 정의는 별도로 뺍니다.
  • 승인과 머지를 분리합니다. 자동 승인이 곧 자동 머지가 되지 않도록, 머지 조건에 통과된 테스트나 사람을 하나 더 남깁니다.
  • 되돌리는 경로를 먼저 만듭니다. 스위치 하나로 즉시 끌 수 있어야 하고, 끄는 방법이 문서 한 줄로 적혀 있어야 합니다.
  • 침묵을 실패로 만듭니다. 리뷰가 아무 말 없이 성공으로 끝나는 상태를 통과로 읽지 않도록, 판정의 존재 자체를 초록불의 조건으로 겁니다.

이번 GitHub의 변경에서 실무적으로 중요한 건 봇이 승인할 수 있다는 사실보다, 그 권한을 어디까지 열어 둘지 각 팀이 직접 정해야 한다는 점입니다. 기본값이 꺼짐이라는 건, 그 결정을 대신 내려 주지 않겠다는 뜻이에요.

참고 자료