본문으로 건너뛰기
마탑6F 도서관
풍경

검토 게이트와 병합: 기록에서 머지 큐까지

이슈와 명세, 결정 기록, 설계 검토, 풀 리퀘스트, 보호 브랜치, 머지 큐와 머지 트레인까지, 병합 앞의 문지기들을 원리에 따라 세우는 법을 정리한 개념 글이다.

시드 개정 1판원문 대조 2026-09-29기록 2026-09-29T00:00:00Z

고친 내용: 초판

문제: 병합 시점에 터지는 일

각자 통과한 변경이 합쳐지는 순간 깨지는 일이 있다. 따로 보면 멀쩡한 두 제안이 합쳐지면 약속이 어긋나고, 기본 브랜치가 깨진다. 문제는 각자의 검사가 틀렸다는 것이 아니라, 합친 상태를 아무도 보지 않았다는 데 있다.

또 다른 문제는 책임의 공백이다. 누가 왜 그렇게 정했는지가 기록에 없으면, 나중에 같은 논쟁이 되풀이되고, 모르는 사이에 결정이 뒤집힌다. 이 글은 병합 앞의 문지기를 두 축으로 정리한다. 왜 그렇게 정했는지를 남기는 기록의 축과, 합친 상태가 도는지를 확인하는 병합의 축이다.

병합 앞 문지기를 상상한 개념도개념 편집 일러스트 · 실제 화면·공식 구조도 아님마탑 편집부가 만든 개념 편집 일러스트다. 병합 앞의 문지기들(점검표·결정 장부·펼친 검토서·줄 선 제안 묶음)을 도서관 홀의 네 아치로 빗댄 그림이며, 실제 코드 호스팅 화면이나 공식 구조도가 아니다.마탑 편집부 생성 개념도 · 편집부 일러스트(실제 화면 아님)근거 S31 · S32
편집부 생성 개념 일러스트. 도서관 홀에 네 석조 아치가 늘어서 있다. 왼쪽 아치 아래 점검표 두루마리, 옆 아치 아래 도장 찍힌 결정 장부, 가운데 아치 아래 돋보기가 얹힌 펼친 책, 오른쪽 아치 아래 줄지어 선 상자·책 무더기. 바닥에 화살표 선이 이어진다. 실제 제품 화면이나 공식 구조도가 아니다.

게이트의 층위: 기록부터 병합까지

문지기는 한 겹이 아니라 여러 겹이다. 이슈는 할 일의 제기, 명세는 만들 것의 약속, 결정 기록은 큰 선택의 남기기, 설계 검토는 병합 전의 방향 점검, 풀 리퀘스트는 변경의 제안과 논의, 보호 브랜치는 자동 조건의 강제, 머지 큐와 트레인은 합친 상태의 확인이다. 각 겹은 서로 다른 질문에 답한다.

층을 나누는 이유는 실패의 자리마다 다른 문지기가 서야 하기 때문이다. 방향의 실패는 설계 검토가, 약속의 실패는 자동 검사가, 합침의 실패는 큐와 트레인이 맡는다. 한 겹을 두껍게 한다고 다른 겹이 대신되지 않는다. 이 구분은 이 글의 편집자 관점이다.

병합까지의 문지기 흐름제기는 이슈에, 약속은 명세에 적는다. 큰 건축 선택만 결정 기록에 남기고(선택), 두 길 모두 풀 리퀘스트로 간다. 보호 브랜치가 자동 조건을 강제하고, 큐가 합친 상태로 검사를 돌린 뒤 기본 브랜치에 병합한다. 이 흐름은 이 글의 예시 팀이 정한 길이며, 모든 팀이 밟는 보편 절차가 아니다. 각 상자는 다른 실패를 맡는다.근거 S31 · S32 · S33 · S34 · S40
일곱 상자를 잇는 흐름도. 1 이슈 제기가 명세로 간다(약속 적기). 2 명세가 풀 리퀘스트 제안으로 간다(바로 제안하기). 3 명세가 결정 기록(선택)으로 간다(큰 선택만 남기기). 4 결정 기록(선택)이 풀 리퀘스트 제안으로 간다(기록 뒤 제안하기). 5 풀 리퀘스트 제안(정식·소유자 승인)이 보호 브랜치로 간다(승인·조건 달기). 6 보호 브랜치(검사·대화 해결)가 머지 줄로 간다(줄 세우기). 7 머지 줄(합친 검사)이 기본 브랜치 병합으로 간다(병합하기).약속 적기바로 제안하기큰 선택만남기기(선택)기록 뒤 제안하기승인·조건 달기줄 세우기병합하기이슈 제기할 일·Done의 뜻명세기대 동작·받아들임·경계결정 기록(선택)큰 건축 선택만 남기기풀 리퀘스트정식 제안·소유자 승인보호 브랜치검사·대화 해결 강제머지 줄합친 상태 검사기본 브랜치 병합묶음 병합
그림을 글로 읽기

일곱 상자를 잇는 흐름도. 1 이슈 제기가 명세로 간다(약속 적기). 2 명세가 풀 리퀘스트 제안으로 간다(바로 제안하기). 3 명세가 결정 기록(선택)으로 간다(큰 선택만 남기기). 4 결정 기록(선택)이 풀 리퀘스트 제안으로 간다(기록 뒤 제안하기). 5 풀 리퀘스트 제안(정식·소유자 승인)이 보호 브랜치로 간다(승인·조건 달기). 6 보호 브랜치(검사·대화 해결)가 머지 줄로 간다(줄 세우기). 7 머지 줄(합친 검사)이 기본 브랜치 병합으로 간다(병합하기).

결정 기록: 왜를 남긴다

결정 기록(ADR)은 지을 것의 큰 선택을 적는 문서다. 뼈대는 맥락과 결정 자체, 그리고 그 결정이 가져오는 결과다. 이 뼈대의 힘은 어떻게 지었는지가 아니라 왜 그렇게 정했는지를 붙잡는 데 있다. 이유가 남으면 나중에 온 사람도 결정을 따르고, 논의에 없던 사람이 함부로 뒤집지 못한다.

절차도 정해져 있다. 누구나 쓸 수 있지만 소유자를 정하고, 쓰는 일은 공유 서식에 맞춘다. 처음 상태는 Proposed이며, 검토의 끝은 받아들임과 손질, 혹은 거부다. 검토 모임은 읽는 시간부터 시작하며 평균 10분에서 15분이면 충분하다고 안내는 적는다. 받아들인 기록은 불변이 된다. 생각이 바뀌면 옛 기록을 고치지 않고 새 기록을 써서 대체하며, 옛 기록의 상태는 Superseded로 바뀐다. 거부된 기록도 이유를 남기고 Rejected로 둔다.

이 절의 근거 S31

결정 기록이 받아들여지기까지소유자가 Proposed로 내면 팀이 읽고 논한다. 손질이면 다시 Proposed, 거부면 이유를 남기고 Rejected, 받아들이면 Accepted로 두고 불변으로 잠근다.근거 S31
  1. Proposed로 내기

    소유자가 공유 서식에 맞춰 맥락·결정·결과를 적어 검토에 올린다.

  2. 읽고 논하기

    모임 앞 10분에서 15분은 읽는 시간으로 두고, 각자 unclear한 곳에 말을 단다.

  3. 손질·거부 가르기

    손질할 점이 있으면 Proposed에 머물고, 거부면 이유를 남기고 Rejected로 둔다.

  4. 받아들여 잠그기

    승인하면 시각·판·이해관계자를 적고 Accepted로 두며 이후 불변으로 둔다.

  5. 바뀌면 새로 쓰기

    생각이 바뀌면 옛 기록을 고치지 않고 새 기록을 써서 Superseded로 잇는다.

단계를 글로 읽기

다섯 단계의 순서. proposed 내기, 읽고 논하기, 손질 또는 거부 가르기, 받아들여 잠그기, 바뀌면 새 기록으로 대체하기.

명세: 무엇을 만들지를 적는다

명세(specification)는 이번 일에서 만들 것의 약속을 적는 문서다. 겉으로 드러나는 기대 동작(어떤 입력에 어떻게 반응하는지), 일을 마쳤다고 말할 수 있는 받아들임 기준(acceptance criteria), 그리고 이번 일의 경계(하지 않기로 한 것과 다음으로 미룬 것)가 뼈대다. 명세가 있으면 검토자는 코드의 모양이 아니라 약속의 충족을 묻고, 만든 이는 목표가 바뀌었을 때 바뀜을 명세의 바뀜으로 보여준다.

팀의 검토는 명세를 함께 읽는 일부터 시작한다. 기준이 모호하거나 경계가 비어 있으면 코드를 읽기 전에 명세로 돌려보낸다. 공개된 명세 서식의 한 보기(Spec Kit의 명세 서식)는 사용자 시나리오와 독립된 테스트, Given/When/Then 받아들임 서술, 경계 사례, 기능 요구, 성공 기준, 범위 경계를 포함한 가정을 적는 칸을 둔다. 이는 명세가 담을 수 있는 항목의 보기로 읽으며, 모든 팀이 같은 서식을 써야 한다는 뜻이 아니다. 명세가 무엇을 이룰지를 정하는 자리라면, 결정 기록(ADR)은 이미 내린 큰 건축 선택의 왜를 남기는 글이며, 설계 검토와 풀 리퀘스트 검토는 그 약속을 어떻게 이룰지를 따지는 자리이다. 무엇을—왜—어떻게의 순서로 읽히지만, 이 순서는 이 글의 예시 팀이 고른 길이지 모든 팀의 보편 규칙은 아니다.

이 절의 근거 S40 · S31 · S32

명세·결정 기록·풀 리퀘스트의 맡은 일세 문서는 서로 다른 질문에 답한다. 명세는 무엇을 만들지를, 결정 기록은 왜 그렇게 정했는지를, 풀 리퀘스트는 그 약속을 어떻게 이룰지를 따진다. 무엇을—왜—어떻게의 순서는 이 글의 예시 팀이 고른 길이지 모든 팀의 보편 규칙은 아니다.근거 S40 · S31 · S32
문서묻는 것뼈대
명세이번 일에서 무엇을 만들지기대 동작·받아들임 기준·이번 일의 경계
결정 기록큰 선택을 왜 그렇게 정했는지맥락·결정·결과, 받아들이면 불변
풀 리퀘스트약속을 어떻게 이룰지변경 제안·대화·검사·바뀐 파일의 논의
표를 글로 읽기

세 행의 표. 명세는 기대 동작과 받아들임 기준·경계로 무엇을 만들지를 적고, 결정 기록은 맥락·결정·결과로 큰 건축 선택의 왜를 남기며, 풀 리퀘스트는 변경 제안과 논의로 약속을 어떻게 이룰지를 따진다.

풀 리퀘스트: 제안과 논의의 자리

풀 리퀘스트는 코드 변경의 병합 제안이며, 병합 전에 논의하고 검토하는 협력의 자리다. 대화와 커밋, 검사, 바뀐 파일이 탭으로 나뉘어 검토에 필요한 맥락을 한데 모으고, 병합 상태는 막힘과 빠진 승인을 따로 보여준다.

초안은 일의 공유와 정식 검토 요청을 가르는 장치다. 초안은 병합할 수 없으며 코드 소유자에게 자동으로 검토를 청하지도 않는다. 검토를 받을 준비가 되면 정식으로 바꾸어 소유자의 검토를 청한다. 병합의 평가를 위해 임시 잣대와 합친 결과의 흉내를 두는 일은 뒤에서 돌며, 자동화와 디버깅의 재료가 된다.

이 절의 근거 S32

보호 브랜치: 자동 조건의 강제

보호 브랜치는 중요한 브랜치에 거는 규칙이다. 강제 푸시와 삭제를 기본으로 막고, 병합 전의 조건을 건다. 조건의 목록은 승인 수, 상태 검사, 대화 해결, 서명된 커밋, 선형 기록, 머지 큐, 배포 성공 등이다.

운용의 결은 세 가지다. 첫째, 승인 수와 코드 소유자를 함께 걸 수 있다. 코드 소유자가 있는 코드는 그 소유자의 승인이 있어야 병합한다. 둘째, 새 커밋이 오르면 낡은 승인을 기각하는 선택이 있다. 검토한 내용과 달라지면 다시 승인받게 하는 것이다. 셋째, 한 번에 적용되는 규칙은 하나뿐이다. 여러 규칙이 겹치면 어느 것이 적용될지 알기 어려우므로, 규칙 모음으로 옮기는 길을 문서는 안내한다.

이 절의 근거 S33

머지 큐와 머지 트레인: 합친 상태를 본다

GitHub의 머지 큐는 바쁜 브랜치의 속도를 위한 장치다. 요구 검사를 마친 제안을 줄에 세우고, 목표 브랜치의 최신과 줄 앞의 변경을 합친 상태로 검사를 돌린다. GitHub Actions로 요구 검사를 구현한 흐름은 merge_group 사건을 추가로 걸어야 하며, 빠뜨리면 검사가 보고되지 않아 병합이 실패한다. 제삼자 CI는 gh-readonly-queue/로 시작하는 임시 브랜치에 밀려 올라오는 커밋에 반응하게 맞춘다. 묶음의 병합 방식과 동시성 범위, 최소와 최대 묶음 수와 대기 시간, 검사 타임아웃은 설정으로 고른다.

GitLab의 머지 트레인은 같은 문제를 다른 모양으로 푼다. 각 제안을 그 앞에 선 모든 제안과 합친 상태로 검사하며, 파이프라인은 병렬로 돈다. 앞 제안이 빠지면 뒤의 파이프라인은 취소되고 합친 상태를 다시 짜서 돌린다. 트레인은 유료 단계의 기능이며, 급한 고침을 위한 즉시 병합 선택이 따로 있지만 그 변경은 줄의 다른 제안과 견주지 않은 것이므로 위험을 안는다. 두 도구의 고정된 단계 수나 앞지르기 금지를 보편 동작으로 읽으면 안 된다. 그런 운영은 각 팀의 정책이다.

이 절의 근거 S34 · S35

머지 큐와 머지 트레인의 다른 모양같은 문제를 다른 모양으로 푼다. GitHub 머지 큐는 줄에 세워 합친 상태로 검사를 돌리고, GitLab 머지 트레인은 각 제안을 앞선 모든 제안과 합친 상태로 병렬 파이프라인을 돌린다. 트레인은 유료 단계의 기능이며, 급한 고침의 즉시 병합은 줄과 견주지 않으므로 위험을 안는다. 고정된 단계 수나 앞지르기 금지를 보편 동작으로 읽으면 안 된다.근거 S34 · S35
도구합친 검사알아둘 점
GitHub 머지 큐줄에 세워 목표 가지 최신과 줄 앞 변경을 합쳐 검사요구 검사가 Actions면 merge_group 사건 필수, 빠뜨리면 미보고로 실패
GitLab 머지 트레인각 제안을 앞선 모든 제안과 합쳐 병렬 파이프라인앞 제안이 빠지면 뒤는 취소·재생성, 즉시 병합은 줄과 견주지 않아 위험
표를 글로 읽기

두 행의 표. GitHub 머지 큐는 줄에 세워 목표 가지 최신과 줄 앞 변경을 합친 상태로 검사를 돌리고, GitLab 머지 트레인은 각 제안을 앞선 모든 제안과 합친 상태로 병렬 파이프라인을 돌리며 앞 제안이 빠지면 뒤를 취소하고 다시 짠다.

따라가기: 한 팀의 예시 게이트

아래는 실행하지 않은 가상의 한 팀 예시이며, 이 팀이 정한 정책이지 보편 규칙이 아니다. 이 팀의 제안은 다음 문을 순서대로 지난다. 먼저 이슈에 할 일과 Done의 뜻을 적는다. 이어서 명세에 기대 동작과 받아들임 기준, 이번 일의 경계를 적고 함께 읽는다. 큰 선택이 있으면 결정 기록을 Proposed로 내고 15분 읽기로 검토한다. 병합 전에는 설계의 방향을 한 번 살핀다. 풀 리퀘스트는 정식으로 내고 코드 소유자의 승인을 받으며, 새 커밋에는 승인을 다시 받는다.

자동 조건으로는 상태 검사와 대화 해결을 걸고, 바쁜 기본 브랜치에는 머지 큐를 건다. 급한 고침은 줄의 맨 앞으로 올리는 예외로만 다루며, 두 사람의 합의가 있을 때만 쓴다. 맨 앞으로 올리면 뒤에 선 항목들은 순서가 밀려 합친 검사를 다시 돌리므로, 앞지르기는 팀의 예외 기록에 남긴다. 단계의 수와 순서, 예외의 조건은 모두 이 팀의 선택이다. 다른 팀은 다르게 정해도 되며, 다르게 정했다면 그 이유를 결정 기록에 남긴다.

실패 양상과 한계

게이트의 실패는 대개 기다림과 멈춤의 모양이다. 큐가 길어지면 병합까지의 기다림이 늘고, 검사 타임아웃이 짧으면 멀쩡한 제안이 떨어지며, 흐름에 사건 걸기를 빠뜨리면 검사가 보고되지 않아 줄이 멈춘다. 트레인에서는 앞 제안의 실패가 뒤 전체의 파이프라인을 갈아엎어 자원을 쓴다.

한계도 분명하다. 게이트는 합친 상태가 도는지를 보지, 합친 상태가 옳은지를 보지 않는다. 방향의 옳음은 기록과 검토의 몫이다. 그리고 게이트가 촘촘할수록 우회로의 유혹이 큰데, 우회는 게이트의 실패가 아니라 운영의 실패다. 예외의 조건을 미리 정해두는 일이 우회를 막는다.

이 절의 근거 S34 · S35

측정과 검증 계획

게이트를 두는 팀은 다음을 잰다. 제안부터 병합까지의 기다림, 게이트에서 떨어진 비율과 그 이유, 멈춘 줄의 수와 풀리는 데 걸린 시간, 그리고 병합 뒤 기본 브랜치가 깨진 수다. 특히 병합 뒤 깨짐은 게이트 전체의 최종 점수다. 그 수가 줄지 않으면 게이트가 두꺼워진 것이지 좋아진 것이 아니다.

검증의 또 다른 축은 기록의 읽기다. 결정 기록이 실제로 읽히는지, Superseded의 사슬이 끊기지 않는지를 본다. 읽히지 않는 기록은 게이트가 아니라 장식이다.

결론: 이유를 남기고, 합친 것을 본다

이 글의 원칙은 두 마디다. 왜 그렇게 정했는지를 기록에 남기고, 합친 상태가 도는지를 줄과 파이프라인으로 확인한다. 기록 없는 병합은 빠르지만 되풀이되고, 확인 없는 병합은 조용하지만 깨진다.

어떤 단계로 세울지는 팀의 정책이다. 서가가 줄 수 있는 것은 단계가 아니라 문지기의 원리뿐이다. 영향 범위의 테스트는 도메인 경계 글에, 밤사이 도는 일의 맡기기는 예약 실행 글에 있으니 함께 읽기를 권한다.

현재 판 출처 (이 개정의 출처 보관본 아님) 6건

아래는 현재 판의 출처 목록을 고리 풀이용으로 그대로 둔 참조이며, 이 개정의 출처 보관본이 아니다.

  1. AWS Prescriptive Guidance: Architectural decision record process (외부)
    S31
  2. GitHub: About pull requests (외부)
    S32
  3. GitHub: About protected branches (외부)
    S33
  4. GitHub: Managing a merge queue (외부)
    S34
  5. GitLab: Merge trains (외부)
    S35
  6. Spec Kit: Specification Template (외부)
    S40
← 고침 기록으로