코드 리뷰어가 당신에게 반하게 만드는 방법
원문은 Michael Lynch님이 에 게재했습니다. 이 블로그 구독하기
코드 리뷰를 이야기할 때 사람들은 보통 리뷰어에게만 집중한다. 하지만 코드를 작성하는 개발자도 코드를 읽는 사람만큼이나 리뷰의 중요한 당사자다. 코드를 리뷰에 내놓기 위해 어떻게 준비해야 하는지에 대한 가이드는 거의 없기 때문에, 저자들은 순전히 몰라서 이 과정을 망치곤 한다.
이 글에서는 코드 리뷰에 저자로서 참여할 때의 모범 사례를 설명한다. 사실 이 글을 다 읽고 나면 코드를 리뷰에 보내는 일을 너무나 잘하게 되어 리뷰어가 말 그대로 당신에게 반하게 될 것이다.
하지만 난 리뷰어가 나를 좋아하게 만들고 싶지 않은데요
어차피 반하게 될 거다. 받아들여라. 죽음을 앞두고 너무 많은 사람이 자신을 사랑했다고 불평한 사람은 아무도 없다.
코드 리뷰를 개선해야 하는 이유
코드 리뷰 스킬을 개선하면 리뷰어와 팀에게 도움이 되고, 무엇보다 당신 자신에게 도움이 된다.
더 빨리 배운다: 체인지리스트를 제대로 준비하면 리뷰어가 지루한 스타일 위반이 아니라 당신의 성장을 돕는 부분에 집중하게 할 수 있다. 건설적인 비판을 열린 마음으로 받아들인다는 걸 보여주면 리뷰어는 더 좋은 피드백을 제공한다.
다른 사람을 더 잘하게 만든다: 당신의 코드 리뷰 방식은 동료들에게 모범이 된다. 저자로서의 효과적인 습관은 팀원들에게 전염되고, 이는 그들이 당신에게 코드를 보낼 때 당신의 일을 더 쉽게 만들어준다.
팀 내 갈등을 줄인다: 코드 리뷰는 마찰이 흔히 생기는 지점이다. 의도적이고 성실하게 접근하면 논쟁을 최소화할 수 있다.
황금률: 리뷰어의 시간을 소중히 하라
당연한 조언처럼 들리지만, 나는 저자들이 리뷰어를 개인 QA 담당자처럼 대하는 경우를 자주 본다. 이런 저자들은 자신의 오류를 직접 잡거나 체인지리스트를 리뷰하기 쉽게 만들려는 노력을 전혀 하지 않는다.
동료는 매일 유한한 집중력을 가지고 출근한다. 그중 일부를 당신에게 할애한다는 건 자신의 업무에 쓸 수 없는 시간을 내준다는 뜻이다. 그 시간의 가치를 최대한 높이는 것이 당연한 도리다.
리뷰는 두 참여자가 서로 신뢰할 때 비약적으로 좋아진다. 당신이 피드백을 진지하게 받아들인다고 믿을 수 있을 때 리뷰어는 더 많은 노력을 기울인다. 리뷰어를 넘어야 할 장애물로 보는 시각은 그들이 제공할 수 있는 가치를 제한한다.
테크닉
- 먼저 자신의 코드를 리뷰하라
- 명확한 체인지리스트 설명을 작성하라
- 쉬운 일은 자동화하라
- 질문에 코드 자체로 답하라
- 변경 범위를 좁게 유지하라
- 기능적 변경과 비기능적 변경을 분리하라
- 큰 체인지리스트는 쪼개라
- 비판에 우아하게 대응하라
- 리뷰어가 틀렸을 때도 인내하라
- 대응을 명시적으로 전달하라
- 부족한 정보는 센스 있게 요청하라
- 애매하면 리뷰어에게 양보하라
- 리뷰 라운드 사이의 지연을 최소화하라
1. 먼저 자신의 코드를 리뷰하라
동료에게 코드를 보내기 전에 스스로 읽어보라. 단순히 실수를 찾는 데 그치지 말고, 처음 보는 코드를 읽는다고 상상해보라. 무엇이 헷갈릴 수 있을까?
나는 코드를 작성한 뒤 리뷰하기 전에 잠시 쉬는 것이 도움이 된다고 생각한다. 사람들은 종종 하루가 끝날 때 변경 사항을 쏘듯 보내버리곤 하는데, 그때가 부주의한 실수를 놓치기 가장 쉬운 순간이다. 아침까지 기다렸다가 새로운 눈으로 체인지리스트를 살펴본 뒤 동료에게 넘겨라.

가능한 한 리뷰어의 환경을 그대로 따라 하라. 리뷰어가 보게 될 것과 동일한 diff 뷰를 사용하라. 일반 소스 에디터보다 diff 뷰에서 어리석은 실수를 발견하기가 더 쉽다.
완벽할 거라 기대하지 마라. 어쩌다 보면 삭제하지 않은 디버깅 코드나 제외하려던 파일이 딸려 들어간 체인지리스트를 보내게 된다. 이런 실수가 세상의 끝은 아니지만, 추적할 가치는 있다. 자신의 실수 패턴에 주목하고, 이를 방지할 시스템을 만드는 것을 고민해보라. 이런 일이 너무 자주 일어나면 리뷰어에게 당신의 시간을 소중히 여기지 않는다는 신호를 주는 셈이다.
2. 명확한 체인지리스트 설명을 작성하라
이전 직장에서 나는 개발자 멘토링 프로그램의 일환으로 한 시니어 엔지니어와 정기적으로 만났다. 첫 미팅 전에 그는 내가 작성한 설계 문서를 가져오라고 했다. 내가 문서를 건네며 프로젝트가 무엇인지, 팀 목표와 어떻게 맞닿아 있는지 설명하자 멘토는 인상을 찌푸렸다. 그는 단호하게 말했다. “지금 말한 모든 내용은 설계 문서 첫 페이지에 있어야 해요.”
그 말이 맞았다. 나는 팀원들이 어떻게 읽을지를 상상하며 설계 문서를 썼지만, 다른 독자들을 고려하지 못했다. 바로 옆 팀원을 넘어 파트너 팀, 멘토, 승진 심사 위원회까지 더 넓은 독자가 있었다. 그들 모두가 문서를 이해할 수 있어야 했다. 그 대화 이후로 나는 내 작업을 맥락 있게 설명하기 위해 어떻게 틀을 잡아야 할지 항상 고민한다.
체인지리스트 설명에는 독자가 필요로 하는 배경 지식을 요약해야 한다. 설명을 쓸 때 머릿속에 특정 코드 리뷰어를 떠올릴지 모르지만, 그 사람이 당신이 상상하는 맥락을 갖고 있으리라는 보장은 없다. 게다가 다른 팀원들도 이 체인지리스트를 읽어야 할 수 있고, 미래의 독자들은 변경 이력을 되돌아볼 때 당신의 의도를 이해할 수 있어야 한다.
좋은 체인지리스트 설명은 이 변경이 높은 수준에서 무엇을 달성하는지, 그리고 왜 이 변경을 하는지를 설명한다.
훌륭한 체인지리스트 설명에 대해 더 깊이 알고 싶다면 내 글 “How to Write Useful Commit Messages”를 참고하라.
3. 쉬운 일은 자동화하라
중괄호 위치가 틀렸는지, 변경 사항이 자동화된 테스트 스위트를 깨뜨렸는지 리뷰어에게 알려달라고 의존한다면 당신은 리뷰어의 시간을 낭비하는 것이다.

자동화된 테스트는 팀의 표준 워크플로우의 일부여야 한다. 리뷰는 지속적 통합 환경에서 모든 자동 검사를 통과한 뒤에 시작되어야 한다.
만약 팀이 심각하게 잘못된 방향으로 가고 있어 지속적 통합에 투자하기를 거부한다면, 이런 검사를 스스로 자동화하라. git pre-commit hook과 린터, 포매터를 개발 환경에 추가해 매 커밋마다 코드가 올바른 컨벤션을 따르고 의도한 동작을 유지하도록 하라.
4. 질문에 코드 자체로 답하라
이 장면에서 무엇이 잘못되었을까?

저자는 내가 함수를 이해하도록 도와줬지만, 다음에 이 코드를 읽을 사람은 어떻게 할까? 변경 이력을 파고들어 지금까지의 모든 코드 리뷰 논의를 읽어야 할까? 더 나쁜 경우는 저자가 내 자리로 와서 직접 설명해주는 것이다. 이는 내 집중을 방해할 뿐 아니라 다른 누구도 그 정보에 접근할 수 없게 만든다.
리뷰어가 코드 동작 방식에 대해 혼란을 표할 때, 해결책은 그 한 사람에게 설명하는 것이 아니다. 모든 사람에게 설명해야 한다.

누군가의 질문에 답하는 가장 좋은 방법은 코드를 리팩터링해 혼란 자체를 없애는 것이다. 더 명확하게 만들기 위해 이름을 바꾸거나 로직을 재구성할 수 있을까? 코드 주석도 괜찮은 해결책이지만, 자연스럽게 스스로를 설명하는 코드보다는 분명히 못하다.
5. 변경 범위를 좁게 유지하라
범위 확장은 코드 리뷰에서 흔한 안티패턴이다. 개발자가 로직 버그를 고치기 시작했는데, 그 과정에서 UI의 흠을 발견한다. “여기까지 온 김에”라고 생각하며 “이것도 그냥 고쳐버리자”고 한다. 하지만 이제 일이 뒤죽박죽이 된다. 리뷰어는 어떤 변경이 목표 A를 위한 것이고 어떤 변경이 목표 B를 위한 것인지 구분해야 한다.
가장 좋은 체인지리스트는 한 가지 일만 한다. 변경이 작고 단순할수록 리뷰어가 모든 맥락을 머릿속에 담아두기가 쉽다. 관련 없는 변경을 분리하면 여러 팀원에게 리뷰를 병렬로 맡길 수 있어 변경 사항의 처리 시간도 줄어든다.
6. 기능적 변경과 비기능적 변경을 분리하라
범위를 최소화하라는 원칙의 당연한 귀결은 기능적 변경과 비기능적 변경을 분리하는 것이다.
코드 리뷰에 익숙하지 않은 개발자들은 이 규칙을 자주 위반한다. 두 줄을 변경했는데 코드 에디터가 파일 전체를 자동으로 재포맷해버린다. 개발자는 자신이 무슨 일을 했는지 인지하지 못하거나 새로운 포맷이 더 낫다고 판단한다. 그리고 수백 줄의 비기능적 공백 변경 속에 묻힌 두 줄짜리 기능 변경을 리뷰에 올린다.

이 체인지리스트의 공백 노이즈 속에 묻힌 기능 변경을 찾을 수 있겠는가?
뒤죽박죽인 체인지리스트는 리뷰어에 대한 엄청난 결례다. 공백만 바꾼 변경은 리뷰하기 쉽다. 두 줄짜리 변경도 리뷰하기 쉽다. 하지만 공백 변경의 바다에 묻힌 두 줄짜리 기능 변경은 지루하고 짜증 난다.
개발자들은 리팩터링하면서 변경을 부적절하게 섞는 경향도 있다. 나는 팀원들이 코드를 리팩터링하는 것을 좋아하지만, 동작을 바꾸면서 리팩터링하는 것은 정말 싫다.

이 체인지리스트는 동작에 한 가지 변경만 가하지만, 리팩터링 변경이 이를 가린다.
한 코드에 리팩터링과 동작 변경이 모두 필요하다면 두세 개의 체인지리스트로 나누어 진행해야 한다:
- 기존 동작을 검증하는 테스트를 추가한다(아직 없다면).
- 테스트 코드는 그대로 두고 프로덕션 코드를 리팩터링한다.
- 프로덕션 코드의 동작을 변경하고 그에 맞춰 테스트를 업데이트한다.
2단계에서 자동화된 테스트를 그대로 두면 리팩터링이 동작을 유지한다는 것을 리뷰어에게 증명할 수 있다. 3단계에 이르렀을 때 리뷰어는 동작 변경과 리팩터링 변경을 굳이 풀어헤칠 필요가 없다. 이미 미리 분리해 두었기 때문이다.
7. 큰 체인지리스트는 쪼개라
지나치게 큰 체인지리스트는 범위 확장의 못생긴 사촌이다. 개발자가 기능 X를 도입하려면 기존 라이브러리 A와 B의 의미를 수정해야 한다고 가정해보자. 변경이 적다면 괜찮지만, 이런 광범위한 수정이 너무 많아지면 체인지리스트가 엄청나게 커진다.
체인지리스트의 복잡도는 건드리는 코드 라인 수에 따라 기하급수적으로 증가한다. 내 변경이 프로덕션 코드 400줄을 넘어가면 리뷰를 요청하기 전에 쪼갤 기회를 찾는다.
모든 것을 한 번에 바꾸는 대신, 먼저 의존성을 변경하고 다음 체인지리스트에서 새 기능을 추가할 수 있을까? 기능의 절반을 지금 추가하고 나머지 절반을 다음 체인지리스트에 추가해도 코드베이스를 정상 상태로 유지할 수 있을까?
동작하고 이해 가능한 변경이 되는 부분 집합을 찾기 위해 코드를 쪼개는 일은 지루하다. 하지만 더 좋은 피드백을 얻을 수 있고 리뷰어의 부담도 덜 수 있다.
8. 비판에 우아하게 대응하라
코드 리뷰를 망치는 가장 빠른 방법은 피드백을 개인적으로 받아들이는 것이다. 많은 개발자가 자신의 작업을 자랑스러워하고 그것을 자신의 연장선으로 여기기 때문에 이는 쉽지 않다. 리뷰어가 피드백을 인신공격처럼 무신경하게 표현한다면 더욱 어렵다.
저자로서 피드백에 대한 반응은 궁극적으로 당신이 통제한다. 리뷰어의 코멘트를 인간으로서의 당신의 가치에 대한 평가가 아니라 코드에 대한 객관적인 논의로 받아들여라. 방어적으로 반응하면 상황만 더 악화될 뿐이다.
나는 모든 코멘트를 도움이 되는 교훈으로 해석하려 노력한다. 리뷰어가 내 코드에서 민망한 버그를 찾아내면 가장 먼저 변명하고 싶은 충동이 든다. 대신 그런 마음을 다잡고 리뷰어의 꼼꼼함에 대해 칭찬한다.

리뷰어가 코드의 미묘한 버그를 찾아냈을 때 감사를 표하라.
놀랍게도 리뷰어가 코드의 미묘한 결함을 발견하는 것은 좋은 신호다. 당신이 체인지리스트를 잘 포장하고 있다는 뜻이다. 잘못된 포맷이나 헷갈리는 이름 같은 뻔한 문제들이 없으니 리뷰어가 로직과 설계에 깊이 집중해 더 가치 있는 피드백을 줄 수 있는 것이다.
9. 리뷰어가 틀렸을 때도 인내하라
때때로 리뷰어는 완전히 틀릴 수 있다. 당신이 실수로 버그 있는 코드를 작성할 수 있듯, 리뷰어도 올바른 코드를 오해할 수 있다.
많은 개발자가 리뷰어의 실수에 방어적으로 반응한다. 사실도 아닌 비판으로 자신의 코드를 모욕당했다고 받아들인다.
리뷰어가 틀렸을 때조차 그것은 여전히 경고 신호다. 리뷰어가 잘못 읽었다면 다른 사람들도 같은 실수를 하지 않을까? 독자가 특정 버그가 없다는 것을 확신하기 위해 비정상적인 수준의 주의를 기울여야 하는 것 아닐까?

리뷰어가 실수했을 때 그 틀림을 증명하고 싶은 유혹을 참아라.
코드를 더 명백하게 올바르게 보이도록 리팩터링하거나 주석을 추가할 방법을 찾아보라. 혼란이 난해한 언어 기능에서 비롯된 것이라면, 비전문가도 이해할 수 있는 방식으로 코드를 다시 작성하라.
10. 대응을 명시적으로 전달하라
나는 누군가에게 코멘트를 남겼는데 상대방이 내 피드백 중 일부만 반영해 코드를 업데이트하고 아무 답글도 쓰지 않는 상황을 자주 겪는다. 그러면 상태가 모호해진다. 내 다른 코멘트를 놓친 걸까, 아직 작업 중인 걸까? 내가 새로운 리뷰를 시작하면 아직 완성되지 않은 체인지리스트에 시간을 낭비할 수 있다. 기다리면 서로 상대방이 계속하기를 기다리는 교착 상태에 빠질 수도 있다.
언제나 누가 “바통을 쥔” 상태인지 명확히 하는 컨벤션을 팀에 만들어라. 저자가 수정을 하고 있거나, 리뷰어가 피드백을 작성하고 있는 상태 중 하나여야 한다. 누가 무엇을 해야 할지 몰라 프로세스가 멈추는 상황은 절대 있어서는 안 된다. 제어권을 주고받는 시점을 알리는 체인지리스트 수준의 코멘트로 이를 쉽게 달성할 수 있다.

제어권을 리뷰어에게 다시 넘길 때 체인지리스트에 코멘트를 남겨 명시적으로 전달하라.
조치가 필요한 모든 코멘트에 대해 이를 처리했음을 확인하는 답글을 명시적으로 남겨라. 일부 코드 리뷰 도구는 코멘트를 해결됨으로 표시할 수 있게 한다. 그렇지 않다면 각 코멘트에 “Done”처럼 간단한 컨벤션을 따르라. 코멘트에 동의하지 않는다면 왜 조치를 취하지 않았는지 정중하게 설명하라.

Reviewable이나 Gerrit 같은 코드 리뷰 도구는 저자가 특정 코멘트를 해결됨으로 표시할 수 있는 메커니즘을 제공한다.
리뷰어의 노력에 맞춰 대응을 조절하라. 무언가 새로운 것을 배우도록 돕기 위해 상세한 코멘트를 남겼다면 단순히 완료 표시만 하지 마라. 노력을 고마워한다는 것을 보여주기 위해 사려 깊게 답하라.
11. 부족한 정보는 센스 있게 요청하라
때때로 코드 리뷰 코멘트는 해석의 여지를 너무 많이 남긴다. “이 함수는 헷갈린다” 같은 코멘트를 받으면 정확히 무엇이 “헷갈린다”는 건지 궁금할 것이다. 함수가 너무 긴 걸까? 이름이 불명확한 걸까? 문서화가 더 필요한 걸까?
오랫동안 나는 방어적으로 들리지 않으면서 모호한 코멘트를 명확히 하는 데 어려움을 겪었다. 본능적으로 “뭐가 헷갈린다는 거죠?”라고 묻고 싶었지만, 그렇게 물으면 투덜대는 것처럼 들린다.
한 번은 내가 무심코 모호한 코멘트를 팀원에게 보냈는데, 그가 정말 기분 좋게 무장 해제시키는 방식으로 답했다:
어떤 변경이 도움이 될까요?
나는 이 답변을 좋아한다. 방어적인 태도가 없고 비판에 열려 있다는 신호를 주기 때문이다. 리뷰어가 불명확한 피드백을 줄 때마다 나는 항상 “어떤 것이 도움이 될까요?”의 변형으로 답한다.
또 다른 유용한 기술은 리뷰어의 의도를 추측하고 그 추정에 기반해 선제적으로 코드를 수정하는 것이다. “헷갈린다” 같은 코멘트를 받았다면 코드를 다시 한번 살펴보라. 보통은 명확성을 높이기 위해 할 수 있는 무언가가 있다. 수정 자체가 당신이 변화를 수용할 의지가 있음을 리뷰어에게 전달하며, 설령 그것이 리뷰어가 생각한 변경이 아니더라도 그렇다.
12. 애매하면 리뷰어에게 양보하라
테니스에서는 상대의 서브가 아웃인지 확신이 서지 않을 때 상대에게 유리하게 판정한다. 코드 리뷰에서도 비슷한 기대가 있어야 한다.

미국 테니스 협회는 선수들에게 라인 판정 시 상대에게 유리하게 판정할 것을 요구한다.
코드에 대한 일부 결정은 개인 취향의 문제다. 리뷰어가 당신의 8줄짜리 함수가 5줄짜리 함수 두 개로 나뉘는 것이 더 낫다고 생각한다면, 누구도 객관적으로 “맞다”고 할 수 없다. 어느 버전이 더 나은지는 의견의 문제다.
리뷰어가 제안을 했는데 두 사람 모두 자신의 입장을 뒷받침할 근거가 대략 비슷하다면 리뷰어에게 양보하라. 두 사람 중에서는 리뷰어가 이 코드를 처음 읽는 것이 어떤 느낌인지에 대해 더 나은 관점을 가지고 있다.
13. 리뷰 라운드 사이의 지연을 최소화하라
몇 달 전, 한 사용자가 내가 유지 관리하는 오픈소스 프로젝트에 작은 변경을 기여했다. 나는 몇 시간 안에 피드백을 줬는데, 그는 곧바로 사라졌다. 며칠 뒤 다시 확인했지만 여전히 답이 없었다.
6주 뒤, 그 미스터리한 개발자가 다시 나타나 수정 사항을 제출했다. 그의 노력은 고마웠지만, 리뷰 라운드 사이의 지연 때문에 내 작업량은 두 배가 되었다. 코드를 다시 읽어야 했을 뿐 아니라 논의 내용을 기억해내기 위해 내 피드백까지 다시 읽어야 했다. 하루 이틀 안에 후속 조치를 했다면 그런 추가 작업을 할 필요가 없었을 것이다.

6주간의 중단은 극단적인 예지만, 나는 팀원들 사이에서 길고 불필요한 지연을 자주 본다. 누군가 체인지리스트를 리뷰에 올리고 피드백을 받은 뒤, 다른 업무에 밀려 일주일 동안 뒷전으로 미뤄둔다.
맥락을 복구하는 데 잃는 시간에 더해, 절반만 완성된 체인지리스트는 복잡도를 높인다. 무엇이 이미 병합되었고 무엇이 진행 중인지 모두가 추적하기 더 어려워진다. 부분적으로 완성된 체인지리스트가 많아질수록 병합 충돌도 많아지는데, 그걸 좋아하는 사람은 아무도 없다.
코드를 리뷰에 올렸다면, 리뷰를 완료까지 끌고 가는 것이 최우선 과제가 되어야 한다. 당신 쪽에서의 지연은 리뷰어의 시간을 낭비하고 팀 전체의 복잡도를 높인다.
결론
다음 체인지리스트를 리뷰에 올릴 준비를 할 때 당신이 통제할 수 있는 요소들을 고려하고, 이를 활용해 리뷰를 생산적으로 이끌어가라. 리뷰에 참여하면서 진행을 지연시키거나 노력을 낭비하는 패턴을 찾아보라.
황금률을 기억하라: 리뷰어의 시간을 소중히 하라. 리뷰어는 당신의 코드 중 흥미로운 부분에 집중할 수 있을 때 고품질의 피드백을 만들어낸다. 코드를 풀어헤치거나 단순한 실수를 단속하도록 만든다면 둘 다 손해다.
마지막으로, 사려 깊게 소통하라. 단순한 오해나 생각 없는 코멘트 하나로 리뷰가 탈선하는 것은 놀랄 만큼 쉽다. 다른 사람의 작업을 비판할 때는 감정이 격해지기 쉬우므로, 리뷰어가 공격받거나 무시당한다고 느낄 수 있는 함정을 의식하라.
축하한다! 여기까지 왔다면 이제 당신은 전문가 수준의 리뷰 대상자다. 리뷰어는 아마 당신에게 반했을 테니, 잘 대해주라.
더 읽을거리
- How to Do Code Reviews Like a Human: 저자 관점에서의 효과적인 관행을 배웠으니, 이제 리뷰어일 때 코드 리뷰를 개선하는 방법을 배워보라.
일러스트: Loraine Yow. 편집: Samantha Mason.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기