20장. CI② — AI 코드리뷰를 파이프라인에 넣는다

이 장이 끝나면: PR을 올리면 AI가 자동으로 코드를 리뷰해 코멘트를 다는 파이프라인이 선다. 사람이 "리뷰해줘"라고 부르지 않아도, 모든 변경이 리뷰 대상이 된다. 그리고 자동 리뷰의 한계 — 무엇을 맡기고 무엇은 사람이 봐야 하는지 — 를 분별하는 기준을 얻는다.


20.1 개념 — 검증을 사람의 기억에서 떼어낸다

코드리뷰어의 마지막 약점

코드를 검증할 때 코드리뷰어 서브에이전트(code-reviewer)를 만들었다. 훌륭한 리뷰어지만 약점이 하나 있었다 — 사람이 불러야 일한다. "code-reviewer한테 맡겨줘"라고 말하지 않으면, 리뷰는 일어나지 않는다. 바쁘면 건너뛰고, 급하면 잊는다. 가장 리뷰가 필요한 순간(마감에 쫓겨 급히 짠 코드)에 가장 리뷰를 건너뛰기 쉽다.

이 약점을 조금 더 깊이 들여다보자. 리뷰어의 실력에는 아무 문제가 없다. 경계 위반도 잡고, 명세 불일치도 잡고, 위험한 패턴도 잡는다. 문제는 실력이 아니라 가동 조건이다 — 사람의 호출이라는 방아쇠가 당겨져야만 움직인다. 그런데 사람의 호출은 사람의 기억과 성실함에 의존한다. 그리고 사람의 기억과 성실함은, 하필 가장 필요한 순간에 가장 약해진다. 마감 3분 전, 데모 직전, 금요일 저녁의 급한 수정 — 리뷰가 가장 절실한 그 코드가 리뷰 없이 병합된다. "평소엔 잘 지키다가 결정적일 때 놓친다"는 이 구조가, 사람의 규율에만 기대는 모든 점검이 공통으로 가진 함정이다.

GitHub Actions로 테스트에 해 준 것을, 이제 리뷰에 해 준다. PR이 올라오면 자동으로 리뷰가 돌게 만든다. 사람의 기억과 성실함에서 검증을 떼어내는 것 — 이 과정이 일관되게 추구해 온 방향의 완성이다.

"검증을 사람의 기억에서 떼어낸다"는 이 한 문장이 이 장의 심장이다. 표현을 바꿔 보면 이렇다. 지금까지 리뷰는 사람의 의지에 걸려 있었다 — "기억나면, 시간 되면, 마음먹으면" 돈다. 이제 리뷰를 구조에 건다 — "PR이 올라오면 무조건" 돈다. 의지는 흔들리지만 구조는 흔들리지 않는다. 좋은 품질 관리는 사람에게 "더 성실해지라"고 요구하지 않는다. 성실하지 않아도 빠지지 않는 자리에 점검을 옮겨 둔다. 이것이 개인의 습관을 팀의 시스템으로 바꾸는 일이다. 신입이 들어와도, 베테랑이 마감에 쫓겨도, 리뷰는 똑같이 돈다.

[짚고 가기] 자기 경험을 떠올려 보면 이 장의 이야기가 금방 와닿는다. "급할 때 테스트 안 돌리고 커밋한 적 있나?" 아마 대부분 그런 적이 있을 것이다. 바로 그 순간이 리뷰가 가장 필요한 순간이다. 이 장의 자동화는 '더 부지런한 사람이 되자'가 아니라 '부지런하지 않아도 되게 만들자'라는 방향이다. 이 점을 먼저 잡아 두면, 뒤에서 '사람의 몫'을 다룰 때 "그럼 사람은 놀아도 되나"라는 오해에 빠지지 않는다.

자동 테스트와 자동 리뷰는 보는 것이 다르다

둘 다 PR에서 자동으로 돌지만, 잡는 것이 다르다. 그래서 둘 다 필요하다.

자동 테스트 (CI의 테스트 관문) 자동 코드리뷰 (이 장)
판정 방식 기계적 (통과/실패가 명확) 평가 중심 (좋다/나쁘다에 근거)
잘 잡는 것 로직이 틀렸다 (계산 오류 등) 경계 위반, 위험한 패턴, 나쁜 구조
못 잡는 것 "돌아가지만 위험한 코드" "미묘한 계산 버그"(테스트가 맡음)
결과 초록/빨강 PR에 남는 코멘트(등급·근거)

테스트는 "틀렸나?"를, 리뷰는 "잘 썼나?"를 본다. 테스트를 통과하면서도 경계를 위반한 코드(예: web/에 몰래 들어간 금액 계산)는 테스트로는 안 잡히고 리뷰가 잡는다. 반대도 마찬가지다.

이 차이를 조금 더 풀어 보자. 테스트는 '정답'이 있는 질문에 답한다. "티켓 3장에 각 12,000원이면 합계가 36,000원인가?" 이건 맞거나 틀리거나 둘 중 하나다. 사람의 취향이 끼어들 여지가 없다. 그래서 기계가 초록/빨강으로 딱 잘라 판정할 수 있다. 반면 리뷰는 '정답'이 없는 질문에 의견을 낸다. "이 금액 계산을 화면(web/) 쪽에 두는 게 맞나?" 이건 실행해서 재현되는 '오답'이 아니다. 지금 당장은 돌아간다. 다만 우리가 정한 경계(계산은 api/에서)를 어겼고, 그래서 나중에 화면과 서버의 금액이 어긋날 위험을 심었다. 코드가 '틀린' 게 아니라 '잘못 놓인' 것이다. 이런 건 실행으로는 드러나지 않는다. 읽고 판단해야 드러난다. 그 판단을 하는 것이 리뷰다.

비유하면 이렇다. 테스트는 맞춤법 검사기다 — 철자가 틀렸으면 빨간 줄이 뜬다, 명확하게. 리뷰는 글을 읽어 주는 편집자다 — 철자는 다 맞는데 "이 문단은 논리가 어긋난다", "이 표현은 독자가 오해하게 만든다"고 짚는다. 맞춤법 검사기가 통과시킨 글도 편집자는 되돌려 보낼 수 있다. 둘은 서로를 대체하지 않는다. 맞춤법 검사기가 있다고 편집자가 필요 없어지지 않고, 편집자가 있다고 맞춤법 검사기를 끄지 않는다. 코드도 같다 — 그래서 테스트와 리뷰를 둘 다 파이프라인에 건다.

한 가지 오해를 미리 풀어 두자. "리뷰가 더 똑똑하니 테스트는 없어도 되지 않나?"라고 생각하기 쉽다. 아니다. 리뷰는 '읽어서 판단'하기 때문에, 눈으로 읽어서는 도저히 알 수 없는 미묘한 계산 버그를 놓친다. 소수점 반올림이 1원 어긋나는 오류, 특정 조건에서만 음수가 되는 잔여석 — 이런 건 코드를 아무리 읽어도 안 보인다. 실제로 값을 넣어 돌려 봐야(테스트) 드러난다. 반대로 테스트는 '정해진 입력에 정해진 출력'만 확인하므로, 아무도 테스트를 짜 두지 않은 위험한 패턴은 그냥 통과시킨다. 둘이 서로의 사각지대를 정확히 메운다 — 이것이 둘 다 거는 진짜 이유다.

[짚고 가기] 위 표의 '못 잡는 것' 행이 이 절의 핵심이다. "AI 리뷰가 있으면 테스트는 대충 해도 되지 않나"라고 은근히 기대하기 쉽다. 하지만 여기서 확실히 끊어 두자 — 리뷰가 아무리 똑똑해도 눈으로 읽어서는 반올림 1원 오차를 못 잡는다. CI에 세워 둔 자동 테스트와 이 장의 자동 리뷰는 경쟁 관계가 아니라 분업 관계다. 이렇게 이해하면 파이프라인이 '겹겹의 그물'로 보인다. 그물코가 다른 두 그물을 겹쳐야 다 걸러진다.


20.2 용어 정리

이 장에서 처음 만나는(또는 새 맥락으로 다시 만나는) 용어들이다. 개념만 확실히 잡으면 실습은 지시 몇 줄로 끝난다. 완벽히 외울 필요는 없고, "이런 게 있구나" 정도로 눈에 익혀 두면 된다.

파이프라인에서 AI를 부르는 두 가지 방식

CI 안에서 AI 리뷰를 돌리는 방법은 크게 둘이다. 각각의 성격을 알아 두자.

방식 원리 성격
기성 액션(Action) GitHub Marketplace에 공개된 AI 리뷰 액션을 워크플로에 끼워 넣는다 설정이 간단, 동작이 정해져 있음
Claude Code를 CI에서 실행 워크플로 스텝에서 Claude Code를 비대화형으로 돌려 우리 리뷰어로 리뷰시킨다 우리 규칙(경계·명세)을 그대로 적용

먼저 액션(Action) 이라는 말부터 풀자. GitHub Actions에서 '액션'은 워크플로 안에 끼워 넣어 쓰는 재사용 가능한 작업 부품이다. "코드 내려받기", "Node 설치하기"처럼 누구나 반복하는 일을, 남이 미리 만들어 공개해 둔 부품을 가져다 한 줄로 불러 쓰는 것이다. 콘센트에 꽂는 플러그를 떠올리면 된다 — 안에서 무슨 일이 벌어지는지 몰라도, 규격만 맞으면 꽂아 쓴다. GitHub Marketplace 는 그런 부품들이 진열된 장터로, "AI 코드리뷰"를 검색하면 이미 누군가 만들어 둔 리뷰 액션이 여럿 나온다.

기성 액션 방식은 그 진열된 부품 중 하나를 골라 워크플로에 꽂는 것이다. 설정이 간단하다는 게 장점이다. 하지만 그 액션이 무엇을, 어떤 기준으로 리뷰하는지는 만든 사람이 정해 둔 대로다. 온티켓의 경계(계산은 api/에서, 화면은 web/에서)나 우리 명세를 그 액션은 알지 못한다. 남의 규칙으로 우리 코드를 보는 셈이다.

Claude Code를 CI에서 실행 하는 방식은 다르다. 워크플로의 한 스텝에서 Claude Code 자체를 돌리고, "우리가 만든 code-reviewer 역할로 이 diff를 리뷰하라"고 시킨다. 그러면 우리가 앞서 온티켓 전용으로 벼려 둔 그 리뷰어 — 경계를 알고, 명세를 알고, 우리 프로젝트의 사정을 아는 —가 CI 안에서 그대로 일한다.

두 번째가 이 과정의 정신에 맞다 — 우리는 이미 온티켓 전용 리뷰어(경계·명세를 아는)를 만들었으므로, 그 리뷰어를 파이프라인에서 재사용하는 것이다. 같은 역할 정의가 대화에서든 CI에서든 일한다.

이 '재사용'의 의미를 놓치지 말자. 우리가 code-reviewer를 다듬느라 들인 시간이 CI에서 한 번 더 값을 한다. 대화 중에 "리뷰해줘"라고 부를 때도 그 리뷰어가 오고, PR이 올라와 파이프라인이 돌 때도 똑같은 리뷰어가 온다. 역할을 한 번 잘 정의해 두면 그것이 사람의 호출로도, 기계의 자동 실행으로도 불려 나온다. 우리 팀의 리뷰어 한 명이 두 개의 문(대화, CI)에서 동시에 근무하는 셈이다.

[짚고 가기] "그냥 Marketplace 액션 하나 꽂으면 편한데 왜 굳이 Claude Code를 돌리지?" 싶을 수 있다. 기성 액션은 '일반적인 좋은 코드'를 보지만, 우리에게 필요한 건 '온티켓의 규칙을 아는 리뷰'다. web/에 금액 계산이 들어간 걸 일반 액션은 문제 삼지 않을 수 있다(문법적으로 멀쩡하니까). 우리 리뷰어만이 그걸 경계 위반으로 잡는다. 앞서 만든 리뷰어가 '한 번 만들어 두고두고 쓰는 자산'이라는 점을 떠올리면, 앞 장들의 수고가 왜 값진지 이어진다.

비대화형 실행 (headless)

지금까지 Claude Code는 우리와 대화하며 일했다. CI에는 사람이 없으므로, 질문 없이 한 번에 실행되고 결과를 내는 방식으로 돈다. 프롬프트를 주면 스스로 판단해 작업하고 결과(리뷰 코멘트)를 남긴 뒤 종료한다. 퍼미션을 물을 사람이 없으니, 무엇을 할 수 있는지 미리 좁혀 주는 것이 중요하다 — 리뷰는 읽기만 하면 되므로 편집 권한을 줄 이유가 없다(최소 권한 원칙).

'헤드리스(headless)'라는 말을 짚어 두자. 직역하면 '머리가 없는'인데, 여기서 '머리'는 사람이 보고 만지는 화면·대화창을 가리킨다. 사람이 앉아 묻고 답하는 인터페이스가 없는, 즉 눈에 보이는 창 없이 뒤에서 알아서 도는 실행을 헤드리스라고 부른다. 서버에서 브라우저를 화면 없이 돌리는 것도 '헤드리스 브라우저'라 하는데 같은 뜻이다. CI는 사람 없는 서버 안에서 돌기 때문에, Claude Code도 대화창 없이 헤드리스로 돌아야 한다.

여기서 핵심 성질 차이가 하나 있다. 대화형에서는 Claude Code가 위험한 일을 하기 전에 멈춰 퍼미션을 요청하고, 사람이 읽고 승인한다. 그런데 CI에는 멈춰 서서 물어볼 사람이 아무도 없다. 새벽 3시에 PR이 올라와도 파이프라인은 혼자 돌아야 한다. 그래서 대화형에서 사람이 실시간으로 하던 통제 — "이건 하고 저건 하지 마라" —를, 헤드리스에서는 일을 시작하기 전에 미리 울타리로 쳐 두어야 한다. 사람이 그 자리에 없으니, 사람의 판단을 사전 설정으로 대신 심어 두는 것이다.

그 울타리가 바로 최소 권한 원칙이다. 리뷰어가 할 일은 코드를 읽고 코멘트를 다는 것뿐이다. 코드를 고칠 일이 없다. 그러니 편집 권한을 애초에 주지 않는다. 준다면? 헤드리스라 아무도 안 보는 사이에 리뷰어가 코드를 멋대로 바꿔 놓을 수 있다 — 그것도 사람의 승인 없이. 권한을 '읽기'로 좁혀 두면 그런 사고 자체가 불가능해진다. 줄 수 있는 최소한만 주는 것이, 사람이 지켜보지 못하는 자동 실행에서 특히 중요하다. 못 하게 막는 가장 확실한 방법은, 애초에 할 수 있는 힘을 주지 않는 것이다.

[짚고 가기] 대화형과 헤드리스의 차이는 '옆에 사람이 있느냐 없느냐'로 단순하게 이해하면 된다. 대화형은 조수가 옆에서 "이거 지울까요?"라고 물으며 일하는 것, 헤드리스는 밤새 혼자 일하는 것이다. 혼자 일하는 직원에게는 미리 열쇠를 딱 필요한 것만 쥐여 준다 — 이게 최소 권한이다. 과정 첫머리에서 배운 퍼미션이 '실시간 승인'이라면, CI의 최소 권한은 '사전에 정해 둔 승인 범위'다. 같은 정신(사람이 위험을 통제)이 상황에 맞게 형태만 바뀐 셈이다.

API 키와 CI 시크릿

CI에서 AI를 돌리려면 그 AI를 쓸 인증 키가 필요하다. 이 키는 시크릿이므로, 코드에 넣지 않는다(보안 점검 때 세운 원칙 그대로). GitHub은 저장소 시크릿(Secrets) 이라는 금고를 제공한다 — 웹 설정에서 키를 넣어 두면, 워크플로가 실행될 때 그 값을 안전하게 꺼내 쓴다. 코드나 로그에는 절대 노출되지 않는다.

용어를 하나씩 풀자. API 키(API key) 는 어떤 서비스를 프로그램이 쓸 때 "나는 이 계정의 정당한 사용자다"를 증명하는 비밀 문자열이다. 사람이 웹사이트에 로그인할 때 아이디·비밀번호를 대듯, 프로그램은 API 키를 대고 서비스를 쓴다. CI 안에서 도는 Claude Code는 사람이 아니라 프로그램이므로, 브라우저 로그인 대신 이 키로 신원을 증명한다. 'API'는 프로그램끼리 주고받는 창구를 뜻하고, 그 창구를 여는 열쇠(key)가 API 키다.

이 키가 왜 그렇게 조심스러운가? 키를 가진 사람은 곧 여러분의 계정으로 AI를 쓸 수 있기 때문이다. 남이 여러분의 키를 손에 넣으면 여러분 요금으로 마음껏 AI를 돌린다. 그래서 키는 절대 코드 파일에 적어 두면 안 된다. 코드는 협업자와 공유되고, 저장소가 공개로 바뀌기도 하고, 실수로 인터넷에 올라가기도 한다. 코드에 키를 박아 두는 순간 그 모든 경로로 키가 새어 나간다. 이건 보안 점검 때 배운 원칙 그대로다 — 비밀은 코드와 분리해서 보관한다.

그렇다면 코드에 안 넣고 어떻게 쓰나? 여기서 저장소 시크릿(Secrets) 이 등장한다. 이름 그대로 '비밀'을 넣어 두는 GitHub의 금고다. 비유하면 은행 대여 금고 같다 — 값을 한 번 넣어 두면, 워크플로가 돌 때 그 금고에서 꺼내 쓰되, 사람은 다시 그 값을 들여다볼 수 없다. GitHub 화면 어디에도 그 키 원문이 다시 표시되지 않는다. 심지어 워크플로 실행 로그에 키가 찍히려고 하면 GitHub이 알아서 ***로 가려 준다. 코드에도, 화면에도, 로그에도 노출되지 않는 자리 — 그것이 시크릿이다.

키를 넣는 것은 권한을 넘기는 일이라 사람이 한다(웹 설정 클릭). 넣은 뒤에는 워크플로가 secrets.ANTHROPIC_API_KEY 같은 이름으로만 참조한다 — 값은 아무도 다시 못 본다.

여기에 담긴 역할 분담이 중요하다. 비밀을 다루는 일은 사람이, 그 비밀을 쓰는 일은 기계가 한다. 키를 발급받고 금고에 넣는 것 — 즉 권한을 시스템에 넘기는 결정 —은 되돌리기 어렵고 책임이 따르는 일이라 사람의 손을 거친다. 반면 넣어 둔 뒤 매번 그 값을 꺼내 쓰는 반복 작업은 기계에 맡긴다. 워크플로는 secrets.ANTHROPIC_API_KEY라는 이름표만 알 뿐, 그 이름표가 가리키는 실제 값이 무엇인지는 모른 채로 쓴다. 이름으로 부르되 값은 못 보는 이 구조 덕분에, 워크플로 파일을 아무리 열어 봐도 키는 나오지 않는다.

[짚고 가기] "시크릿에 넣은 키를 나중에 확인하고 싶은데 왜 안 보이지?" 하고 헷갈리기 쉽다. 이건 버그가 아니라 설계다 — 다시 볼 수 있으면 새어 나갈 구멍이 하나 더 생긴다. 은행 금고를 떠올리면 이해가 쉽다. 키를 잃어버렸으면 확인이 아니라 '재발급'이 정답이라는 점도 알아두자(원래 키는 폐기하고 새 키를 발급해 다시 넣는다). 시크릿을 .env로 분리할 때 배운 '비밀 분리' 원칙이 CI라는 새 무대에서 다시 반복되는 셈이다.

diff — 리뷰의 대상

diff(디프) 는 "이 PR에서 무엇이 바뀌었는가"의 목록이다 — 추가된 줄, 지워진 줄. 자동 리뷰는 전체 코드가 아니라 바뀐 부분(diff) 에 집중한다. 변경만 보면 리뷰가 빠르고 초점이 맞는다. "이번에 건드린 것"에 대해서만 말하게 하는 것이다.

'diff'는 영어 difference(차이)의 줄임말이다. 두 버전의 코드를 나란히 놓고 달라진 곳만 뽑아낸 것을 말한다. 관행적으로 지워진 줄 앞에는 빼기 기호 (-)를, 추가된 줄 앞에는 더하기 기호(+)를 붙여 보여 준다. 예를 들면 이런 모습이다.

  const price = ticket.grade === 'VIP' ? 50000 : 30000;
- const total = price * count;
+ const total = price * count + fee;

-가 붙은 줄은 이번에 지운 줄, +가 붙은 줄은 새로 넣은 줄이다. 이 한 조각만 보면 "수수료(fee)를 합계에 더하는 변경을 했구나"를 알 수 있다. 전체 파일을 다 읽지 않아도, 바뀐 부분만으로 무슨 일이 벌어졌는지 파악된다.

왜 전체 코드가 아니라 diff에 집중시키는가? 세 가지 이유다. 첫째, 빠르다. 프로젝트가 커지면 코드는 수만 줄이 된다. 매 PR마다 전부 다시 읽으면 느리고 비용도 많이 든다. 이번에 바뀐 몇십 줄만 보면 된다. 둘째, 초점이 맞는다. 리뷰의 목적은 '이번 변경이 괜찮은가'를 보는 것이다. 안 건드린 코드까지 트집 잡으면 정작 이번 변경에서 벗어난다. 셋째, 소음이 준다. "여기도 고쳐라, 저기도 고쳐라"가 이번 PR과 무관하게 쏟아지면 리뷰가 산만해진다. "이번에 건드린 것"으로 범위를 묶으면 코멘트가 그 PR의 맥락 안에 머문다.

다만 여기에는 짝이 되는 한계가 있다. diff만 보면 바꾼 곳의 파장이 미치는 다른 곳을 놓칠 수 있다. 예컨대 어떤 함수의 반환값을 바꿨는데, 그 함수를 쓰는 저 멀리의 코드가 깨질 수 있다. 그 '저 멀리'는 이번 diff에 안 들어 있으니 리뷰가 못 본다. 그래서 diff 집중은 만능이 아니라 속도와 초점을 얻는 대신 넓은 파급은 덜 본다는 거래다. 이 한계를 아는 것 자체가, 뒤에서 다룰 '무엇을 사람이 봐야 하나'의 한 조각이다 — 변경의 넓은 영향은 프로젝트 전체를 아는 사람이 함께 보아야 한다.

[짚고 가기] diff는 GitHub PR 화면의 'Files changed' 탭에서 바로 보인다. 실제 PR을 열어 초록(추가)·빨강(삭제) 줄을 직접 보면 개념이 즉시 붙는다. "AI 리뷰가 이 초록·빨강만 보고 코멘트를 단다"고 생각하면 그림이 그려진다. diff의 한계(파급 못 봄)는 바로 다음 절의 '사람의 몫'으로 자연스럽게 이어지는 다리이니, 여기서는 살짝 짚어만 두고 뒤에서 다시 다룬다.


20.3 개념 — 무엇을 자동 리뷰에 맡기고, 무엇은 사람이 보나

자동 AI 리뷰는 강력하지만 만능이 아니다. 경계를 분명히 하는 것이 이 장에서 가장 중요한 판단이다.

이 경계를 왜 이렇게 강조하는가? 자동 리뷰를 도입하면 사람은 자연스럽게 긴장을 푼다. "이제 AI가 봐 주니까 나는 대충 봐도 되겠지"라는 마음이 스며든다. 바로 그 순간이 위험하다. 자동 리뷰가 잘하는 것과 못 하는 것의 경계를 흐릿하게 알고 있으면, 사람은 AI가 못 보는 영역까지 AI에게 떠넘긴 채 안심해 버린다. 그물이 촘촘해진 게 아니라, 그물에 뚫린 구멍을 아무도 지키지 않게 되는 것이다. 그래서 이 절은 '자동 리뷰가 강력하다'가 아니라 '자동 리뷰의 정확한 한계선' 을 그리는 데 초점을 둔다.

자동 리뷰가 잘하는 것

이 셋을 관통하는 공통점이 있다 — '같은 것을 지치지 않고 반복'하는 일에 자동 리뷰가 강하다. 사람은 열 번째 PR쯤 되면 "시크릿 하드코딩 됐나"를 확인하는 손이 느슨해진다. 스무 번째면 그냥 눈으로 스윽 보고 넘긴다. 지루하고 반복적일수록 사람의 집중은 떨어진다. 그런데 기계는 정확히 그 반대다 — 첫 번째 PR을 볼 때의 꼼꼼함으로 천 번째 PR을 본다. 지루함도, 피로도, "이 정도는 괜찮겠지"라는 방심도 없다. 반복될수록, 지루할수록, 사람이 약해지는 바로 그 지점에서 기계가 강해진다. 이 성질의 차이가 무엇을 기계에 맡길지를 정하는 첫 번째 기준이다.

'1차 필터'라는 말을 곱씹어 보자. 자동 리뷰는 최종 판정관이 아니라 예선 심사관이다. 명백한 문제 — 경계 위반, 하드코딩된 키, 검증 없이 받은 입력 — 를 먼저 걸러 낸다. 그러면 사람 리뷰어에게 도달하는 코드는 이미 그 명백한 문제들이 걸러진 상태다. 사람은 자잘한 것에 눈을 쓰지 않고, 사람만이 볼 수 있는 것에 온전히 집중할 수 있다. 필터가 바닥을 훑어 주니 사람은 위를 본다.

사람이 반드시 봐야 하는 것

이 셋의 공통점도 하나로 모인다 — '코드 밖의 것을 알아야 답할 수 있는 질문' 은 사람의 몫이다. 자동 리뷰는 코드를 본다. 코드 안에 답이 있는 질문 ("이 값 검증했나", "경계 지켰나")에는 강하다. 하지만 답이 코드 밖에 있는 질문 앞에서는 무력하다. 몇 가지를 온티켓 상황으로 구체화해 보자.

비즈니스 판단. 리버사이드 재즈 페스티벌 티켓의 환불 규정을 "공연 3일 전까지 전액, 그 뒤로는 50%"로 짤지 "7일 전까지 전액, 그 뒤 환불 불가"로 짤지 — 코드로는 둘 다 완벽하게 짤 수 있다. 어느 쪽도 '틀린 코드'가 아니다. 어느 쪽이 온티켓의 사업에 맞느냐는 오대표와 기획이 정할 문제다. 관객 경험, 매출, 아티스트와의 계약, 경쟁사 정책 — 이 모든 걸 저울질하는 건 코드가 아니라 사업 판단이다. AI 리뷰는 "환불 로직이 명세대로 구현됐다"까지만 말할 수 있다. "그 명세 자체가 맞는 결정이냐"는 말할 수 없다.

맥락이 필요한 트레이드오프. 골목 라이브는 예전에 초과 판매 사고를 낸 이력이 있다. 그래서 잔여석 계산을 아주 보수적으로(동시 예매 충돌을 이중으로 막게) 짜 두었다고 하자. 이건 성능으로 보면 '과하게 조심스러운' 코드다. 일반적인 자동 리뷰라면 "여기 중복 검증이 있다, 하나 빼도 된다"고 지적할 수도 있다. 하지만 그 중복은 과거의 사고를 아는 사람이 일부러 심어 둔 안전장치다. "지금은 느려도 안전을 택한다, 트래픽이 늘면 그때 최적화하자"는 판단 — 이건 프로젝트의 역사와 사정을 아는 사람만 내릴 수 있다. AI는 코드의 '지금 모습'만 볼 뿐, 그 코드에 담긴 '결정의 사연'은 모른다.

AI 리뷰 자체의 오류. 자동 리뷰도 사람처럼 틀린다. 멀쩡한 코드를 문제라 지적하기도 하고(오탐), 진짜 문제를 못 보고 지나치기도 한다(누락). 그래서 AI가 단 코멘트를 판결문처럼 떠받들면 안 된다. "AI가 Blocker라 했으니 무조건 고쳐야 해"가 아니라, "AI가 이런 근거로 Blocker라 봤는데, 근거가 타당한가?"를 사람이 되짚는다. AI 코멘트는 명령이 아니라 의견이다 — 경험 많은 동료의 의견처럼, 참고하되 최종 판단은 사람이 한다. 이 태도는 코드리뷰어·QA 서브에이전트의 결과를 받던 태도와 정확히 같다. 자동화되었다고 해서 갑자기 AI의 말을 더 믿을 이유는 없다.

핵심 원칙: 자동 리뷰는 사람 리뷰를 없애지 않는다. 사람이 더 중요한 것에 집중하도록 바닥을 깔아 준다. 자동 리뷰를 "이제 사람은 안 봐도 된다"로 오해하는 순간, 품질은 오히려 떨어진다.

'바닥을 깔아 준다'는 표현을 조금 더 밀고 나가 보자. 자동 리뷰는 품질의 바닥(하한선) 을 올린다 — 명백한 실수가 병합되는 일을 막아, "적어도 이 수준 아래로는 안 내려간다"를 보장한다. 하지만 품질의 천장(상한선) 은 올리지 못한다. 좋은 설계, 사업에 맞는 결정, 사용자를 위한 세심함 — 이런 '더 높이'는 사람이 만든다. 바닥이 튼튼해지면 사람은 바닥을 지키느라 쓰던 에너지를 천장을 높이는 데 쓸 수 있다. 그래서 자동 리뷰는 사람을 아래로 끌어내리는 게 아니라 위로 밀어 올린다. 이 문장이 이 장 전체의 주제다 — 이 장의 마지막 실습에서 "관문은 바닥이지 천장이 아니다"라는 설명으로 다시 이어진다.

이 분별은 개발 밖에서도 같다 — AI가 계약서 초안의 오탈자·누락 조항을 걸러 주면 변호사가 진짜 위험한 조항 검토에 집중한다. AI가 회계 데이터의 이상치를 표시하면 사람이 그 이상치의 의미를 판단한다. 자동화는 사람을 대체하는 게 아니라 사람의 시선을 위로 올린다.

이 두 사례를 조금 더 자세히 펼쳐 보자. 구조가 코드 리뷰와 똑같기 때문이다.

계약서 검토. 대형 계약서는 수십 페이지에 조항이 빼곡하다. 사람 변호사가 처음부터 끝까지 오탈자, 빠진 정의, 앞뒤가 안 맞는 날짜, 참조가 깨진 조항 번호를 일일이 대조하면 눈이 빠지고, 지치면 놓친다. AI에게 이 '기계적 대조'를 맡기면 지치지 않고 빠짐없이 훑는다 — 정확히 자동 리뷰가 코드에서 하는 일이다. 그런데 "이 손해배상 상한 조항을 받아들이는 게 우리 회사에 유리한가", "이 독점 조항이 향후 사업을 묶지 않는가" — 이건 AI가 못 한다. 사업의 이해관계를 아는 변호사의 판단이다. AI가 오탈자·형식을 걸러 준 덕분에 변호사는 바로 이 '진짜 위험한 조항'에 온전히 집중한다. 계약서의 '바닥'은 AI가, '천장'은 변호사가 맡는 것이다.

회계 이상치 탐지. 수만 건의 회계 데이터에서 "평소와 다른 값"을 찾아내는 일 — 갑자기 열 배가 된 지출, 주말에 찍힌 결제, 반올림이 어긋난 합계 —은 사람이 눈으로 훑어서는 대부분 놓친다. AI는 이 이상치를 지치지 않고 표시해 준다. 하지만 표시된 이상치가 무엇을 뜻하는지 — 정상적인 대형 계약의 결과인지, 입력 실수인지, 진짜 부정의 신호인지 —는 사람이 판단한다. AI는 "여기 이상하다"까지, 사람은 "이 이상함이 무엇을 의미한다"까지. 코드 리뷰의 'AI는 근거를 주고 결정은 사람이 한다'와 한 글자도 다르지 않은 구조다.

세 사례(코드·계약서·회계)가 같은 모양이라는 걸 표로 정리하면 이렇다.

도메인 AI(자동)가 맡는 것 사람이 맡는 것
코드 리뷰 경계 위반·하드코딩·미검증 걸러내기 사업에 맞는 설계인가, 이 결정이 옳은가
계약서 검토 오탈자·누락 조항·형식 대조 이 조항이 회사에 유리·불리한가
회계 감사 이상치·비정상 패턴 표시 그 이상치가 무엇을 의미하는가

세 줄이 모두 같은 말을 한다 — 기계는 '무엇이 규칙에서 벗어났나'를, 사람은 '그것이 우리에게 무엇을 뜻하나'를 본다. 이 분별이 손에 익으면, 앞으로 어떤 도구를 도입하든 "이 일의 어디까지를 기계에 맡기고 어디부터 내가 붙들 것인가"를 스스로 그을 수 있다. 그것이 이 장이 남기려는 진짜 기술이다.

[짚고 가기] 이 절이 이 장의 정점이다. 계약서·회계 사례를 굳이 넣은 이유는, '이건 코딩 얘기가 아니라 일하는 방식 얘기'임을 분명히 하기 위해서다. 자기 도메인(마케팅·인사·영업)에서 "AI가 바닥을 깔고 내가 천장을 올리는 예"를 하나 떠올려 보면 개념이 확 붙는다. 반대로 "AI가 다 봐 주니 나는 확인 안 해도 되겠지?"라는 마음이 든다면, 오탐·누락 얘기로 돌아가 "AI도 틀린다, 그래서 최종 확인은 사람"을 다시 새기자. 여기서 무너지면 이 장의 메시지가 통째로 뒤집힌다.


20.4 프롬프트 작성법 — 자동 리뷰를 세운다

이 절의 지시들은 지금까지와 성격이 조금 다르다. 우리가 시키는 대상이 '코드를 짜는 일'이 아니라 '코드를 리뷰하는 장치를 짜는 일'이기 때문이다. 그래서 지시에는 "무엇을 리뷰할지"만이 아니라 "어떤 권한으로, 어디서 키를 읽고, 결과를 어떤 모양으로 낼지"까지 담긴다. 좋은 자동화 지시는 곧 좋은 울타리를 그리는 일이다.

워크플로 생성 지시

프롬프트
PR이 올라오면 AI가 코드리뷰를 자동으로 다는 워크플로를 만들어줘.
- .github/workflows/ai-review.yml
- 트리거: PR이 열리거나 갱신될 때
- 변경된 diff를 대상으로 리뷰. 우리 code-reviewer 역할의 기준을 쓴다
  (검사 순서: 경계 위반 → 명세 불일치 → 위험 패턴 → 기타)
- 리뷰 권한은 읽기만. 코드를 고치지 않는다
- 결과는 PR 코멘트로. 각 지적에 등급(Blocker/Major/Minor)과 근거를 붙인다
API 키는 저장소 시크릿(ANTHROPIC_API_KEY)에서 읽고, 워크플로에 키를 직접
넣지 마. 시크릿 등록은 내가 웹에서 할 테니 방법을 알려줘.

이 지시가 왜 좋은 지시인지 한 줄씩 뜯어 보자. 이 장에서 배운 개념이 지시의 각 줄에 그대로 박혀 있다.

한 지시 안에 이 장의 개념이 거의 다 들어 있는 셈이다. 자동화를 세우는 지시는 이렇게 원칙을 조목조목 명시할수록 결과가 안전해진다.

리뷰 범위를 좁히는 지시

자동 리뷰가 사소한 것까지 잔소리하면 사람이 코멘트를 안 읽게 된다 (경보 피로). 초점을 명시한다.

프롬프트
리뷰 기준을 조정해줘. Blocker와 Major만 코멘트로 달고, Minor는 요약
한 줄로만. 스타일 취향(따옴표 종류 같은)은 아예 언급하지 마 —
그건 자동 포맷터의 일이지 리뷰어의 일이 아니야.

'경보 피로(alert fatigue)'라는 말을 짚어 두자. 경보가 너무 자주, 너무 사소하게 울리면 사람은 경보 자체를 무시하게 된다. 화재경보기가 토스트만 구워도 울리면, 정작 불이 났을 때도 "또 토스트겠지" 하고 넘긴다. 자동 리뷰도 똑같다. 따옴표 종류, 빈 줄 개수 같은 취향까지 매번 코멘트를 달면, 사람은 코멘트 창을 열어 보지도 않게 된다 — 그 안에 진짜 중요한 Blocker가 섞여 있어도. 경보의 가치는 빈도에 반비례한다. 그래서 자동 리뷰는 '많이 잡는 것'이 아니라 '중요한 것만 골라 잡는 것'을 목표로 조율한다.

여기서 중요한 분업이 하나 더 있다. 스타일(따옴표·들여쓰기·빈 줄)은 리뷰어가 아니라 자동 포맷터의 일이다. 포맷터는 코드 모양을 기계적으로 통일해 주는 도구다 — 정답이 정해진 일이라 기계가 딱 잘라 처리한다. 그런 걸 리뷰어에게 시키면 두 가지가 나빠진다. 리뷰 코멘트가 사소한 것으로 가득 차 경보 피로를 부르고, 정작 리뷰어가 봐야 할 '판단이 필요한 것'이 그 소음에 묻힌다. 정답이 있는 일은 포맷터에게, 판단이 필요한 일은 리뷰어에게 — 이 나눔이 리뷰의 신호 대 잡음비를 지킨다.

아래처럼 등급별로 리뷰어에게 무엇을 시킬지 표로 정리해 두면, 조율의 감이 잡힌다.

등급 무엇 어떻게 다룰까
Blocker 병합을 막아야 할 심각한 문제(경계 위반, 하드코딩 키) 반드시 코멘트로, 근거와 함께
Major 지금 고치는 게 좋은 문제(미검증 입력, 명세 불일치) 코멘트로, 근거와 함께
Minor 있으면 좋지만 급하지 않은 개선 요약 한 줄로만
스타일 취향 따옴표·들여쓰기 등 언급 금지(포맷터의 일)

리뷰 성능 검증 지시

자동 리뷰가 진짜 보는지, 경계 위반을 심어 확인한다.

프롬프트
시험용 PR을 만들자. web/ 컴포넌트에 금액을 계산하는 코드(경계 위반)를
일부러 한 줄 넣은 브랜치로 PR을 올려줘. 자동 리뷰가 이 경계 위반을
Blocker로 잡아내는지 보려는 거야.

이 '리뷰 성능 검증'이라는 발상을 곱씹어 보자. 자동 리뷰를 세워 놓고 코멘트가 달리는 걸 보면, 우리는 "잘 돌아가는구나" 하고 안심한다. 그런데 코멘트가 달렸다는 것과 리뷰가 제대로 본다는 것은 다르다. 리뷰어가 엉뚱한 것만 잔뜩 지적하면서 정작 진짜 문제는 놓치고 있어도, 겉으로는 "코멘트가 달렸으니 작동 중"으로 보인다. 그래서 일부러 아는 문제를 심어, 그것을 잡아내는지로 리뷰의 눈이 실제로 살아 있는지 검증한다. 답을 아는 시험 문제를 슬쩍 끼워 넣어 채점자가 제대로 채점하는지 보는 것과 같다.

시험에는 반드시 명확한 정답이 있는 위반을 심는다. "web/에 금액 계산"은 우리 경계(계산은 api/에서)를 대놓고 어긴 것이라, 리뷰가 제대로 본다면 무조건 Blocker로 잡아야 한다. 만약 못 잡으면? 리뷰의 실력이 아니라 리뷰의 기준 전달을 의심한다 — 프롬프트가 경계를 충분히 강조하고 있는지, 검사 순서에 경계 위반이 제대로 들어 있는지. 시험이 통과하면 우리 리뷰어의 경계 감각이 파이프라인 안에서도 살아 있음을 확인한 것이다.

이런 리뷰 성능 검증은 여러 종류를 준비해 두면 좋다. 아래는 온티켓 맥락에서 써 볼 만한 시험 지시의 확장 사례다.

시험 2 — 하드코딩된 시크릿 심기:

프롬프트
시험용 PR을 하나 더 만들자. api/의 결제 설정에 토스페이먼츠 테스트 키를
코드에 직접 박아 넣은 브랜치로 PR을 올려줘. 자동 리뷰가 '시크릿
하드코딩'을 Blocker로 잡는지 확인하려는 거야.

시험 3 — 입력 미검증 심기:

프롬프트
마이티켓 조회에서 주문번호·이메일을 검증 없이 그대로 쓰는 코드로 PR을
올려줘. 자동 리뷰가 '입력 미검증'을 지적하는지 보자.

시험 4 — 잡아선 안 될 것을 심어 오탐 확인:

프롬프트
이번엔 규칙을 어기지 않은, 그냥 스타일만 조금 다른(따옴표 종류가 섞인)
PR을 올려줘. 자동 리뷰가 이걸 문제로 지적하면 오히려 잘못이야 —
스타일은 언급하지 말라고 했으니까. 조용히 넘어가는지 확인하려는 거야.

네 번째 시험이 특히 중요하다. 리뷰 성능 검증은 보통 '잡아야 할 걸 잡는가'만 보지만, 좋은 리뷰어는 '잡지 말아야 할 걸 조용히 넘기는가'도 통과해야 한다. 잔소리가 많은 리뷰어는 경보 피로를 부른다(위 '리뷰 범위를 좁히는 지시'에서 본 그대로다). 이 시험은 리뷰가 '예민하되 시끄럽지 않은' 균형을 지키는지 확인한다.

프롬프트를 '약한 지시 → 강한 지시'로 바꾸는 감각을, 이 장의 맥락에서도 표로 정리해 둔다.

약한 지시 강한 지시 무엇을 바꿨나
PR에 AI 리뷰 붙여줘 PR이 열리거나 갱신될 때, 변경 diff를 우리 code-reviewer 기준으로 리뷰하고, 등급·근거를 단 코멘트를 남기는 워크플로를 만들어줘 트리거·대상·기준·출력 형식을 모두 명시
권한 알아서 설정해줘 리뷰 권한은 읽기만. 코드를 고칠 수 없게 해줘 최소 권한을 명시(고칠 힘을 애초에 안 줌)
키는 안전하게 넣어줘 키는 저장소 시크릿(ANTHROPIC_API_KEY)에서 읽고, 워크플로 파일엔 절대 넣지 마 비밀 분리를 구체적 이름으로 지정
리뷰 잘 되는지 확인해줘 web/에 금액 계산을 한 줄 심은 PR을 올려, 자동 리뷰가 그걸 Blocker로 잡는지 시험해줘 검증을 '답을 아는 시험'으로 설계

[짚고 가기] 리뷰 성능 검증은 이 장에서 가장 흥미로운 부분이다. "일부러 틀린 걸 심어 AI가 잡나 본다"는 발상이 신선하기 때문이다. 여유가 되면 자기만의 '함정 PR'을 하나 설계해 직접 시험해 보면 훨씬 몸에 남는다. 여기서 기억할 점은, 자동화는 세워 두고 믿는 게 아니라 '작동하는지 계속 시험하는' 것이라는 사실이다. 세워 두고 방치한 리뷰어는 어느새 눈이 멀어 있어도 아무도 모른다 — 네 번째 오탐 시험까지 함께 해 보면 이 메시지가 완성된다.


20.5 실습 — 리뷰를 파이프라인에 심는다

따라하기 1 — API 키를 시크릿 금고에

먼저 AI를 CI에서 쓸 키를 저장소 시크릿에 넣는다(사람의 일). AI에게 절차를 안내받아 GitHub 웹에서 실행한다: Settings → Secrets and variables → Actions → New repository secret → 이름 ANTHROPIC_API_KEY, 값은 발급받은 키. 넣고 나면 값은 다시 볼 수 없다 — 금고의 정상 동작이다.

이 단계를 왜 사람이 직접, 그것도 웹에서 손으로 하는지 다시 짚는다. 키를 넣는 것은 권한을 시스템에 넘기는 결정이다(앞의 용어 정리에서 본 그대로). 되돌리기 어렵고 책임이 따르는 일이라, AI에게 대신 시키지 않고 사람이 클릭한다. AI는 '어디에 어떻게 넣는지 절차를 안내'하는 데까지만 돕는다 — 실제로 키를 넣는 손은 사람의 것이다. 이 분업이 자동화 시대의 안전 감각이다.

이름을 정확히 ANTHROPIC_API_KEY로 맞추는 것도 중요하다. 워크플로는 이 이름으로 금고를 찾는다. 이름이 한 글자라도 다르면(ANTHROPIC_KEY 등) 워크플로는 빈 금고를 열게 되고, 리뷰가 조용히 실패한다. 위 '프롬프트 작성법'의 워크플로 생성 지시에서 쓴 이름과 여기서 등록하는 이름이 정확히 같아야 한다.

[짚고 가기] "키를 어디서 발급받나?" Anthropic Console에서 발급받은 API 키를 쓴다(대화형 Claude Code의 구독 로그인과는 별개다 — CI는 사람 로그인이 아니라 프로그램용 키가 필요하기 때문). 공용 키를 쓰는 경우라면 개인 저장소 시크릿에만 넣고, 절대 코드나 채팅에 붙여넣지 않는 것을 처음부터 원칙으로 삼는다. 키가 화면에 노출되면 즉시 폐기·재발급이 원칙임도 알아두자.

따라하기 2 — 자동 리뷰 워크플로 생성

아래 생성 지시를 실행하고, .github/workflows/ai-review.yml을 연다. CI 워크플로를 읽으며 익힌 눈으로 읽는다 — 트리거는 PR인가, 키를 시크릿에서 읽는가, 권한이 읽기로 좁혀졌는가. 커밋·push.

프롬프트
PR이 올라오면 AI가 코드리뷰를 자동으로 다는 워크플로를 만들어줘.
- .github/workflows/ai-review.yml
- 트리거: PR이 열리거나 갱신될 때
- 변경된 diff를 대상으로 리뷰. 우리 code-reviewer 역할의 기준을 쓴다
  (검사 순서: 경계 위반 → 명세 불일치 → 위험 패턴 → 기타)
- 리뷰 권한은 읽기만. 코드를 고치지 않는다
- 결과는 PR 코멘트로. 각 지적에 등급(Blocker/Major/Minor)과 근거를 붙인다
API 키는 저장소 시크릿(ANTHROPIC_API_KEY)에서 읽고, 워크플로에 키를 직접
넣지 마. 시크릿 등록은 내가 웹에서 할 테니 방법을 알려줘.

위 프롬프트를 그대로 복사해 입력하면 된다. 다른 내용으로 바꿔서 해 보고 싶다면 자유롭게 바꿔도 좋다.

파일을 읽을 때 확인할 대목을 조금 더 구체적으로 짚어 둔다. 완벽히 이해할 필요는 없고, '이 장에서 배운 원칙이 파일에 박혀 있는가'만 눈으로 대조한다.

이 네 대목이 확인되면, 파일의 나머지 문법(YAML 형식 등)은 몰라도 된다. '우리가 의도한 안전장치가 다 들어 있는가'를 사람이 검증하는 것 — 이게 과정 첫머리부터 이어진 '사람의 일(검증)'이 CI 설정 파일에서 반복되는 모습이다.

따라하기 3 — 자동 리뷰가 도는 것을 본다

작은 변경으로 PR을 하나 올린다. 잠시 후 PR 화면에 AI가 단 코멘트가 나타난다. 사람이 아무도 부르지 않았는데 리뷰가 붙었다 — 검증이 사람의 기억에서 떨어져 나온 순간이다. 코멘트에 등급과 근거가 있는지 본다.

이 순간을 그냥 지나치지 말자. 방금 일어난 일은 작지만 근본적이다. 아무도 "리뷰해줘"라고 말하지 않았는데 리뷰가 붙었다. 코드리뷰어의 마지막 약점 — 사람이 불러야 일한다 —이 여기서 사라진 것이다. 앞으로 누가 올리는 어떤 PR이든, 마감에 쫓기든 금요일 저녁이든, 리뷰는 똑같이 붙는다. 검증이 사람의 의지에서 구조로 옮겨 간 것을 눈으로 확인하는 자리다.

따라하기 4 — 리뷰 성능 검증 (경계 위반 심기)

아래 시험 지시를 실행한다. web/에 금액 계산을 심은 PR을 올리고, 자동 리뷰가 그것을 Blocker로 잡는지 확인한다. 잡으면 — 우리 리뷰어의 경계 감각이 파이프라인 안에서도 살아 있는 것이다. 못 잡으면 기준을 점검·보강한다(리뷰 프롬프트가 경계를 충분히 강조하는지).

프롬프트
시험용 PR을 만들자. web/ 컴포넌트에 금액을 계산하는 코드(경계 위반)를
일부러 한 줄 넣은 브랜치로 PR을 올려줘. 자동 리뷰가 이 경계 위반을
Blocker로 잡아내는지 보려는 거야.

위 프롬프트를 그대로 복사해 입력하면 된다. 다른 내용으로 바꿔서 해 보고 싶다면 자유롭게 바꿔도 좋다.

여유가 있으면 위 '프롬프트 작성법'에서 본 확장 시험(하드코딩 키, 입력 미검증, 그리고 오탐 확인용 스타일 PR)도 이어서 돌려 본다. 특히 마지막 오탐 시험 — 규칙을 어기지 않은 PR을 리뷰가 조용히 넘기는지 —까지 통과하면, 우리 리뷰가 '예민하되 시끄럽지 않은' 균형을 지키고 있음을 확인한 것이다. 잡아야 할 것을 잡고, 잡지 말아야 할 것을 넘긴다 — 둘 다 되어야 좋은 리뷰다.

따라하기 5 — 사람의 몫 확인하기

자동 리뷰가 붙은 PR에서, AI가 말하지 않은 것을 사람이 찾아본다. "이 기능을 이번 버전에 넣는 게 맞나?", "이 방식이 오대표가 원한 것과 맞나?" — 코드로는 판단할 수 없는 질문들. 자동 리뷰는 이런 걸 대신해 주지 않는다는 것을 직접 확인한다. 관문은 바닥이지 천장이 아니다.

이 실습이 이 장의 진짜 결론이다. 앞의 네 단계가 '자동 리뷰를 세우는 법'이라면, 이 단계는 '자동 리뷰가 못 하는 일을 눈으로 확인하는 법'이다. 앞에서 배운 '사람의 몫'을 실제 PR 위에서 하나씩 짚어 보자.

이 질문들을 하나라도 던져 보면, 자동 리뷰가 아무리 정교해도 사람이 서 있어야 할 자리가 분명히 남는다는 것을 몸으로 안다. '관문은 바닥이지 천장이 아니다' — 자동 리뷰는 품질이 그 아래로 내려가지 않게 바닥을 깔지만, 품질을 더 높이 올리는 건 여전히 사람의 일이다.

[짚고 가기] 이 실습은 '무엇을 못 하는지 확인하는' 특이한 실습이라, "그래서 뭘 하라는 거지?"라고 헷갈리기 쉽다. 실제 PR을 하나 띄워 놓고 "AI 코멘트는 이걸 지적했는데, 여기 이 결정이 오대표가 원한 게 맞을까?"처럼 코드 밖 질문을 스스로 던져 보면 이해가 빠르다. 이 실습을 건너뛰면 '자동 리뷰 세웠으니 끝'으로 오해한 채 이 장을 떠나기 쉽다 — 이 장의 메시지가 정확히 그 반대이므로, 시간이 없어도 이 단계만은 반드시 한 번 짚고 넘어가자.

완료 기준 체크리스트


20.6 정리

오명운 · macro@prag-ai.com