백발의 개발자를 꿈꾸며

💬

“반복되는 행위만 하며, 다른 결과를 기대하는 것은 정신병 초기 증상이다.” - 아이슈타인

코드 리뷰

효율적으로 리뷰 하는 방법

코드 리뷰를 높은 우선 순위로 작업에 할당해야 한다.

코드 리뷰 작성자는 리뷰가 종료될 때 까지 해당 작업은 더 이상 진행할 수 없는 대기 상태에 빠진다.

코드 리뷰를 바로 시작 하면, 선순환이 된다.

코드를 읽고 피드백을 줄 때 까지 충분한 시간을 가지고 진행해도 되지만, 시작은 바로 해야 한다. 가장 이상적인 것은 코드 리뷰를 요청 받으면, 수분 내에 진행 하는 것

리뷰 라운드는 최대 하루 안에 어프로브 되도록 만들어라.

우선순위 높은 업무로 1일 내 불가하면 다른 리뷰어로 지정할 수 있다. 월 1회 이상 리뷰어를 재지정 한다면 속도를 줄여 건강한 개발 관습을 유지할 수 있도록 문화적으로 접근해야 한다.

PR 가이드

작고, 범위가 좋은 PR 은 대체적으로 쉽고 리뷰하기 편리하여, 빠르게 리뷰 라운드가 진행될 수 있다.

효율적 리뷰 방법

고수준으로 시작해서, 저수준으로 내려가라

리뷰 라운드에서 많은 의견을 남길수록, 코드 작성자가 당황할 위험이 커진다.

초기 라운드에서는 고수준 피드백으로 제한

고수준의 피드백이 처리된 후, 저수준 이슈를 꺼낸다.

예시를 제공하는 것에 관대하라

예제는 너무 길면 관대한 것이 아니라 억압적일 수 있다. 리뷰에서, 너무 많은 예제가 많으면 코드 작성자의 수준이 낮다고 인지할 가능성이 있다.

리뷰의 범위에 존중하라

태그를 활용하라

Nit 태깅은 고치면 좋지만 아니어도 그만을 의미로 사용되며, 보통은 그래도 고친다.

리뷰어는 항상 더 개선할 수 있는 의견을 자유롭게 남길 수 있어야 하는 SNS 소셜 코딩과 같은 존재이기 때문에, 코드 작성자가 이를 어느정도 의식하여 자신이 의사결정을 내릴 수 있어야한다.

📝

현재 글리티에서 리뷰 태그를 활용하는 방법 P 레벨을 사용하여, 레벨이 낮을수록 점차적으로 선택적인 범위가 되고, 높을수록 크리티컬한 내용을 언급한다.

한두 등급만 코드 레벨을 올리는 것을 목표로 하는 것이 리뷰이다.

D 등급의 PR 을 받았다고 하면, 코드 작성자가 C 나 B 등급을 받을 수 있도록 만들어라.

승인을 보류하는 이유는, 수 차례의 리뷰 라운드 후에도 코드 등급이 계속 F 상태일 때 보류된다.

피드백 방법

코드 리뷰의 핵심은 “무엇이 코드를 나아지게 했는가?”에 대한 주제를 나눠야지, 누가 그런 잘못된 코드를 작성했는지에 대해 얘기를 나누는 공간이 아니다.

비판의 대상은 코드일 뿐, 저자가 아니다. 피드백에서 사용하는 소통 방식은 상대방이 작성한 결과물로 하여금 나의 감정을 나타내는 화법을 이용하면 원활한 의사소통이 된다.

건설적인 피드백을 하라

동료들간의 코드 리뷰는 서로간 누가 코드를 잘 작성하는지 경쟁을 하는 것이 아닌 팀의 생산성을 높이는 행위이다.

코드 리뷰를 자신의 코드에 대한 비판이 아닌, 학습 과정으로 인지하면 전체적인 프로젝트의 성공에 기여할 수 있다.

진정한 칭찬을 해라

대부분의 리뷰어가 잘못된 부분에만 집중하기 나름인데, 긍정적 행위 강화를 위한 값진 기회이기도 하다.

온라인 코드 리뷰를 많이 접하지 않은 코드 작성자는 매우 민감하고, 방어적으로 받아들일 수 있다.

피드백은 명령이 아니라 요청으로 표현해라

우린 일상에서 동료에게 명령하지 않는다. 그렇기에 리뷰에서는 강압적인 명령이 아니라 요청을 해야 한다.

“특정 클래스의 파일로 분리 하라” -> “특정 클래스가 너무 커지는 것 같은데 유지보수할 때 괜챃을까요?”

의견이 아니라 원칙에 기반하여 피드백하라

저자에게 의견을 줄 때는, 제안하는 변경 사항과 변경의 이유를 모두 설명해야 한다.

🧪

예시 이 클래스를 2개로 분리하면 어떨까요?

지금, 이 클래스는 파일 다운로드와 파싱의 2가지 책임을 가지고 있어요. 이를, 다운로더와 파서 2개로 분리하여 SRP 를 준수하면 나중에 수정사항이 생길 때 편하게 작업할 수 있을 것 같아요. 어떻게 생각하시나요?

원칙으로 설명이 되지 않는 경우도 존재한다. 예를 들어, 직관적이지 않다는 주관적인 견해에 의해서 내용을 전달해야 할 때가 있다.

이러한 경우에는, 나의 생각 / 내가 느낀점을 설명한다.

코드 리뷰의 토론이 지속적으로 격양되어 교착상태에 빠지려고 한다면, 적극적으로 처리하라.

교착상태 판단 방법

코드 리뷰의 최악의 결과는 교착 상태이며, 코멘트를 반영하지 않으니 승인이 거부되고 코드 작성자는 이해할 수 없기 때문에 리뷰 반영을 거부한다.

이럴 경우에 대면 리뷰로 전환하여, 텍스트 기반으로 의견을 나누지 말고 상대방과 대화로 나누는 것이 가장 좋은 방법이다.

교착상태 처리 방법 상대방을 인정하고, 승인한다. - Agree to disagree

저수준 코드를 승인 했다고 해서 코드 품질이 나빠질 수 있다. 하지만, 동료와 다퉈서 관계가 악화된다면 고수준 품질을 얻을 기회를 날리게 된다.

인정을 못하겠다면, 상급자를 참여 시키거나 다른 리뷰어로 변경하라.

코드 리뷰 사례

리뷰를 반영한 커밋 제공하기

코드 작성자가 피드백에 대해 반영한 내용을 커밋 해시를 제공하여, 리뷰어가 해당 커밋으로 바로 이동하여 어떻게 반영 했는지 내용을 확인할 수 있다.

배움을 주고 받기

코드 리뷰를 아주 재밌게 하는 방법

코드 작성자가 PR을 올린 내용을 리뷰어 환경에서 코드를 직접적으로 수정하고, 개선하는 모습을 보여주며 개선점을 설명한다.

설명한 후, 변경된 코드 내역은 restore 하여 제거한 뒤 코드 작성자에게 개선사항을 반영해서 다시 PR 을 보내도록 요청한다.

이렇게 해서 얻을 수 있는 것은 코멘트로만 전달하기 어려운 내용을 직접적으로 전달할 수 있다.

체크 리스트

📝

버그/장애

  • NPE
  • Thread Safety
  • OOM
  • 단위 테스트가 작성되지 않은 중요한 로직
  • 경계값 테스트
  • 외부 URL 이나 DB 를 N 번 호출하는 경우

NPE 방지 eqauls 사용


code.equals("") // X

"".eqauls(code) // O

ThreadSafety

OOM

기능성

가독성 / 유지보수 용이성

테스트

자료 구조

성능

도날드 크누스 - 미리 성능을 고려 하는 행위는 97% 악의 근원이다.

설계

레거시 다루기

📝

레거시의 흔한 특징

  • 이상한 이름과 이름이 의미하는 행위와 코드의 행동이 다른 경우가 많다.
  • 코드가 직관적이지 못하고 복잡하다.
  • 긴 메서드와 긴 클래스를 갖고 있다.
  • 복사 붙여넣기의 산출물 처럼 중복이 많다.
  • 테스트 코드가 존재하지 않다.
💡

악순환 줄이기

  • 악순환을 줄이려면 코드 변경 비용을 낮춰야 함
  • 변경 비용을 낮추려면 변경하기 쉬운 구조로 점진적으로 리팩토링 해야 함
  • 리팩토링해도 이전과 동일하게 동작해야 함
  • 이전과 동일하게 동작하는지 확인할 수 있는 테스트가 필요함
  • 테스트를 만들려면 기능이 어떻게 동작하는지 분석해야 함

즉, 악순환을 줄이려면 레거시를 분석하고 테스트를 만들고 리팩토링 해야 한다.

레거시 분석하기

레거시 분석의 한계

  1. 코드 전반을 머릿속에 보관하기 어렵다.
  2. 긴 코드를 한 번에 보는 것이 어렵다.
  3. 코드를 이해하기 위한 보조 수단이 필요하다.
💡

코드 분석에 도움이 되는 보조 수단

  • 코드 시각화: 다이어그램을 사용해서 코드 흐름을 시각화 -> 실행 흐름 이해에 많은 도움
  • 코드 출력 + 형광펜
  • 함께 코드 보기
  • 스크래치 리팩토링

코드 시각화를 위한 몇 가지 표기법

액티비티 다이어그램: 단계적인 코드 실행 흐름

코드가 시각적으로 어떤 행동을 하는지 잘 드러나기 때문에, 논리적으로 무슨 기능인지 파악하는데 많은 도움을 얻을 수 있다.

시퀀스 다이어그램: 구성 요소간 연동 흐름

의존/호출 관계 그래프: 클래스/변수/필드/메서드 간 의존 관계를 그림으로 표현

스크래치 리팩토링

실제로 리팩토링 하지 않고, 코드를 이해하는 것을 목적으로 리팩토링 하는 것을 의미한다.

리팩토링을 수행하며 코드를 이해하는 시간을 갖고, 코드를 되돌린 뒤 이해한 내용을 바탕으로 다음 기능을 만들어낸다.

레거시에 테스트 코드 만들기

범위를 좁혀서 테스트 만들기

테스트 할 대상을 기존 코드와 분리해서 테스트 작성

대체 구현을 사용해서 테스트 만들기

테스트 대상이 이미 사용하고 있는 객체, 기능이 존재하는 경우 의존 대상의 구현을 대체할 대역을 만들어서 테스트 코드를 실행 시킨다.

범위를 넓혀서 테스트 만들기 - 레거시 코드에서 가장 효과적인 테스트 ✅

📝

좁은 범위 테스트가 어려운 경우

  • 의존 대상이 많아 특정 범위만 테스트 만들기 어려움
  • 테스트를 만들기 위해 변경해야 하는 코드가 너무 많음
  • 코드 의미를 알 수 없어 테스트 대상 범위를 좁히기 어려움
  • 일부 로직이 쿼리에 위치함

이 테스트 방식은, 범위를 가능한 넓혀 다양한 구성 요소 간 연동을 포함하는 테스트 코드 작성 방식이다.

예시)

테스트 환경 구성

로컬에 운영환경과 동일한 DBMS 구성

필요한 DB 테이블 생성 방법

  1. 로컬에 미리 테이블을 생성, 변경되면 반영
  2. 테스트를 실행할 때 마다 테이블 초기화(create-drop)

테스트 흐름

리팩터링

코드 변경 비용을 낮추려면 이해/변경이 쉬운 구조로 점진적으로 개선해야 한다.

테스트가 있다면 과감하게 리팩터링이 가능하다.

테스트가 없어도 필요하면 리팩터링을 진행한다.

미사용 코드 삭제

사용되지 않는 코드는 주석으로 날짜를 기록하고, 일정 기간 뒤에 삭제한다.

매직 넘버

숫자, 문자, 리터럴은 의미를 알 수 없기 때문에 의미가 담긴 상수나 열거형으로 추출한다.

이름 변경

이름 짓기가 어려운 만큼, 이름이 갖고 있는 예측되는 행위와 실제 행위는 다른 경우가 많다. 또는, 이름이 어떤 행위를 하는지 예측할 수 없다.

변수 선언과 사용

변수는 사용 직전 위치로 이동 시켜주는 것이 좋다. 이는, 변수 선언 위치와 사용 위치가 멀리 떨어져 있으면 이 변수가 어떤 값이었는지 어떤 값으로 변경 되지는 않는지 생각을 하기 때문에 부담 된다.

변수 제거

긴 코드에서 값이 바뀌는 변수가 많을수록 코드 추적이 어렵다.

조건 분기 줄이기

if-else 에서 if 가 길고, else 가 짧은 경우 조건을 뒤집어 구조를 단순화 한다.

메서드 분리

비슷한 행위을 하는 두 기능이, 하나의 메서드에서 동작하고 있는 상황에 사용한다.

클래스 분리

클래스는 항상 커지기 마련이다. 클래스를 분리 할 경우 발생하는 복잡성 보다 클래스가 커졌을 때 복잡성이 더 크다. 이럴 때, 일부 기능을 별도 클래스로 분리 하여 증가하는 복잡도를 낮춰주도록 해야 한다.

Q. 클래스로 분리하게 전, 메서드 추출로 중복을 처리 했는데 분리하고 나면 중복이 발생하지 않나? A. 클래스 분리를 진행하는 과정에서 중복이 발생하는 것은 별도의 리팩터링으로 중복을 제거하면 된다.

메서드 추출

클래스 추출

좁은 범위에 대해 테스트를 작성하는 방법론에서, 리팩터링 단계에서 클래스로 추출할 경우 테스트 하기 수월 해진다.

파라미터 값 정리

파라미터는 적을수록 좋다. 개발을 하다보면 자연스레 개수는 증가하게 되고, 코드가 변할수록 사용하지 않는 파라미터가 생길 수 있다.

가능하면 메서드에서 사용하고 있는 값만 파라미터만 사용하는 것이 좋다.