Skip to content
Published on

가르치는 코드 리뷰, 상처 주는 코드 리뷰 — 같은 지적을 다르게 만드는 것들

공유하기
Authors

들어가며 — 같은 지적, 다른 결과

두 개의 리뷰 코멘트가 있습니다. 하나는 "이거 왜 이렇게 짰어요?"이고, 다른 하나는 "이 함수가 검증과 저장을 같이 하고 있어서, 나중에 검증 규칙만 바꿀 때 저장 로직까지 건드려야 할 것 같아요. 두 개로 나누면 어떨까요?"입니다. 지적하는 대상은 완전히 같습니다. 하지만 첫 번째를 받은 사람은 방어를 준비하고, 두 번째를 받은 사람은 코드를 봅니다.

이 차이는 인성이나 예의의 문제로 설명되곤 하는데, 실은 대부분 형식의 문제입니다. 친절한 사람이 좋은 리뷰를 쓰는 게 아니라, 좋은 형식이 평범한 사람에게서 좋은 리뷰를 뽑아냅니다. 그래서 이 글은 마음가짐보다 형식에 대한 이야기입니다. 승인의 기준, 코멘트의 접두어, 문장의 순서, PR의 크기 — 전부 팀 위키 한 페이지로 합의할 수 있는 것들입니다.

리뷰의 세 가지 목적, 그리고 대부분이 놓치는 두 개

코드 리뷰는 세 가지 일을 동시에 합니다. 결함을 발견하고, 지식을 퍼뜨리고, 코드의 소유권을 나눕니다. 그런데 대부분의 팀은 첫 번째만을 리뷰의 정의로 여기고, 나머지 둘을 부수 효과 취급합니다.

흥미로운 건 데이터가 반대 방향을 가리킨다는 점입니다. 알베르토 바첼리(Alberto Bacchelli)와 크리스천 버드(Christian Bird)는 2013년 마이크로소프트에서 수행한 연구에서, 개발자와 관리자가 리뷰의 목적으로 결함 발견을 가장 먼저 꼽지만 실제로 관측되는 산출물은 그것과 다르다는 점을 보고했습니다. 실제 코멘트의 다수는 결함 지적이 아니라 가독성 개선, 대안 제시, 그리고 코드가 왜 그렇게 생겼는지에 대한 이해 공유였습니다. 케이틀린 사도스키(Caitlin Sadowski) 연구팀이 2018년에 발표한 구글 사례 연구도 비슷한 결론에 도달했습니다. 구글 내부에서 리뷰의 일차적 동기로 인식되는 것은 결함 사냥이 아니라 교육과 규범 유지 쪽에 가까웠습니다.

두 번째 목적, 지식 전파는 이렇게 작동합니다. 리뷰어는 코드를 읽으면서 그 도메인의 지도를 얻습니다. 6개월 뒤 그 코드가 새벽 3시에 터졌을 때, 리뷰에 참여했던 사람이 한 명이라도 있으면 복구 시간이 달라집니다. 이 관점에서 보면 리뷰어를 정할 때 "가장 잘 아는 사람"만 넣는 것은 최적화가 아닙니다. 잘 아는 사람 한 명과 모르는 사람 한 명 조합이 팀 전체로는 더 남는 장사인 경우가 많습니다.

세 번째, 소유권 공유는 사고가 났을 때 드러납니다. 리뷰를 거쳐 머지된 코드는 작성자 개인의 것이 아니라 팀의 결정이 됩니다. 이건 책임 회피 장치가 아니라 실패를 개인의 실수가 아니라 시스템의 문제로 다루기 위한 전제입니다. 개인 탓하기를 멈추는 문화가 왜 회복력의 조건인지는 일어난 일을 자산으로 바꾸는 법 편에서 다뤘습니다.

승인의 기준 — 완벽이 아니라 명확한 개선

리뷰가 지옥이 되는 가장 흔한 경로는 리뷰어가 스스로에게 잘못된 질문을 하는 것입니다. "이 코드가 내가 짰을 코드인가?"라고 물으면 승인은 영원히 오지 않습니다.

구글이 공개한 엔지니어링 관행 문서는 이 지점을 명시적으로 정리해 뒀습니다. 요지는 이렇습니다. 리뷰어는 코드가 완벽해지기를 요구해서는 안 되고, 그 변경이 시스템 전체의 코드 건강도를 명확히 개선한다면 승인해야 한다는 것. 완벽한 코드라는 것은 없고 더 나은 코드가 있을 뿐이라는 문장이 함께 붙어 있습니다. 승인의 질문은 "최선인가"가 아니라 "머지 전보다 나아졌는가"입니다.

이 기준이 왜 중요한가 하면, 반대 기준이 만드는 비용이 눈에 잘 안 보이기 때문입니다. 완벽을 요구하는 리뷰어 밑에서 사람들은 두 가지로 대응합니다. PR을 아주 작게 쪼개는 게 아니라, 리뷰를 아예 피할 수 있는 방식으로 일하기 시작합니다. 리팩터링을 미루고, 그 리뷰어를 리뷰어 목록에서 빼고, 급하다는 이유로 승인을 건너뛸 명분을 찾습니다. 리뷰 품질을 극단으로 밀면 리뷰 자체가 우회됩니다.

다만 이 기준에 예외는 있습니다. 보안, 데이터 정합성, 되돌리기 어려운 마이그레이션, 공개 API의 계약 — 이런 영역에서는 "나아졌으니 통과"가 통하지 않습니다. 차단할 것과 넘길 것의 목록을 팀 차원에서 미리 합의해 두면, 그 판단이 리뷰어 개인의 성격 문제로 오해되지 않습니다.

코멘트의 문법 — 강도 표시와 세 문장 구조

리뷰가 상처를 주는 가장 흔한 이유는 내용이 아니라 강도가 전달되지 않아서입니다. 리뷰어는 "고치면 좋겠지만 안 해도 됨" 정도로 쓴 코멘트를 작성자는 "이걸 고치지 않으면 승인 안 해 줌"으로 읽습니다. 열 개의 코멘트가 달리면 열 개가 전부 최고 강도로 읽힙니다.

해결책은 놀랄 만큼 단순합니다. 접두어를 붙이는 것. 구글의 리뷰 문서에도 사소한 지적에 "nit:"을 붙이라는 관행이 명시돼 있습니다. 팀 사정에 맞게 한글로 써도 되고, 세 단계면 충분합니다.

접두어승인 차단예시
nit취향에 가까운 사소한 지적안 함nit: 변수명을 복수형으로 하면 읽기 편할 것 같아요
제안지금 반영하면 좋지만 다음 PR도 괜찮음안 함제안: 이 분기 로직은 별도 함수로 빼면 테스트가 쉬워질 것 같아요
반드시머지 전에 해결이 필요함반드시: 이 쿼리에 사용자 ID 필터가 빠져서 다른 계정 데이터가 노출됩니다
질문판단이 아니라 정보 요청안 함질문: 여기서 재시도를 3회로 잡은 기준이 있을까요

접두어가 만드는 진짜 변화는 리뷰어 쪽에도 있습니다. 강도를 붙이려면 리뷰어가 스스로에게 "이게 정말 머지를 막을 일인가"를 물어야 하고, 그 질문만으로 차단성 코멘트의 수가 눈에 띄게 줄어듭니다.

강도를 정했다면 다음은 문장 구조입니다. 관찰, 영향, 제안 순서로 쓰면 평가어가 자연스럽게 빠집니다. "이 코드 별로네요"는 관찰이 없고 영향이 없고 제안도 없는, 순수한 평가입니다. 반면 "이 함수가 예외를 삼키고 있어서(관찰) 장애 시 원인 추적이 어려울 것 같아요(영향). 로깅 후 다시 던지는 건 어떨까요(제안)"는 같은 내용을 협업으로 바꿉니다. 이 구조는 코드 리뷰만의 것이 아니라 일반적인 피드백 문법이기도 합니다. 상황별 문장 패턴은 회사에서 그대로 쓰는 문장들 편에 정리해 뒀습니다.

한 가지 덧붙이면, 잘된 부분을 짚는 코멘트는 예의가 아니라 정보입니다. 리뷰에 부정 신호만 흐르면 작성자는 무엇을 유지해야 하는지 알 수 없습니다. "이 에러 처리 방식은 다른 모듈에도 가져가면 좋겠어요" 같은 한 줄이 팀의 관행을 만듭니다.

PR을 리뷰어를 위해 설계하기 — 크기와 설명

리뷰 품질의 상당 부분은 리뷰어가 아니라 작성자가 결정합니다. 그리고 작성자가 쥔 가장 큰 변수는 크기입니다.

널리 인용되는 숫자가 있습니다. 한 번에 200에서 400줄을 넘어가면 결함 발견 능력이 떨어진다는 것. 출처는 2006년 스마트베어가 시스코에서 수행한 사례 연구입니다. 개발자 약 50명, 코드 320만 줄, 리뷰 2500건 규모의 데이터를 분석해 리뷰 규모가 커질수록 줄당 결함 발견 수가 떨어지고, 시간당 500줄을 넘는 속도나 60분을 넘는 연속 리뷰에서 효과가 급감한다는 결론을 냈습니다.

이 근거의 한계도 같이 알아 둘 필요가 있습니다. 하나의 회사, 하나의 코드베이스, 2006년의 도구 환경에서 나온 단일 사례 연구이고, GitHub 풀 리퀘스트 이전의 관행을 전제합니다. 게다가 핵심 지표인 결함 밀도는 줄 수로 나눈 값이라, 큰 변경에서 값이 낮아지는 것은 부분적으로 정의상 당연한 결과이기도 합니다. 리뷰어가 실제로 보고한 결함만 세었다는 점도 걸립니다. 그러니 200에서 400줄은 물리 법칙이 아니라 경험칙으로 받아들이는 편이 정확합니다.

경험칙이라도 방향은 대체로 맞습니다. 참고로 구글 사례 연구에서 보고된 변경의 중앙값 크기는 수십 줄 수준이었습니다. 우리가 상상하는 것보다 훨씬 작습니다. 실무적으로는 이렇게 접근하면 충분합니다. 리팩터링과 기능 변경을 한 PR에 섞지 않는 것, 자동 포매팅과 실제 로직 변경을 분리해 커밋하는 것, 대규모 변경은 인터페이스를 먼저 머지하고 구현을 뒤에 붙이는 것.

크기를 줄였다면 남은 절반은 설명입니다. 좋은 PR 설명은 세 가지를 담습니다. 왜 이걸 하는가(맥락과 링크), 어떤 선택지를 고려했고 왜 이걸 골랐는가(대안), 어디를 집중해서 봐 달라는가(리뷰 요청 포인트). 특히 세 번째가 비동기 리뷰의 왕복을 결정적으로 줄입니다. "3번 파일의 락 처리만 특히 봐 주세요, 나머지는 기계적 변경입니다"라는 한 줄이 리뷰어의 두 시간과 하루치 왕복을 아낍니다.

리뷰를 받는 쪽의 기술

리뷰 문화는 리뷰어만 잘해서 만들어지지 않습니다. 받는 쪽에도 기술이 있습니다.

첫째, 첫 반응을 지연시키는 것. 코멘트를 읽고 즉시 반박문을 쓰는 것이 가장 흔한 실수입니다. 특히 시간을 들여 짠 코드일수록 지적이 인신공격처럼 읽힙니다. 30분 뒤에 다시 읽으면 같은 문장이 다르게 보이는 경우가 많습니다.

둘째, 동의하지 않을 때 근거를 요청하는 것. "그렇게 하면 성능이 나빠질 것 같은데요"보다 "이 방식을 고른 이유는 A였는데, 제안하신 방식이 그 부분에서 어떤 이점이 있을까요?"가 훨씬 빨리 결론에 도달합니다. 논쟁이 길어지면 형식을 바꾸는 것도 방법입니다. 왕복 세 번을 넘어가면 15분 통화가 스레드 스무 개보다 쌉니다.

셋째, 반영하지 않기로 한 것도 답하는 것. 침묵으로 넘어간 코멘트는 리뷰어에게 무시로 읽힙니다. "이건 다음 PR에서 처리하겠습니다, 티켓 만들어 뒀어요"나 "이건 의도한 동작이라 주석을 추가했습니다" 한 줄이면 충분합니다. 반영률보다 응답률이 신뢰를 만듭니다.

넷째, 배운 것을 명시적으로 말하는 것. "이 패턴 몰랐는데 배웠습니다"라는 한 줄은 아부가 아니라 리뷰어에게 주는 피드백입니다. 리뷰는 대체로 보상이 희박한 노동이고, 그 한 줄이 다음 리뷰의 성실도를 바꿉니다.

리뷰가 권력 게임이 될 때

여기까지가 잘 작동하는 경우의 이야기입니다. 그렇지 않은 경우도 있습니다.

신호는 대체로 비슷합니다. 특정 리뷰어의 PR만 유독 오래 걸린다. 지적의 내용이 리뷰마다 바뀌어서 맞출 수가 없다. 아키텍처 문제는 그냥 넘어가면서 변수명과 공백에는 코멘트가 스무 개 달린다. 마지막 항목은 노스코트 파킨슨(C. Northcote Parkinson)이 1957년에 지적한 자전거 보관소 논쟁과 정확히 같은 구조입니다. 원자로 설계에는 아무도 의견을 못 내지만 자전거 보관소 색깔에는 모두가 의견을 냅니다. 사소한 지적이 몰리는 곳은 대개 아무도 어려운 부분을 읽지 않았다는 신호입니다.

리뷰가 완전히 중립적인 기술 행위가 아니라는 실증 연구도 있습니다. 조시 테렐(Josh Terrell) 연구팀이 2017년에 발표한 깃허브 풀 리퀘스트 분석은, 전체적으로는 여성의 PR 승인율이 더 높았지만 성별이 프로필에서 식별 가능한 외부 기여자의 경우에는 그 우위가 사라진다고 보고했습니다. 이 연구는 성별 추정 방법과 관측 데이터의 인과 해석을 두고 상당한 반박을 받았고, 결론이 확정된 사안은 아닙니다. 다만 확정되지 않았다는 것이 "리뷰는 순수하게 기술적이다"의 근거가 되지도 않습니다.

대응은 개인기보다 제도가 낫습니다. 스타일 논쟁은 사람이 아니라 포매터와 린터가 판정하게 만들 것. 강도 접두어를 팀 규칙으로 못 박아서 차단 권한을 명시화할 것. 승인까지의 시간과 왕복 횟수를 사람별이 아니라 팀 지표로 볼 것. 그리고 반복적으로 특정 사람에게만 다른 기준이 적용된다면, 그건 리뷰 스레드에서 풀 문제가 아니라 매니저와의 1on1에서 다룰 문제입니다.

마치며 — 리뷰는 코드가 아니라 사람을 통과합니다

좋은 리뷰 문화의 지표는 결함 발견 수가 아닙니다. 6개월 뒤 그 코드를 고칠 수 있는 사람이 몇 명 늘었는가입니다. 결함은 테스트와 모니터링으로도 잡히지만, 설계의 맥락이 팀에 퍼지는 통로는 리뷰가 거의 유일합니다.

오늘 당장 바꿀 수 있는 것은 두 가지뿐입니다. 다음 코멘트에 강도 접두어를 붙이는 것, 그리고 다음 PR 설명에 "어디를 봐 달라"는 한 줄을 넣는 것. 문화는 합의문이 아니라 이런 한 줄들이 쌓여서 만들어집니다.