18
4월
2019

코드리뷰를 위한 좋은 코딩 – Javascript 편

코드리뷰(Code review)는 개발자가 작성한 코드를 다른 개발자가 리뷰어가 되어 코드를 검토하는 프로세스를 말합니다. 장점이 많아서 회사 개발 팀 등의 조직에서는 대부분 도입하고 있죠.

사실 혼자서 프로그래밍하고 유지보수를 하고 있다면 개발자 자신이 알아볼 수만 있다면 되는데요. 차후에 이 코드를 알아볼 수 있는지에 대해서는 별개 문제이지만, 어쨌든 다른 사람을 신경 쓸 필요 없이 자신이 편한대로 작업하면 될 일이죠.

하지만 회사에서 여려명이 협업을 하는 경우엔 다릅니다.

자신이 만들었던 코드는 다른 관계자도 모두 알아야 하고, 차후의 인수인계 등 얼마든지 그 코드는 자신을 떠날 수 있기 때문에 깨끗하고 읽기 쉬운 코드를 생산해야 합니다. 하지만 개발자 스스로 코드를 검토하는 것은 한계가 있으므로, (QA가 존재하는 이유와 같습니다) 제3의 개발자가 리뷰하는 코드리뷰는 필수적인 프로세스입니다.

다만 빠르게 기능을 개발해서 배포해야하는 스타트업 같은 경우, 전략적으로 코드리뷰를 하지 않고 코드 품질을 희생하는 선택을 하기도 합니다.

프로페셔널의 세계인 회사에서, 개발 기간에 일부러 시간을 투자해 별도의 프로세스를 추가한다는 것은 큰 부담이 될 수 있습니다. 따라서 코드리뷰를 도입하는 것에 대해서는 충분히 공감대를 형성해야 할 필요가 있습니다. 왜 코드리뷰를 도입할까요?


코드리뷰로 얻을 수 있는 장점

품질 향상

코드가 깨끗해지고 읽기 쉬워지며, 유지보수가 쉬워집니다. 결과적으로 코드의 품질이 크게 향상됩니다.

책임의 분산

코드리뷰를 통해 결과적으로 한명이 작성한 코드가 아닌 여러명이 함께 작성한 코드를 얻을 수 있습니다. 제품 출시 이전에 훨씬 효율적으로 버그를 잡을 수 있고, 만약 문제가 발생하더라도 책임소재를 누군가 떠안는 대신 문제 해결과 재발 방지에 집중할 수 있게 됩니다.

지식 공유 및 성장

개발자 개인이 잘 몰랐던 문법, 개선된 로직과 알고리즘, 또는 좋은 오픈소스 등의 지식을 코드리뷰를 통해 얻고 성장을 할 수 있습니다.

코드리뷰 때문에 발생하는 단점과 오해

LOC (line of code)가 증가하고 용량이 많아진다?

코드리뷰를 위해서는 효율보다는 가독성을 우선해야 하는데, 경우에 따라서는 코드 자체의 용량이 증가하여 효율이 떨어질 수 있습니다. 하지만 빌드 툴의 uglify 도구가 최적화를 담당하여 용량을 크게 줄여주고, 최근의 웹브라우저에서도 gzip으로 또 한번 압축을 하기 때문에 LOC의 증가는 큰 문제가 아닙니다.

최적화하기 어렵다?

현재는 컴퓨터나 모바일 단말기 등이 충분히 빠르고, 자바스크립트 엔진도 자체적으로 최적화하여 수행하기 때문에 코딩 단계의 미세 최적화는 노력의 낭비라고 볼 수 있습니다. → 자바스크립트 미세 최적화 피하기

만약 성능에 관해 개선하고 싶다면 크롬의 개발자도구 등을 활용하여 프로파일링 한 후 병목을 찾아 고치는 것이 좋은 방향입니다.

코드리뷰 프로세스 때문에 개발 기간이 늘어난다?

코드리뷰는 제3자가 리뷰를 하기 때문에 필연적으로 검토하는 시간과 수정하는 시간이 추가됩니다. 또한 리뷰어 입장에서 보면 자신의 코드가 아닌 제3자의 코드를 «특별히» 시간을 내서 리뷰해야 하죠. 심지어 업무가 너무 많을 때엔 코드리뷰 자체가 형식적이 되는 경우도 많은 건 사실입니다.
그래서 코드리뷰는 도입하기 전에 구성원들끼리 코드 품질 향상이라는 목표에 대해 공감대를 가져야 합니다.

코드의 양이 많거나 시간이 부족할 경우엔, 체크리스트를 설정해서 코드의 성격에 따라 코드리뷰 진행 여부를 결정하는 것도 도움이 됩니다.

남이 내 코드를 지적하는 게 싫다?

이것은 농담같은 이유이지만 무시할 수 없습니다. 코드리뷰는 절대로 상급자가 하급자를 훈련하기 위한 것이 아니라, 코드 품질을 향상하는 것이 목적입니다. 개발은 사람이 하는 것이기 때문에 하급자 대하듯 리뷰한다면 감정이 생길 수밖에 없고 코드리뷰의 목적을 달성할 수 없게 됩니다.

그래서 코드 리뷰를 할 때에는 상/하급자, 시니어/주니어를 막론하고 서로 존중하는 마음으로 리뷰를 해야 합니다. 코드에겐 불친절하게, 사람에겐 친절하게…

코드리뷰에 대해서는 별도로 크게 다룰 정도로 많은 화제가 있습니다만, 차후 얘기하기로 하고… 이 글에서는 개발자 입장에서 코드리뷰를 위한 좋은 코드를 만드는 팁을 정리해보았습니다.


1. 리뷰할 코드의 양은 최소한으로 제한해야 한다.

우선 코드의 양이 많으면 리뷰어 입장에서 리뷰하기가 쉽지 않습니다. 또한 여러가지 feature가 섞여있는 경우에도 제3자 입장에선 로직의 흐름을 따라가기 어렵기 때문에 리뷰가 어렵게 됩니다.
따라서 좋은 리뷰를 받기 위해서는 단 하나의 feature에 집중하여 개발하고, 관계없는 코드를 추가하지 않아야 합니다. 예를 들어 feature 개발을 완료했는데, 해당 작업과 관련이 없는 config 파일을 개선하고 싶다고 해서 함께 Pull Request를 보내는 것은 피해야 합니다.

불가피하게 하나의 feature임에도 불구하고 대량의 코드를 추가했다면, 온라인 코드리뷰 대신에 오프라인으로 회의를 열어 직접 설명하는 것이 좋겠네요.

2. 해당 기능이 완벽히 구현되었을 때에 PR을 보낸다.

코드를 개발하는 도중에 PR을 보내지 않고, 완벽하게 구현을 완료한 후 PR을 보내야 합니다. 만약 github을 이용한다면 travis CI를 연동하여 push를 할 때에 빌드를 바로 검사할 수 있도록 자동화하는 것도 좋은 방법입니다.

3. 코드의 효율보다는 컨벤션과 가독성을 우선한다.

코드의 효율을 개선하는 미세 최적화, 코드의 용량을 줄이는 코딩 방법, 또는 개발자 개인의 습관 등은 허용되지 않는 것은 아니지만, 이 모든 것보다 가독성을 최우선하여 프로그래밍하는 것이 좋습니다.

실제로 컨벤션은 코드리뷰에서 자주 지적되는 소모적인 이슈인데, 가능하다면 프로젝트에 ESLint, Prettier 등의 도구를 설정해서 자동으로 컨벤션과 가독성을 통일시키는 도구를 사용하는 것이 좋습니다.

4. 주석은 최소화해야 한다.

과거에는 주석을 많이 추가하는 것이 미덕처럼 여겨졌던 시기도 있었지만, 현재는 그렇지 않습니다.

로직을 별도로 설명하기 위한 주석은 달지 않는 것이 좋습니다. 바꿔 말하자면, 주석 없이 이해할 수 있는 코드를 생산해야 합니다.
개발할 때에 나중에 할 일을 “todo”로 시작하는 주석을 달아두는 경우도 많은데, 이 역시 개발을 완료하여 todo를 남기지 않거나 feature를 분리하는 것이 좋습니다.

또한 코드 자체를 주석처리하고 작동하지 않게 설정하거나, 나중에 사용하기 위해 남겨놓는 경우도 많은데, 이 역시 코드를 읽기 어렵게 만듭니다. 만약 과거의 코드를 재사용할 일이 예상되어 주석을 남겼다면, 차후에 git commit history 등으로 복원하는 것도 가능하기 때문에 주석을 삭제하는 것이 좋습니다.

주석을 남기기에 적당한 것은 코드만으로 이해할 수 없는 부분입니다. 예를 들면 결정된 기획, 관련 이슈나 위키의 URL, 타 부서와의 협의 결과 등이죠.

5. 네이밍 및 활용 – 상수, 변수 편

네이밍은 사소한 주제인 것 같으면서도 사실은 프로그래머가 가장 힘들어하는 일 중에 하나라고 하는 재미있는 통계가 있습니다.

변수/함수의 네이밍은 실제로 코드리뷰에서 가장 많이 지적되는 부분 중 하나인데요, 4번 항목과도 연관이 되지만 네이밍 만으로도 역할과 하는 일을 설명할 수 있는 로직이 바로 주석이 필요없는 좋은 로직이기 때문에 네이밍은 매우 중요합니다.

습관이나 조직에 따라 다르겠지만 일반적으로 상수와 변수는 다음과 같은 방식으로 이름을 짓습니다.

  • 상수(const) : 대문자 + underscore의 조합
    예. DELAY_TIME, HOME_ITEM_MAX_LENGTH, …
  • 변수(var, let) : 소문자 + 카멜 케이스
    예. className, packageType, tabId, …

uglify 도구를 빌드할 때 사용하기 때문에 네이밍이 길어지는 것은 걱정할 필요가 없습니다. 예외적으로 객체의 property명 등이 uglify되지 않는 경우가 있습니다만, property명을 일부러 줄이는 것은 미세 최적화에 속하기 때문에 역시 가독성을 우선하는 것이 좋습니다.

다음과 같은 룰은 설명하지 않아도 자바스크립트 프로그래밍의 기본이지요.

  • 전역변수는 최대한 사용하지 않거나 사용을 금지한다.
  • var, let, const를 항상 사용하여 변수의 scope를 적절한 범위로 설정한다.

상수나 변수를 만들 때에 최대한 알기 쉽게 기술하는 것도 좋습니다.
예를 들어 “대기 시간을 10분으로 설정”하기 위해 상수를 만들 때엔 600000이라는 단순한 숫자를 할당하는 것보다는 10 * 60 * 1000으로 풀어 쓰고 주석으로 단위를 달아주는 것이 이해하기 쉽습니다. 그리고 이런 구문은 대부분 uglify 도구에서 최적화되므로 코드의 양은 걱정하지 않아도 됩니다.

// HMM...
const DELAY_TIME = 600000;

// GOOD!!
const DELAY_TIME = 10 * 60 * 1000; // ms

아래의 예는 효율 면에서는 불필요한 변수를 만들기 때문에 좋지 않은 코드로 보이지만 사실 코드로 모든것을 설명할 수 있는 코드이며 가독성이 좋습니다. 이 또한 uglify 도구에서 최적화되므로 걱정할 필요가 없습니다.

// HMM...
doSomething(sendAPI, 600000);

// GOOD!!
const SEND_API_INTERVAL_TIME = 10 * 60 * 1000; // ms
doSomethingWithInterval(sendAPI, SEND_API_INTERVAL_TIME);

6. 네이밍 및 활용 – 함수 편

함수의 이름은 별도의 주석이 달려있지 않더라도 무슨일을 하는 함수인지 확실히 표현할 수 있도록 이름을 지어야 합니다. 정확한 네이밍을 통해 함수의 정체성을 확보하면 차후에 함수 내에 관련이 없는 코드를 자꾸 집어넣는 일도 의도적으로 줄어들게 됩니다.

최근의 트렌드인 함수형 프로그래밍에서는 특히 네이밍이 중요해졌습니다. 함수형 프로그래밍에서의 함수는 역할을 최소화하여 한가지 일에 집중하고, 책임을 확실하게 분리해야 합니다.

프로그래밍을 하다 보면 비슷한 코드를 추가하는 경우도 많은데, 이러한 중복은 최소한으로 줄이고 함수로 분리를 하는 것이 좋습니다. 이 역시 매우 중요한데, 가끔 프로그래밍하시는 분들 중에는 코드 중복을 극단적으로 싫어하고 1줄이라도 중복된다면 리팩토링해야 한다고 생각하는 분들도 있을 정도입니다.

함수의 책임을 계속 분리하다 보면 가끔 기본적으로 모든 프로그램에 공통으로 포함될만한 코드가 생산되기도 합니다. “순수함수”의 영역에 속하는 함수들인데, 예를들면 map, filter, reduce, sort, isNumber 같은 종류입니다.
이러한 함수는 직접 만들기보다는 Lodash, Underscore.js 등에서 필요한 함수만 import하는 것도 좋은 방법입니다. 이 코드들은 모든 테스트케이스를 통과하여 검증된 견고한 코드이기 때문입니다.

7. 예외처리 잘하기

예외 처리는 버그가 발생하기 쉬운 부분 중 하나입니다. 함수의 입출력을 확실히 한정시키고, 모든 입력에 대해 적절하게 처리하여 출력할 수 있도록 프로그래밍해야 합니다.

가끔 스크립트의 외부에서 받는 입력에서 문제가 생기는 경우가 있는데, 가장 흔한 예로는 서버로부터 받는 데이터, JSON 의 예외처리입니다.
JSON의 형식에 문제가 있을 경우, runtime 오류가 발생하여 실행이 중지되는데요, JSON.parse() 에 대해 try-catch를 사용해서 예외처리를 해야 합니다.

let data;
if (serverResponse) {
  try {
    data = JSON.parse(serverResponse);
  } catch(e) {
    doSomething(e); // JSON에 오류가 있을 경우의 처리
  }
}

8. 그 외에 자주 지적되는 이슈

(1) if / elseif의 중첩, switch / case의 중첩이 있을 경우

이 경우에는 중첩된 if / switch 문을 별도의 함수로 분리할 수 있는지 고민해야 합니다. ifswitch는 가독성을 해치는 대표적인 문법인데 중첩이 계속되면 읽기가 쉽지 않기 때문입니다.

(2) ifelse, switchdefault가 있는가?

예외 처리에 관련된 것인데요, 로직 상 비어있는 구멍을 만들지 않고 모든 조건에 대해 적절하게 처리될 수 있도록 프로그래밍해야 합니다.

(3) 조건의 순서

프로그램의 흐름상 만날 가능성이 높다고 생각되는 조건을 먼저 판단하는 것은 미세 최적화 방법 중 하나입니다. 예를들어 0과 0이 아닌 수가 조건이라면, 자연스럽게 “0이 아닌 수” 쪽이 true가 될 가능성이 높다고 예상할 수 있습니다.

if (lastNumber !== 0) {
  // doSomething();
} else {
  // doOtherSomething();
}

하지만 이런 미세 최적화보다는 역시 가독성을 우선으로 하는 것이 좋습니다. 가독성에 있어서는 부정보다 긍정의 조건을 먼저 두는 순서로 하면 좋다고 합니다.

(4) 비동기로 실행되는 영역은 최소화할 것

setInterval, setTimeout 구문은 비동기로 실행되기 때문에 프로그램의 흐름을 방해하는 대표적인 로직 중 하나입니다. 사용을 최소화하고, 만약 사용하더라도 최소한의 영역과 책임을 가지도록 해야 합니다.

비동기로 실행되는 로직은 항상 버그의 가능성이 높은데, ES6에 추가된 promise 또는 async, await를 활용하여 동기식으로 처리하는 기법을 적용하는 것이 좋습니다.


이상입니다.

리뷰어는 전반적인 로직을 파악할 가능성이 적기 때문에 feature에 따른 기능구현을 검토하기보다는 함수 단위의 리뷰, 일반적인 로직과 알고리즘에 대한 리뷰, 또는 컨벤션을 중점적으로 검토하는 경향이 있습니다.

따라서 개발자 입장에서는 위 내용을 항상 염두에 두고 프로그래밍한다면, 좋은 코드리뷰를 받을 가능성이 높아질 것입니다.
코드리뷰를 단번에 통과하거나, 단순하고 반복적인 리뷰는 최소화하면서 중요한 로직이나 놓친 부분에 대한 소중한 리뷰를 받을 수 있겠죠?


Update in 2019.04.19

코드리뷰를 위한 좋은 코딩이라는 것은 곧 “클린 코드”를 작성한다는 것과 일맥상통합니다. Robert C. Martin 의 책 Clean code의 내용을 javascript에 맞게 적용시켜 옮긴 github repo가 있습니다. 이 글보다 훨씬 좋은 내용인지라 링크를 추가합니다.

Clean code for javascript (원문) : https://github.com/ryanmcdermott/clean-code-javascript

클린 코드 (한국어 번역) : https://github.com/qkraudghgh/clean-code-javascript-ko

2 Responses

  1. Sun 댓글:

    유용한 정보 잘 읽었습니다!

  2. 쌩땍쥐베리 댓글:

    감솨^^
    전문가의 포스가 느껴집니다^^

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다

%d 블로거가 이것을 좋아합니다: