코드 리뷰하는 방법
다른 사람의 코드를 리뷰한 지 꽤 오래됐습니다. 정확히 말하면 20년이 넘었습니다. 요즘은 업무 시간의 50~70% 정도를 어떤 형태로든 코드 리뷰에 쓰고 있습니다. 시스템 설계와 함께, 이것이 제가 돈을 받고 하는 일입니다.
시간이 흐르면서 코드를 효과적으로 리뷰하는 방법에 대해 한두 가지 배운 것이 있습니다. 지금은 처음 시작했을 때와는 다른 부분에 집중합니다.
큰 그림을 보세요
나쁜 리뷰는 시야가 좁습니다. 유지보수성과 확장성 대신 문법이나 스타일, 사소한 문제에만 집중합니다.
좋은 리뷰는 변경 내용 자체뿐 아니라, 그 변경이 어떤 문제를 해결하는지, 앞으로 어떤 문제가 생길 수 있는지, 그리고 전체 시스템 설계에 어떻게 들어맞는지를 함께 봅니다.
저는 변경되지 않은 코드를 살펴보는 것을 좋아합니다. 진짜 이야기는 거기에 담겨 있는 경우가 많기 때문입니다.
예를 들어, 코드베이스나 문서의 연관된 부분을 업데이트하는 것을 잊는 경우가 많습니다. 이는 버그나 혼란, 호환성을 깨뜨리는 변경, 보안 문제로 이어질 수 있습니다.
꼼꼼하게 살펴보고 새로운 코드가 호출되는 모든 곳을 확인하세요. 올바르게 업데이트되었나요? 테스트는 여전히 올바른 대상을 검증하고 있나요? 변경은 적절한 위치에 이루어졌나요?
코드를 리뷰할 때 스스로에게 던지는 질문들을 정리한 치트 시트입니다:
- 이 코드는 시스템의 나머지 부분과 어떻게 어울리는가?
- 코드베이스의 다른 부분과는 어떻게 상호작용하는가?
- 전체 아키텍처에는 어떤 영향을 미치는가?
- 앞으로 계획된 작업에 영향을 주지는 않는가?
이 질문들은 변경 자체보다 시스템 설계와 더 관련이 깊습니다. 큰 그림을 놓치지 마세요. 좋지 않은 변경을 받아들이면 시스템이 쉽게 무너지게 됩니다.
코드는 고립된 채로 작성되지 않습니다. 경험이 많은 개발자의 역할은 운영상의 마찰을 줄이고 프로젝트의 리스크를 관리하는 것입니다. 문서와 테스트, 데이터 타입은 코드 그 자체만큼이나 중요합니다.
코드가 발전해 나가는 과정에서 더 나은 추상화가 없는지 항상 눈여겨보세요.
네이밍이 전부다
코드를 리뷰할 때 좋은 이름을 고민하는 데 상당한 시간을 씁니다.
이름 짓기는 어렵습니다. 그래서 제대로 하는 것이 그만큼 중요합니다. 코드 리뷰에서 가장 중요한 부분이 이름인 경우도 많습니다.
또한 가장 주관적인 부분이기도 해서, 사소한 트집과 중요한 네이밍 결정을 구분하기 어려워 지루하게 느껴지기도 합니다.
이름은 개념을 담고 코드에서 “구성 요소” 역할을 합니다. 나쁜 이름은 더 깊은 곳에 문제가 있음을 암시하는 코드 스멜입니다. 인지 부하를 한 단계, 혹은 그 이상으로 크게 높입니다.
예를 들어, 게임에서 플레이어의 스탯을 나타내는 구조체가 있다고 해보겠습니다:
struct Player {
username: String,
score: i32,
level: i32,
}저는 이런 코드를 자주 봅니다:
// Bad: using temporary/arbitrary names creates confusion
fn update_player_stats(player: Player, bonus_points: i32, level_up: bool) -> Player {
let usr = player.username.trim().to_lowercase();
let updated_score = player.score + bonus_points;
let l = if level_up { player.level + 1 } else { player.level };
let l2 = if l > 100 { 100 } else { l };
Player {
username: usr,
score: updated_score,
level: l2,
}
}이 코드는 읽고 이해하기 어렵습니다. usr, updated_score, l2는 무엇을 의미할까요? 의도가 명확하게 전달되지 않습니다. 이렇게 되면 인지 부하가 쌓이고 로직을 따라가기가 더 어려워집니다.
그래서 저는 조금 지나치게 깐깐하게 느껴지더라도, 항상 변수에 가장 적합한 이름을 고민합니다.
// Good: meaningful names that describe the transformation at each step
fn update_player_stats(player: Player, bonus_points: i32, level_up: bool) -> Player {
// Each variable name describes what the value represents
let username = player.username.trim().to_lowercase();
let score = player.score + bonus_points;
// Use shadowed variables to clarify intent
let level = if level_up { player.level + 1 } else { player.level };
let level = if level > 100 { 100 } else { level };
// If done correctly, the final variable names
// often match the struct's field names
Player {
username,
score,
level,
}
}값의 선언 위치와 사용 위치가 멀리 떨어져 있고, 많은 개발자가 문제 영역에 대해 공통된 이해를 공유해야 하는 큰 코드베이스에서는 좋은 이름이 더욱 중요해집니다.
“No”라고 말하는 것을 두려워하지 마세요
저는 수시로 변경을 거절해야 하는데, 결코 쉽지 않습니다. 어쨌든 누군가 많은 노력을 들였고 자신의 작업이 받아들여지기를 바라기 때문입니다.
결정을 그럴듯하게 포장하거나 무조건 좋게 보이려 하지 마세요. 객관적으로 이유를 설명하고 더 나은 대안을 제시하세요. 거절 자체에 머무르지 말고 다음 단계에 집중하세요.
옳지 않고 나중에 문제를 일으킬 코드를 받아들이는 것보다 거절하는 편이 낫습니다. 한 번 선례를 만들면 나중에 변경을 거절하기가 더욱 어려워집니다.
그것이 리뷰 과정의 목적입니다. 코드가 반드시 받아들여지리라는 보장은 없습니다.
오픈소스에서는 많은 사람들이 기준에 미치지 못하는 코드를 기여합니다. “안 된다”고 말하는 사람이 필요하고, 이는 매우 인기가 없는 역할입니다(오픈소스 메인테이너에게 물어보세요). 하지만 훌륭한 프로젝트에는 게이트키퍼가 필요합니다. 그렇지 않으면 수준 이하의 코드가 쌓이고 결국 유지보수할 수 없는 프로젝트가 되기 때문입니다.
때로는 사람들이 “일단 머지하고 나중에 고치자”고 말합니다. 저는 그것이 위험한 경사로라고 생각합니다. 기술 부채와 나중에 추가 작업으로 이어질 수 있습니다. 원칙을 지키는 것은 어렵지만 중요합니다. 뭔가 잘못되었다고 느끼면 목소리를 내세요.
어려울 때는 사람을 거절하는 것이 아니라 코드를 거절하는 것임을 기억하세요. 그들의 노력에 감사하고, 개선을 돕고 싶다는 점을 알려주세요.
리뷰에서 무엇에 집중해야 할지 직감이 생기더라도, 여전히 사실로 뒷받침해야 합니다. 같은 이유로 반복해서 거절하고 있다면, 팀을 위한 스타일 가이드나 가이드라인을 작성하는 것을 고려해 보세요.
너그럽되 단호하세요. 결국 코드일 뿐입니다.
코드 리뷰는 소통이다
코드 리뷰는 코드에 관한 것만이 아닙니다. 사람도 중요합니다. 동료와 좋은 관계를 쌓는 것이 중요합니다.
저는 가능하면 처음 몇 번의 리뷰는 페어 프로그래밍 세션으로 함께 진행하려고 합니다.
이렇게 하면 서로의 소통 방식에서 배울 수 있습니다. 신뢰를 쌓고 서로를 알아가는 데에도 효과적입니다. 나중에 소통에 문제가 생기거나 오해가 생겼다고 느끼면 이 과정을 다시 반복하는 것이 좋습니다.
여러 차례에 걸쳐 리뷰하세요
“이 PR 좀 빨리 봐줄 수 있을까요? 오늘 머지하고 싶어요.” 코드 리뷰가 한 번에 끝나는 일이라는 기대가 종종 있습니다. 하지만 실제로는 그렇지 않습니다. 코드 리뷰는 반복적인 과정입니다. 코드를 제대로 만들기 위해서는 여러 차례의 리뷰가 필요하다고 생각해야 합니다.
저는 첫 번째 반복에서는 큰 그림과 전체 설계에 집중합니다. 그 단계가 끝나면 세부 사항으로 들어갑니다.
목표는 가능한 한 빨리 머지하는 것이 아니라, 품질이 높은 코드를 받아들이는 것이어야 합니다. 그렇지 않다면 애초에 코드 리뷰를 하는 의미가 무엇이겠습니까? 이는 중요한 사고방식의 전환입니다.
리뷰는 오로지 결함을 지적하는 것만이 아니라, 팀 내에서 코드에 대한 공통된 이해를 만드는 과정이기도 합니다. 저는 종종 다른 사람의 코드를 리뷰하면서 더 나은 코드를 작성하는 법을 가장 많이 배웁니다. 훌륭한 엔지니어들로부터 제 코드에 대해 훌륭한 피드백을 받기도 했습니다.
이런 값진 “아하 모먼트”들이 개발자로서 성장하는 데 도움이 됩니다. 전문가들이 소중한 시간을 내어 제 코드를 리뷰해 주었고, 저는 그 과정에서 많은 것을 배웠습니다. 모든 사람이 커리어에서 한 번쯤은 이런 경험을 해봐야 한다고 생각합니다.
무례하게 굴지 마세요
때로는 작성자와 의견이 맞지 않을 때가 있습니다. 존중하고 건설적인 태도를 유지하는 것이 중요합니다. 인신공격이나 깔보는 듯한 말투는 피하세요. “이건 틀렸습니다”라고 말하지 말고, “저라면 이렇게 하겠습니다”라고 말하세요. 상대가 망설인다면, 그들의 생각을 이해하기 위해 질문을 던져 보세요.
- “이렇게 하면 기존 워크플로가 깨지지 않을까요?”
- “어떤 대안들을 고려해 보셨나요?”
- “이 함수를 빈 배열로 호출하면 어떻게 되나요?”
- “이 값을 설정하지 않으면 사용자에게 어떤 오류 메시지가 표시되나요?”
이런 “소크라테스식 질문”1은 작성자가 자신의 결정을 다시 생각해보게 하고 더 나은 설계로 이어질 수 있습니다.
사람들은 당신의 피드백을 받는 과정을 즐거워해야 합니다. 그렇지 않다면 자신의 리뷰 방식을 되돌아보세요. 자신이 받아도 기분 좋을 만한 코멘트만 남기세요.
저는 때때로 “이 부분 좋네요”나 “정말 좋은 아이디어예요” 같은 긍정적인 코멘트를 남기곤 합니다. 작성자의 동기를 유지하고 그들의 작업을 높이 평가하고 있음을 보여주는 것은 큰 힘이 됩니다.
가능하다면 코드를 직접 실행해 보세요
코드를 너무 오래 바라보면 미묘한 부분을 놓치기 쉽습니다. 직접 이것저것 만져볼 수 있는 로컬 복사본이 있으면 큰 도움이 됩니다.
저는 가능하면 코드와 테스트, 린터를 실행해 봅니다. 브랜치를 체크아웃해서 이것저것 옮겨 보고, 일부러 망가뜨려 보며 어떻게 동작하는지 이해하려는 과정이 제 리뷰 방식의 일부입니다.
UI 변경이나 오류 메시지 같은 사용자 대면 변경은 코드를 직접 실행해 보고 망가뜨려 보면서 확인하는 것이 훨씬 쉽습니다.
그다음에는 변경 사항을 되돌리고, 필요하면 발견한 내용을 코멘트로 남깁니다. 이런 접근을 통해 더 깊이 이해할 수 있습니다.
리뷰 가능 여부를 솔직하게 밝히세요
코드 리뷰는 개발 과정에서 병목이 되는 경우가 많습니다. 완전히 자동화할 수 없고 코드를 보고 피드백을 줘야 하는 사람이 반드시 개입되어야 하기 때문입니다.
하지만 동료가 내 코드를 리뷰해주기만 기다리다 보면 좌절감을 느끼게 됩니다. 그런 사람이 되지 마세요.
때로는 코드를 리뷰할 시간이 없을 수도 있고, 그건 괜찮습니다. 합리적인 시간 안에 리뷰할 수 없다면 작성자에게 미리 알려주세요.
저도 아직 노력 중이지만, 제 리뷰 가능 여부에 대해 더 능동적으로 알리고 명확한 기대치를 설정하려고 합니다.
배움을 멈추지 마세요
코드 리뷰는 제가 새로운 것을 배우는 가장 좋아하는 방법입니다. 새로운 기술과 패턴, 라이브러리를 배우지만, 무엇보다도 다른 사람들이 문제에 어떻게 접근하는지를 배웁니다.
저는 리뷰할 때마다 한 가지 새로운 것을 배우려고 노력합니다. 팀 전체가 개선되고 성장하는 데 도움이 된다면, 결코 낭비되는 시간이 아닙니다.
사소한 것에 집착하지 마세요
포매터가 존재하는 데는 이유가 있습니다. 공백이나 서식은 도구에 맡기세요. 정말 중요한 문제에 에너지를 아끼세요.
로직과 설계, 유지보수성, 정확성에 집중하세요. 코드 품질에 영향을 주지 않는 주관적인 취향은 피하세요.
스스로에게 물어보세요. 이게 기능에 영향을 주나요? 혹은 미래의 개발자를 혼란스럽게 할까요? 아니라면 그냥 넘어가세요.
“어떻게”보다 “왜”에 집중하세요
코드를 리뷰할 때는 변경 이면에 있는 이유에 집중하세요. 이유 없이 결함만 지적하는 것보다 훨씬 성공 확률이 높습니다.
다음 두 가지 코드 리뷰 코멘트를 비교해 보세요. 첫 번째는 도움이 되지 않고 단정적입니다.

두 번째는 대안을 제시하고 문서 링크를 첨부하며, 왜 이 변경이 나중에 문제를 일으킬 수 있는지 설명합니다.

어느 쪽을 받고 싶으신가요?
이런 방식이 더 많은 시간과 노력을 필요로 한다는 것을 잘 압니다. 하지만 그만한 가치가 있습니다! 대부분의 경우 작성자는 이를 고마워하고 앞으로 같은 실수를 반복하지 않게 됩니다. 도움이 되는 리뷰는 시간이 지날수록 복리처럼 효과가 쌓입니다.
바보 같은 질문을 두려워하지 마세요
추측하는 것보다 물어보는 편이 낫습니다. 이해가 안 되는 부분이 있다면 작성자에게 설명을 요청하세요. 아마 당신만 이해하지 못한 것이 아닐 가능성이 높습니다.
작성자는 대개 자신의 생각을 기꺼이 설명해 줍니다. 이를 통해 코드와 시스템 전체에 대한 이해가 깊어질 수 있습니다. 또한 작성자가 다른 관점에서 생각해 보는 계기가 되기도 합니다. 어쩌면 자신의 가정이 틀렸다는 것을 깨닫거나, 시스템이 스스로를 잘 설명하지 못한다는 것을 알게 될 수도 있습니다. 어쩌면 문서가 빠져 있는 것일지도 모릅니다.
당신의 리뷰 방식에 대해 피드백을 구하세요
때때로 작성자에게 당신의 피드백에 대한 피드백을 구해보세요:
- 너무 가혹하거나, 사소한 것에 집착하거나, 느리거나, 허술하지 않았나요?
- 올바른 부분을 지적했나요?
- 당신의 피드백이 도움이 되었나요?
- 개선할 점에 대한 제안이 있나요?
말하자면, 당신의 리뷰 과정을 리뷰해 달라고 부탁하는 셈이죠, 하하.
코드를 리뷰하는 방법을 배우는 것은 꾸준한 연습과 다듬기가 필요한 기술입니다. 자신만의 스타일을 찾으시길 바랍니다.
그 용어를 알려준 Lucca에게 고마움을 전합니다! ↩
글을 무작위로 읽기