인간처럼 하는 코드 리뷰 방법 (파트 1)
요즘 코드 리뷰 모범 사례 기사를 읽고 있어요. 이 기사들은 버그 찾기에 거의 모든 부분을 집중시키고 코드 리뷰의 다른 모든 요소를 배제하는 것 같아요. 발견한 문제를 건설적이고 전문적인 방식으로 전달하는 건? 불필요하죠! 버그만 모두 찾아서 해결하면 나머지는 알아서 처리될 거예요.
그래서 저는 이런 생각을 하게 됐어요: 코드에 이게 통한다면 로맨스에도 적용해볼 수 있지 않을까요? 그와 함께 개발자들이 사랑생활을 도와줄 새로운 전자책을 발표하게 됐습니다:

제 혁신적인 전자책은 파트너의 결점을 최대한 많이 찾아내는 입증된 기법을 가르쳐줍니다. 이 전자책은 다음을 다루지 않습니다:
- 파트너와 공감과 이해를 바탕으로 문제를 전달하는 것.
- 파트너가 약점을 극복하도록 돕는 것.
코드 리뷰 문헌을 읽어본 결과, 관계의 이런 부분들은 명백하고 논의할 가치가 없다고 생각합니다.
이게 좋은 전자책 같으신가요? 아마 ‘비노노노!’라고 외치고 계실 거예요.
그럼 왜 우리는 코드 리뷰에 대해 그렇게 말하는 걸까요?
제가 읽은 기사들이 미래의 모든 개발자들이 로봇인 세계에서 나온 게 아닐까요? 그 세계에서는 동료들이 코드를 건성으로 비판해도 그들의 차가운 로봇 마음을 따뜻하게 해줍니다.
제가 여러분이 현재 인간 동료들과의 코드 리뷰를 개선하고 싶다고 가정하겠습니다. 더 과감하게 동료들과의 긍정적 관계가 단순한 결점당 비용 최소화 변수가 아니라 목적 자체라고 가정하겠습니다. 이 조건에서 리뷰 관행이 어떻게 달라질까요?
이 글에서는 코드 리뷰를 기술적 과정뿐만 아니라 사회적 과정으로 다루는 기법을 논의합니다.
코드 리뷰란 무엇인가
‘코드 리뷰’라는 용어는 동료 어깨너머로 코드를 읽는 것부터 코드 한 줄씩 선별하는 20명 미팅에 이르기까지 다양한 활동을 가리킬 수 있습니다. 저는 형식적이고 문서화된 과정을 가리키되, 대면 코드 검사 미팅만큼 무거운 과정은 아니라고 정의합니다.

코드 리뷰 참여자는 코드를 작성하고 리뷰를 위해 보내는 저자와 코드를 읽고 팀 코드베이스에 병합할 준비가 됐다고 판단하는 리뷰어입니다. 리뷰는 여러 리뷰어가 있을 수 있지만, 간단히 제가 단독 리뷰어라고 가정하겠습니다.
코드 리뷰가 시작되기 전에 저자는 변경 목록(changelist)을 작성해야 합니다. 이는 팀 코드베이스에 병합하고 싶은 소스 코드 변경 세트입니다.
리뷰는 저자가 변경 목록을 리뷰어에게 보내는 순간 시작됩니다. 코드 리뷰는 라운드로 진행됩니다. 각 라운드는 저자와 리뷰어 간 완전한 왕복: 저자가 변경 사항을 보내고 리뷰어가 해당 변경 사항에 대한 서면 피드백을 줍니다. 모든 코드 리뷰에는 하나 이상의 라운드가 있습니다.
리뷰는 리뷰어가 변경 사항을 승인할 때 끝납니다. 이는 흔히 ‘LGTM’(Looks Good To Me, ‘괜찮아 보이네’)이라고 합니다.
왜 이렇게 어려운가
프로그래머가 여러분에게 멋진 변경 목록을 보내고 여러분이 그것이 잘못됐다는 광범위한 이유 목록을 써준다면, 그건 민감한 메시지를 전달하는 일입니다.
그게 바로 제가 IT를 그만두게 된 이유예요. 프로그래머들은 정말 불쾌한 사람들… 예를 들어 항공에서는 자신의 기술 수준을 과대평가하는 사람들이 모두 죽었어요.
-Philip Greenspun, ArsDigita 공동 창업자, Founders at Work에서 발췌
저자가 코드를 비판받는 걸 자신의 프로그래머로서 무능함으로 받아들이기 쉽습니다. 코드 리뷰는 지식 공유와 정보 기반 엔지니어링 결정의 기회입니다. 하지만 저자가 논의를 개인 공격으로 받아들인다면 그게 불가능합니다.
더 어려운 건 서면으로 생각을 전달하는 일인데, 오해의 위험이 더 높습니다. 저자가 목소리 톤이나 몸짓을 볼 수 없으므로 피드백을 조심스럽게 표현하는 게 더 중요합니다. 방어심이 들고 있는 저자에게 ‘파일 핸들을 닫는 걸 잊었네요.’라는 무해한 메모가 ‘너 파일 핸들을 닫는 걸 믿을 수 없었구나! 정말 바보였어.’로 읽힐 수 있습니다.
기법
- 컴퓨터가 지루한 일은 하게 내버려두자
- 스타일 논쟁은 스타일 가이드로 해결하자
- 리뷰를 즉시 시작하자
- 높은 수준부터 시작해서 점점 내려가자
- 코드 예제를 후하게 주자
- ‘너’라고 하지 말자
- 피드백을 명령이 아닌 요청으로 표현하자
- 노트를 의견이 아닌 원칙에 연결하자
컴퓨터가 지루한 일은 하게 내버려두자
회의나 이메일 같은 방해 사이에 집중할 수 있는 시간은 소중합니다. 정신적 체력은 더 적습니다. 동료 코드를 읽는 건 인지적으로 부담이 크고 집중력이 필요합니다. 컴퓨터가 더 잘할 수 있는 일에 이런 자원을 낭비하지 마세요.
공백 오류 같은 명확한 예시입니다. 인간 리뷰어가 들여쓰기 실수를 찾아 저자와 함께 수정하는 데 얼마나 노력이 드는지, 자동 서식 도구로 해결하는 데 얼마나 드는지 비교해보세요:
| 인간 리뷰어가 있을 때 필요한 노력 | 서식 도구가 있을 때 필요한 노력 |
|---|---|
| 아무것도! |
오른쪽은 비어 있는데, 저자가 ‘저장’ 버튼을 누를 때마다 자동으로 공백을 서식 처리하는 코드 에디터를 쓰기 때문입니다. 최악의 경우 저자가 코드 리뷰를 보내고 연속 통합 도구가 공백이 잘못됐다고 보고합니다. 저자가 문제를 해결하면 리뷰어가 신경 쓸 일도 없어집니다.
리뷰에서 컴퓨터가 해결할 수 있는 기계적 작업을 찾아보세요. 흔한 작업은 다음과 같습니다:
| 작업 | 자동화된 해결책 |
|---|---|
| 코드 빌드 확인 | 연속 통합 도구, 예: Travis 또는 CircleCI. |
| 자동 테스트 통과 확인 | 연속 통합 도구, 예: Travis 또는 CircleCI. |
| 코드 공백이 팀 스타일과 일치 확인 | 코드 서식 도구, 예: ClangFormat (C/C++ 서식 도구) 또는 gofmt (Go 서식 도구). |
| 미사용 임포트 또는 미사용 변수 식별 | 코드 린터, 예: pyflakes (Python 린터) 또는 JSLint (JavaScript 린터). |
자동화는 리뷰어로서 의미 있는 기여를 더 많이 할 수 있게 해줍니다. 임포트 순서나 소스 파일명 명명 규칙 같은 이슈 클래스를 무시할 수 있으면 기능적 오류나 가독성 약점 같은 더 흥미로운 일에 집중할 수 있습니다.
저자에게도 이점입니다. 실수한 실수를 몇 초 만에 발견할 수 있고 즉각적인 피드백으로 맥락을 유지하며 더 쉽게 배우고 고칠 수 있습니다. 게다가 컴퓨터가 말해주는 게 훨씬 에고에 좋습니다.
팀과 함께 코드 리뷰 워크플로우에 이 자동 체크를 직접 넣으세요(예: Git의 pre-commit hooks 또는 GitHub의 webhooks). 리뷰 과정에서 저자가 수동으로 이 체크를 실행하면 대부분 이점을 잃습니다. 저자가 언젠가 잊고 버그를 리뷰해야 하니까요.
스타일 논쟁은 스타일 가이드로 해결하자
스타일 논쟁은 리뷰에서 시간 낭비예요. 일관된 스타일은 중요하지만 코드 리뷰가 중괄호 어디에 놓을지 논쟁할 시간은 아니에요. 리뷰에서 스타일 논쟁을 제거하는 최고의 방법은 스타일 가이드를 유지하는 거예요.

좋은 스타일 가이드는 명명 규칙이나 공백 규칙 같은 표면적 요소뿐만 아니라 주어진 프로그래밍 언어 기능을 어떻게 사용할지도 정의합니다. JavaScript와 Perl는 기능이 풍부해서 같은 논리를 구현하는 여러 방법이 있어요. 스타일 가이드는 한 팀이 한 세트 언어 기능을 쓰고 다른 팀이 다른 세트를 쓰는 일을 막습니다.
스타일 가이드가 있으면 저자가 누구의 명명 규칙이 좋다고 싸우는 리뷰 사이클을 낭비할 필요가 없어요. 스타일 가이드에 따르고 넘어갑니다. 스타일 가이드가 특정 이슈에 대해 명시하지 않았다면 보통 논쟁할 가치가 없어요. 스타일 이슈가 중요하고 가이드에 없으면 팀과 논의하세요. 결정은 스타일 가이드에 기록해서 다시 논의할 일이 없게 만드세요.
옵션 1: 기존 스타일 가이드 채택
온라인에서 검색하면 쉽게 찾을 수 있어요. Google의 스타일 가이드가 가장 유명하지만 상황에 맞는 다른 가이드도 있어요. 기존 가이드를 채택하면 처음부터 만드는 부담 없이 이점도 얻습니다.
단점은 조직마다 스타일 가이드를 최적화한다는 점입니다. 예를 들어 Google 스타일 가이드는 새 언어 기능 사용을 보수적으로 다뤄요. 그들은 수많은 코드베이스와 홈 라우터부터 최신 iPhone까지 모든 환경에서 실행되는 코드가 있어서요. 4명 스타트업이라면 최신 기능 사용을 더 공격적으로 할 수 있어요.
옵션 2: 점진적으로 자기 스타일 가이드 만들기
기존 가이드를 채택하고 싶지 않다면 직접 만들 수 있어요. 코드 리뷰에서 스타일 논쟁이 일어나면 전체 팀에 물어보고 공식 규칙을 정하세요. 합의되면 스타일 가이드에 기록하세요.
저는 Markdown로 팀 스타일 가이드를 소스 컨트롤 아래 유지하는 걸 좋아해요(예: GitHub Pages). 리뷰 과정이 적용되니까요. 변경을 위해 누군가가 명시적으로 승인하고 팀이 논의할 기회가 있어요. 위키나 Google Docs도 괜찮아요.
옵션 3: 하이브리드 접근
옵션 1과 2를 결합하면 기존 스타일 가이드를 기반으로 하고 로컬 스타일 가이드에서 확장하거나 오버라이드할 수 있어요. Chromium C++ 스타일 가이드가 좋은 예예요. Google C++ 스타일 가이드를 기반으로 하지만 그 위에 변경과 추가를 합니다.
리뷰를 즉시 시작하자
코드 리뷰를 최우선으로 다루세요. 실제 코드를 읽고 피드백할 때는 시간을 들이되 시작은 가능하면 몇 분 내에 하세요.

동료가 변경 목록을 보내면 다른 작업으로 막힌 상태일 거예요. 이론적으로 소스 컨트롤 시스템은 저자가 브랜치를 만들고 작업을 계속하며 리뷰에서 병합할 수 있게 해줍니다. 하지만 실제로는 4명 개발자만 효율적으로 할 수 있어요. 나머지는 3-웨이 diff를 풀 때 너무 오래 걸려 리뷰가 돌아오는 걸 기다리는 진척이 취소됩니다.
리뷰를 즉시 시작하면 선순환을 만듭니다. 리뷰 turnaround은 저자 변경 목록 크기와 복잡도에만 달려있어요. 저자에게 소규모 변경 목록을 보내도록 유인합니다. 리뷰하기 쉽고 즐거우니까요. 리뷰가 더 빨라지고 사이클이 계속됩니다.
동료가 1000줄 변경을 하는 새 기능을 구현한다면, 리뷰를 200줄로 2시간 안에 할 수 있다면 기능을 하루나 이틀 만에 완료할 수 있어요. 하지만 하루를 모두 리뷰한다면 기능이 일주일 걸립니다. 동료는 일주일 기다리고 싶지 않으니 500-600줄짜리 변경 목록을 보내게 될 거예요. 리뷰하기 더 비용이 들고 600줄 변경에서는 200줄처럼 맥락 유지가 어렵니 피드백 품질이 떨어집니다.
리뷰 라운드의 절대 최대 turnaround은 1 영업일입니다. 더 높은 우선순위 이슈로 하루를 넘기면 동료에게 알려주고 다른 사람에게 재지정 기회를 주세요. 한 달에 한 번 이상 리뷰를 거부해야 한다면 팀이 느린 발전 관행을 유지할 수 있게 속도를 줄여야 합니다.
높은 수준부터 시작해서 점점 내려가자
한 라운드에 쓴 노트가 많을수록 저자가 압도당할 위험이 커집니다. 정확한 한계는 개발자마다 다르지만 20~50노트 구간이 위험합니다.
저자가 노트 바다에 빠지는 걸 걱정한다면 초기 라운드에는 높은 수준 피드백으로 제한하세요. 클래스 인터페이스 redesign나 복잡 함수 분리 같은 이슈에 집중하세요. 낮은 수준 이슈(변수 명명이나 코드 주석 명확성 등)를 해결한 후에 다루세요.
높은 수준 노트를 저자가 통합하면 낮은 수준 노트가 무의미해질 수 있어요. 나중 라운드로 미루면 작성하고 주석 달기 노력을 줄이고 저자에게 불필요한 노트 처리 부담도 덜어줍니다. 이 기법은 리뷰 중 추상화 계층을 명확하게 나눠주어 체계적인 진행을 돕습니다.
코드 예제를 후하게 주자
이상 세계라면 저자는 리뷰를 감사할 거예요. 배우는 기회고 실수 막아줍니다. 현실에는 저자가 리뷰를 부정적으로 받아 저자에게 원망을 품을 수 있는 외부 요인이 있어요. 데드라인 압박 때문에 즉각 승인 외의 건설적 피드백이 방해로 느껴질 수 있어요. 함께 일해본 적이 적어서 피드백이 진심인지 불신할 수 있어요.
리뷰 과정에 저자가 기분 좋게 느끼게 하는 최고의 방법은 리뷰 중 선물을 주는 기회를 찾는 거예요. 개발자들이 좋아하는 선물은? 물론 코드 예시죠.

제안하는 변경을 직접 작성해 저자의 부담을 줄이면 리뷰어로서 관대한 것을 보여줍니다.
예를 들어 Python의 리스트 컴프리헨션 기능을 모르는 동료가 이런 코드를 보냈다고 가정해보죠:
urls = []
for path in paths:
url = 'https://'
url += domain
url += path
urls.append(url)‘이걸 리스트 컴프리헨션으로 간단히 할 수 있지 않을까요?’라고 하면 20분 동안 처음 써보는 걸 연구하게 만듭니다.
다음처럼 받으면 훨씬 기분 좋을 거예요:
리스트 컴프리헨션으로 이렇게 간단히 할 수 있을 것 같아요:
urls = ['https://' + domain + path for path in paths]
이 기법은 1라인으로 제한되지 않아요. 큰 증명을 위해 직접 브랜치를 만들어 함수 분리나 엣지 케이스 테스트 추가 같은 걸 보여줄 수 있어요.
명확하고 논쟁 없는 개선에만 제한하세요. 리스트 컴프리헨션 예시처럼 줄 수 83% 감소는 거의 논쟁 없을 거예요. 하지만 개인 취향 스타일 변화 같은 긴 예시를 쓰면 밀어붙이는 것처럼 보이니까요.
리뷰 라운드당 2~3개 코드 예시로 제한하세요. 전체 변경 목록을 대신 작성하면 저자가 자기 코드 작성 능력이 없다고 신호를 보내는 거예요.
‘너’라고 하지 말자
이건 이상하게 들릴 수 있지만 잘 들어보세요: 코드 리뷰에서 ‘너’라는 단어를 절대 쓰지 마세요.
리뷰에서 내리는 결정은 아이디어가 코드를 더 좋게 하는 게 아니라 누구인지에 기반해야 합니다. 동료가 변경 목록에 큰 노력을 기울였고 자랑스러워할 거예요. 자신의 일에 대한 비판은 방어심이 들고 보호본능을 발휘하게 해요.
피드백을 동료의 코드를 비판하는 게 아니라 코더를 비판하는 방식으로 하세요. ‘너’라는 단어를 보면 저자의 초점이 코드에서 자신으로 돌아가고 비판을 개인적으로 받아들일 위험이 커집니다.
이 무해해 보이는 메모를 보세요:
‘successfully’ 철자를 잘못 썼어요.
저자가 이 메모를 두 가지로 해석할 수 있어요:
- 해석 1: 잘했어! ‘successfully’ 철자 실수했지만 아직 똑똑해 보이네! 아마 그냥 실수였을 거야.
- 해석 2: ‘successfully’ 철자 잘못 썼네, 바보야.
‘너’를 빼면:
sucessfully -> successfully
후자는 단순 수정이고 저자 판단이 아니에요.
다행히 ‘너’를 피하는 건 쉽습니다.
옵션 1: ‘너’를 ‘우리’로 바꾸기
이 변수를 더 설명적인 이름으로 바꾸는 건 우리가 어떻게 할까요? 예:
seconds_remaining
‘우리’는 코드에 대한 팀 책임을 강조합니다. 저자가 다른 회사로 갈 수 있지만 코드 소유 팀은 계속 남아있을 거예요. ‘우리’가 자신에게 기대하는 것 같으면 어색하지만 비난보다 낫습니다.

옵션 2: 문장에서 주어를 제거하기
‘너’를 피하는 또 다른 방식은 문장에서 주어를 생략하는 단축 표현입니다:
이 변수를 더 설명적인 이름으로 rename하세요. 예:
seconds_remaining.
수동 태동으로 비슷한 효과를 낼 수 있어요. 기술 글에서 수동 태동을 피하지만 ‘너’ 피하기에 유용합니다:
이 변수는 rename해야 해요. 더 설명적인 이름으로, 예:
seconds_remaining.
추가로 ‘what about…’나 ‘how about…’로 질문으로 시작할 수 있어요:
이 변수를 더 설명적인 이름으로 rename하는 건 어떤가요? 예:
seconds_remaining?
피드백을 명령이 아닌 요청으로 표현하자
코드 리뷰는 논의를 개인 논쟁으로 돌릴 위험이 커서 더 세심한 태도가 필요합니다. 리뷰어들이 예의 바르게 다룰 거라 기대하지만 이상하게 반대로 가는 경우가 많아요. 동료에게 ‘그 스테이플러 주세요, 그 다음 소다 주세요.’라고 말하지 않지만 많은 리뷰어가 ‘이 클래스를 별도 파일로 옮겨.’처럼 명령처럼 표현합니다.
피드백이 너무 부드러워지게 하세요. 노트를 명령이 아닌 요청이나 제안으로 표현하세요.
같은 노트를 두 방식으로 비교해보죠:
| 명령처럼 피드백 | 요청처럼 피드백 |
|---|---|
Foo 클래스를 별도 파일로 옮겨. | Foo 클래스를 별도 파일로 옮길까요? |
저자가 자기 일을 통제한다고 느끼게 하세요. 요청하면 자율감을 줍니다.
요청은 저자가 정중히 밀어붙일 수 있게 해줍니다. 좋은 이유가 있다면 요청이면 답할 수 있어요. 명령이면 불순종처럼 보입니다.
명령이나 요청에 따라 대화가 얼마나 전투적으로 보이는지 비교해보죠:
| 명령처럼 피드백 (전투적) | 요청처럼 피드백 (협력적) |
|---|---|
리뷰어: Foo 클래스를 별도 파일로 옮겨.저자: 그럼 Bar 클래스와 너무 멀어져. 클라이언트는 보통 함께 사용하니까요. | 리뷰어: Foo 클래스를 별도 파일로 옮길까요?저자: 가능은 하지만 Bar 클래스와 너무 멀고 클라이언트는 보통 함께 사용하니까요. 어떻게 생각해? |
노트를 요청으로 표현하면 대화가 훨씬 더 예의 바르게 변합니다.
노트를 의견이 아닌 원칙에 연결하자
저자에게 노트를 줄 때 제안한 변경과 변경 이유를 설명하세요. ‘이 클래스를 두 개로 분리해야 해.’ 대신 ‘지금 이 클래스는 파일 다운로드와 파싱을 둘 다 담당해. 단일 책임 원칙에 따라 다운로더 클래스와 파싱 클래스 두 개로 분리해야 해.’라고 하세요.
노트를 원칙에 기반하면 논의를 건설적으로 만듭니다. 구체적인 이유를 인용하면 저자가 ‘아니, 내 방식이 좋아.’라고 할 수 있어요. 하지만 그게 바보보이니까요.
소프트웨어 개발은 예술이자 과학입니다. 항상 명확한 원칙으로 코드를 잘못된 부분을 설명할 수 없어요. 가끔 코드는 그냥 추하다거나 직관적이지 않아요. 이 경우 할 수 있는 건 설명하고 객관적으로 유지하세요. ‘제가 이해하기 어려웠어요.’는 객관적이고, ‘이게 혼란스러워요.’는 주관적 판단으로 모든 사람에게 적용되지 않아요.
가능하면 링크로 증거를 제공하세요. 팀 스타일 가이드 섹션이 최고예요. 언어 문서나 라이브러리 문서도 좋고, StackOverflow 고평점 답변도 좋지만 권위 문서에서 멀어질수록 증거가 약해집니다.
파트 2
이 글을 즐겼다면 이 글의 두 번째 절반을 확인해보세요. 이는 논쟁 없이 리뷰를 성공적으로 마무리는 기법을 다룹니다. 대형 리뷰 처리, 칭찬 기회 인식, 리뷰 범위 존중, 교착 상태 완화 기술이 포함됩니다.
인간처럼 하는 코드 리뷰 (파트 2)
편집: Samantha Mason. 삽화: Loraine Yow. 초기 초안 피드백: @global4g.
글을 무작위로 읽기