코드 라인별 검토를 멈추고 대신 검토하는 것들
요약
AI의 발전으로 코드 생산 속도가 빨라지면서, 기존의 '라인별 검토' 방식은 더 이상 효과적이지 않다는 문제점을 지적합니다. 이제는 코드를 훑어보는 것을 넘어, 데이터 변경 사항과 권한 관리 등 시스템 전반의 위험(Risk)에 초점을 맞춰야 합니다.
핵심 포인트
- AI 시대에는 라인별 코드 검토가 한계에 도달했다.
- AI가 만든 '깨끗한 실수'는 발견하기 어렵다.
- 검토 초점은 데이터 변경 사항과 권한 관리 등 시스템 위험으로 이동해야 한다.
AI가 diff의 절반을 작성해 주면서, 모든 라인을 읽는 것은 더 이상 업무가 아닙니다. 어디를 봐야 하는지 아는 것이 중요합니다.
라인이 아닌 위험(Risk)을 검토하기
지난 분기, 저는 숫자를 세어봤습니다.
평균적인 날에 저희 팀의 풀 리퀘스트(pull requests)에서 1,500개에서 2,500개 사이의 코드 라인을 검토하라는 요청을 받았습니다. 1년 전만 해도 그 숫자는 600개에 가까웠습니다.
팀원 수는 두 배가 되지 않았는데, 결과물은 그렇게 늘어난 것입니다.
AI 어시스턴트 덕분에 모두 코드를 생산하는 속도가 빨라졌습니다. 하지만 아무도 제가 그것을 읽는 속도를 빠르게 만들지는 못했습니다.
그래서 저는 대부분의 리드 개발자들이 하는 일을 했습니다. 훑어보는 것이었습니다. 명백한 실수를 찾고, 테스트가 통과했는지 확인하고, 네이밍에 대한 코멘트를 몇 개 남긴 뒤, 승인(Approve) 버튼을 눌렀습니다.
그러다 제가 승인했던 PR에 있던 버그가 프로덕션 환경에서 발생했습니다. 그 버그는 영리한 코드 속에 숨어있지 않았습니다. 제가 처음부터 끝까지 읽었음에도 놓친, 완벽하게 깨끗하고 잘 포맷된 300라인의 diff 안에 숨어 있었습니다.
그때 저는 불편하지만 받아들여야 할 사실을 알게 되었습니다:
라인별 검토는 더 이상 확장될 수 없습니다. 그리고 그렇게 될 수 있다고 가장하는 것은, 그렇지 않다고 인정하는 것보다 더 나쁩니다.
라인별 검토가 무너지는 이유
코드 리뷰는 코드를 작성하는 것이 느린 시대에 맞춰 설계되었습니다.
개발자가 기능 하나를 만드는 데 하루를 썼고, 리뷰어는 그것을 읽는 데 30분을 썼습니다. 그 비율이 통했던 것입니다.
하지만 그 비율은 사라졌습니다.
오늘날 개발자는 예전에 하루가 걸리던 것을 한 시간 만에 생산할 수 있습니다. diff는 세련되어 보이고, 포맷팅도 완벽하며, 변수 이름도 합리적입니다. 마치 좋은 코드처럼 읽히는데, 이것이 바로 문제입니다.
AI가 생성한 코드는 사람이 만든 코드와 다르게 실패합니다:
- 사람은 지저분한 실수(messy mistakes)를 합니다. 오타, 어색한 로직, 미완성된 생각 등. 보기 흉해서 발견하기 쉽습니다.
- AI는 깨끗한 실수(clean mistakes)를 합니다. 누락된 권한 확인(permission check). 루프 안의 쿼리(query inside a loop). 필요한 단계를 커버하지 못하는 트랜잭션(transaction) 등. 완전히 올바르게 보입니다.
모든 것이 올바르게 보일 때, 라인별로 읽는 것은 잘못된 것을 스캔하는 것으로 변합니다. 그리고 위험한 버그들은 그렇게 보이지 않습니다.
저는 신중하게 검토하는 것을 멈추지 않았습니다. 제가 주의를 기울이는 곳을 바꿨을 뿐입니다.
이제, 단 한 줄의 코드를 읽기 전에 저는 다섯 가지 질문을 합니다. 각 질문은 실제 문제가 발생하는 지점으로 제 주의를 집중시킵니다.
1. 데이터는 무엇이 바뀌었나?
이것은 항상 저의 첫 번째 검토 포인트입니다.
마이그레이션(Migrations), 모델 변경, 데이터베이스에 쓰기 작업을 하는 모든 것이 해당됩니다. 코드는 몇 분 안에 되돌릴 수 있습니다. 하지만 데이터는 그럴 수 없습니다.
저는 다음 사항들을 살펴봅니다:
- 큰 테이블을 잠가버리는 마이그레이션
- 오래된 코드가 여전히 의존하고 있음에도 이름이 변경되거나 제거된 필드
- 트랜잭션(transaction) 안에 있어야 하지만 그렇지 않은 쓰기 작업
- 수백만 개의 행을 조용히 채울 기본값(Defaults)
데이터 계층에 오류가 있다면, PR(Pull Request)의 다른 어떤 것도 중요하지 않습니다.
2. 누가 이것을 할 수 있는가?
두 번째로 비용이 많이 드는 버그는 권한 관련 버그입니다.
새로운 엔드포인트나 뷰(view)마다 저는 한 가지 질문을 합니다: "사용자가 자신의 데이터가 아닌 데이터에 접근할 수 있는가?"
저는 쿼리(queries)가 요청에서 받은 ID로 단순히 필터링되는 것이 아니라, 현재 사용자 또는 조직 범위로 제한되어 있는지 확인합니다. AI가 생성한 뷰는 _올바른 객체_를 반환하는 데 매우 능숙합니다. 하지만 오직 올바른 객체만을 반환하는 것에는 훨씬 신뢰성이 떨어집니다.
3. 데이터의 100배가 되면 어떻게 되는가?
개발자 노트북에서 20밀리초(milliseconds) 만에 실행되는 쿼리가 프로덕션 환경에서는 20초가 걸릴 수 있습니다.
저는 다음 사항들을 살펴봅니다:
- 루프 안에 있는 쿼리 (고전적인 N+1 문제)
- 누락된
select_related또는prefetch_related - 인덱스(index)가 없는 컬럼에 대한 필터링
- 테이블 전체를 메모리에 로드하는 모든 작업
이것들을 찾기 위해 모든 줄을 읽을 필요는 없습니다. 코드가 데이터베이스에 닿는 모든 곳을 찾아내고, 그것들을 천천히 읽으면 됩니다.
4. 실패하면 어떻게 되는가?
성공적인 경로(Happy paths)는 검토하기 쉽습니다. 프로덕션은 성공하지 못하는 상황들 속에 존재합니다.
- 외부 API가 시간 초과되면 어떻게 될까?
- Celery 작업이 두 번 실행되면 어떻게 될까?
- 사용자가 버튼을 더블클릭하면 어떻게 될까?
만약 답이 "아마 일어나지 않을 것이다"라면, 일어날 것입니다. 보통 주말에 말입니다.
5. 작성자가 설명할 수 있는가?
마지막 이 질문은 코드와는 전혀 관련이 없습니다.
어떤 사소하지 않은 변경 사항(non-trivial change)이든, 저는 작성자에게 그 접근 방식에 대해 두세 문장으로 설명해 달라고 요청합니다. 테스트하라는 것이 아닙니다. 디자인이 그들의 것인지 아니면 단순히 수용된 것인지를 알아내기 위함입니다.
만약 그들이 그것을 설명할 수 있다면, 저는 나머지 diff를 더 신뢰하게 됩니다. 만약 설명할 수 없다면, 그것은 제가 어디에 속도를 늦춰야 하는지 알려줍니다.
저는 더 이상 코드를 검토하지 않습니다. 저는 위험(risk)을 검토합니다. 코드는 단지 위험이 존재하는 곳일 뿐입니다.
포기하게 된 것들
초점을 전환한다는 것은 일부 사소한 것들에 대해 의식적으로 덜 신경 쓰는 것을 의미합니다.
저는 주로 다음 사항에 대한 코멘트를 중단했습니다:
- 형식 및 스타일(Formatting and style). 이것은 린터(Linters)와 포맷터(formatters)가 저보다 더 잘 처리합니다.
- 명명 규칙 선호도(Naming preferences). 이름이 적극적으로 오해를 불러일으키지 않는 한, 검토할 가치가 없습니다.
- "나는 다르게 했을 것이다." 만약 코드가 정확하고 안전하며 읽기 쉽다면, 저의 선호도는 중요하지 않습니다.
처음에는 이것이 어려웠습니다. 스타일 코멘트는 생산적인 것처럼 느껴집니다. 눈에 보이고 쓰기 쉽기 때문입니다.
하지만 변수 이름 하나에 쓴 매 분은 누락된 권한 확인(missing permission check)에 사용하지 못한 시간이었습니다.
팀의 변화
이 다섯 가지 질문들은 제가 검토하는 방식만 바꾼 것이 아닙니다. 시간이 지남에 따라, 팀이 PR을 작성하는 방식까지 바꾸어 놓았습니다.
사람들이 리뷰어가 데이터(data), 권한(permissions), 규모(scale), 실패(failure), 그리고 설명(explanation)으로 시작할 것임을 알게 되자, 그들은 검토 전에 이미 이 질문들에 답하기 시작했습니다.
그래서 저희는 PR 템플릿에 짧은 섹션을 추가했습니다:
## 위험 노트 (Risk notes)
- 데이터 변경 사항:
- 권한 영향:
...
마지막 줄이 가장 가치 있는 것이었습니다. 누군가 "재시도 로직(retry logic)이 맞는지 잘 모르겠습니다"라고 작성하면, 그들은 제가 어디를 봐야 할지 정확히 알려준 것입니다.
검토는 빨라졌습니다. 그리고 한동안 처음으로, 그것들은 동시에 더 나아졌습니다.
리뷰어의 진정한 역할
오랫동안 저는 좋은 리뷰어란 모든 것을 읽고 아무것도 놓치지 않는 사람이라고 생각했습니다.
그것은 결코 현실적이지 않았습니다. AI가 순환하는(in the loop) 환경에서는 심지어 불가능합니다.
오늘날 좋은 리뷰어는 보통 어디에 문제가 숨겨져 있는지 아는 사람입니다: 데이터 계층(data layer), 권한 경계(permission boundary), 확장성이 떨어지는 쿼리, 아무도 계획하지 못한 실패 지점 말이죠. 그들은 자신의 주의를 그곳에 집중하고 나머지 부분은 도구들이 처리하도록 맡깁니다.
AI 덕분에 코드를 작성하는 것은 저렴해졌습니다. 하지만 판단력(judgment)을 발휘하는 것은 더 비싸졌습니다. 코드 리뷰는 바로 그 판단력이 사용되는 곳입니다.
이제 여러분 차례입니다 (Over to You)
이렇게 많은 부분이 AI로 생성된 상황에서, 어떻게 코드를 리뷰하고 계신가요? 여전히 모든 줄을 읽으시나요, 아니면 접근 방식을 바꾸셨나요? 다른 팀들에게 어떤 방법이 효과적인지 알고 싶습니다. 댓글에 알려주세요 👇
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기