GitHub 비동기 병합, 202 응답 뒤 언제 완료로 처리할까?
비동기 병합 API가 202를 돌려줬고 PR도 이미 병합됐는데, 봇이 조회하는 결과는 계속 enqueued다. 배포 봇은 언제를 병합 완료로 보고 다음 단계를 시작해야 할까?

- 비동기 병합 API의 202는 접수 응답이다. 반환된 UUID로 처리 결과를 조회해야 한다.
- enqueued는 큐 진입이며, 같은 UUID의 결과가 나중에 merged로 바뀌지 않는다.
- 큐에 넣은 PR은 별도의 병합 여부 조회로 추적하고, 실제 병합 뒤에 후속 작업을 시작해야 한다.
202를 받았다면 요청 ID부터 보관한다
GitHub 병합 봇이 HTTP 202를 받았다고 배포를 시작해서는 안 된다. 10월 1일 정식 제공된 비동기 병합 API는 작업을 백그라운드로 넘긴다. 새 요청을 받았다는 응답과 PR을 실제로 병합했다는 결과가 분리된다.
먼저 PUT /repos/OWNER/REPO/pulls/NUMBER/merge-async로 요청하고, 응답의 details.uuid를 보관한다. 이어 GET /repos/OWNER/REPO/pulls/NUMBER/merge-async/UUID로 결과를 조회한다. GitHub는 이 API를 프로그램에서 PR을 병합하는 권장 경로로 제시한다.
여기서 enqueued가 나오면 조회 대상을 바꿔야 한다. 공식 결과 조회 문서에 따르면 enqueued는 큐에 넣는 요청의 최종 결과다. 큐가 나중에 PR을 병합해도 해당 UUID의 결과는 바뀌지 않는다.
HTTP 성공보다 응답 본문의 상태가 중요하다
비동기 요청의 상태는 처리 중인 pending과 세 가지 최종 결과로 나뉜다. 봇은 각 상태에 따라 다음 작업을 달리해야 한다.
| 상태 | 뜻 | 자동화의 다음 처리 |
|---|---|---|
pending | 백그라운드 처리 중이다. | 같은 UUID로 결과를 다시 조회한다. |
merged | 대상 브랜치에 병합됐다. | 반환된 병합 커밋 식별자를 기록하고 후속 작업으로 넘어간다. |
enqueued | 병합 큐에 들어갔다. | PR 자체의 병합 여부를 별도로 추적한다. |
failed | 요청이 실패했다. | 실패 메시지를 확인하고 원인을 해결한다. |
큐에 들어간 뒤에는 PR 병합 여부 조회인 GET /repos/OWNER/REPO/pulls/NUMBER/merge를 사용할 수 있다. 병합됐다면 204를 반환한다. 이 엔드포인트의 미병합 응답은 404지만, 잘못된 경로나 저장소 접근 문제로 생긴 404까지 미병합 상태로 간주해서는 안 된다.
예를 들어 PR을 큐에 넣자마자 “배포 준비 완료” 알림을 보내는 봇이라면, 그 알림 조건을 실제 병합 확인 뒤로 옮겨야 한다. 큐 진입은 이후 검사를 받을 자리를 얻은 것이며, 대상 브랜치에 코드가 반영됐다는 뜻이 아니다.
409가 오면 새 요청을 반복하기 전에 기존 UUID를 본다
같은 PR에 비동기 병합 요청이 이미 진행 중이면 409와 함께 기존 요청의 UUID·병합 옵션을 반환한다. 이 경우에는 응답에 든 요청을 조회하는 쪽으로 이어가야 한다. 이미 병합됐거나 큐에 있는 PR에는 200을 즉시 반환할 수 있으므로 이때도 본문의 merged와 enqueued를 구별한다.
요청 결과는 가장 최근 갱신 후 24시간 보관된다. 그 시간이 지나면 UUID 조회가 404를 반환한다. 다음 날 다시 시작한 작업에서 과거 UUID를 못 찾았다고 병합이 취소됐다고 결론 내릴 수는 없다. PR의 현재 상태를 확인하고 이어갈 작업을 결정해야 한다.
새 커밋도 결과를 바꾼다. 요청에 sha를 생략하면 요청 당시 PR의 HEAD를 기준으로 삼으며, 요청과 실행 사이에 새 커밋이 푸시되면 병합을 취소한다. 접수 직후에는 기본 PR 상태만 검사하고 브랜치 보호·저장소 규칙은 실제 처리 때 평가하므로, 접수 뒤 실패하는 흐름 자체는 정상적으로 처리할 수 있어야 한다.
스택에서는 선택한 PR 아래의 열린 PR도 함께 대상이 된다
GitHub의 스택 API 설명은 스택 PR 병합에 비동기 API를 사용하도록 요구한다. 선택한 PR과 그 아래에서 연결된 열린 PR들이 작업 대상에 포함된다. 그룹은 함께 병합되거나 함께 큐에 들어가며, 그렇지 않으면 어느 것도 처리되지 않는 방식이다.
merge_action의 기본값인 default는 대상 브랜치에 큐가 있으면 큐를 사용하고, 없으면 직접 병합한다. 따라서 같은 요청 코드를 쓰더라도 저장소 설정에 따라 최종 결과가 merged일 수도, enqueued일 수도 있다.
큐에 들어간 뒤에는 병합 그룹의 필수 검사가 실패해 PR이 제거될 수 있다. 봇의 완료 조건은 “API가 요청을 받았다”에서 끝내지 않고, “대상 PR이 실제로 병합됐다”까지 이어져야 한다.