Javascript 코드 품질 [2]

17383 단어 JavaScriptJavaScript

4. 닌자 코드

닌자 코드란 유지 보수 담당 개발자들을 힘들게 하는(가독성을 떨어뜨리는) 코드를 말한다.
본문에서는 우리가 피해야 할 코딩 스타일의 다양한 예들을 보여줄 것이다.

코드 짧게 쓰기

가능한 코드를 짧게만 쓰려고 코드 가독성을 떨어뜨리는 경우가 있다.
다음은 조건부 연산자 ?를 통해 코드를 한 줄로 줄인 경우다.

// 출처: 아주 유명한 라이브러리
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

이런식으로 조건부 연산자 중첩을 통해 간단하고 읽기 어려운 코드를 만들 수 있다.

글자 하나만 사용하기

변수 이름으로 a, b, c, ...이런식으로 사용하면 안된다.
해당 변수가 하는 일을 절대로 알 수 없다.
예외적인 상황으로 for 반복문을 쓸 때 i대신 x, y이런 변수를 사용하지 말자.

약어 사용하기

ex)

  • list -> lst
  • userAgent -> ua
  • browser -> brsr
  • 등등...

포괄적인 명사 사용하기

obj, data, value, item, elem처럼 포괄하는 개념이 많도록 변수를 선언하지 말자.
str, num과 같이 자료형과 관련된 변수명으로 선언하지 말자.
변수명 뒤에 data1, item2, ...이런식으로 숫자만 바꿔서 선언하지 말자.

철자가 유사한 단어 사용하기

date, data처럼 유사한 철자를 가진 단어를 조합해서 사용하지 말자. 개발하는데 실수도 많이 날 수 있고, 코드를 읽을 때도 식별하기 어렵다.

동의어 사용하기

같은 보여주는 내용을 displayMessage, showName 이런식으로 다른 접두어를 사용해 선언하지 말자.
같은 동작들에 같은 접두어를 사용하지 않으면 코드를 읽는 개발자는 접두어가 다른 이유를 한참 생각할 것이다.

이름 재사용하기

내부 변수를 선언하지 않고, 매개변수로 가져온 변수에만 계속 할당해서 사용하는 경우가 있는데, 이도 피해야 할 코딩 스타일이다.

function ninjaFunction(elem) {
  // 매개변수로 받아온 elem을 이용한 코드

  elem = clone(elem);

  // elem의 복제(clone)본을 이용한 코드
}

이처럼 사용하면 코드를 읽는 개발자가 어느 순간 매개변수의 값이나 형태가 바뀐 것인지 한참 찾아야 하는 상황이 나올 수 있다.

재미로 언더스코어(_) 사용하기

_name, __value처럼 같은 이름을 피하기 위해서 언더스코어를 붙이는 방법은 피해야 한다.
아무 의미 없이 붙였다고 해도 코드를 읽는 개발자는 특별한 의미를 찾으려 할 것이다.

과장 형용사 사용하기

superElement, megaFrame, niceItem와 같은 형태로도 작성하지 말자.
실제로 코드가 나이스하거나 super하다고 해도 이런건 붙이지 말자.

외부 변수 덮어쓰기

변수 이름을 짓는데 귀찮다고 외부 변수와 동일한 이름을 사용해서 덮어쓰는 것은 좋지 않은 방법이다.

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ...함수 길이가 긺...
  ...
  ... // <-- 개발자는 user와 관련된 이 부분의 코드를 수정해야 함
  ...
}

이러한 상황이라면 외부 변수가 재선언된 사항을 놓치고 함수 내부에서 의도와 다른 코드 수정을 할 수가 있다.
또는 변수를 일괄적으로 수정할 때도 실수할 수 있는 상황이다.

부작용이 있는 코드 작성하기

isReady(), checkPermission()같은 함수들은 단순하게 확인만 하고 어떤 값을 수정하는 동작은 함수에 포함시키면 안된다.
이런 함수 내부에서 체크동작 이외에 다른 동작이 포함되면 다른 부분에서 쉽게 side effect가 날 수 있다.

함수에 다양한 기능 넣기

한 함수에서 선언한 함수명의 기능 외에 다른 기능까지 포함시키지 말자.
예를 들어 validateEmail(email) 함수에서 이메일 주소를 확인해주는 기능 외에, 잘못된 이메일인 경우 alert을 띄우는 작업까지 포함시키지 말자.
함수 하나에 여러 기능들이 들어가게 된다면 해당 함수를 재사용할 수 있는 가능성이 줄어든다.

5. 테스트 자동화와 Mocha

거듭제곱 함수와 명세서

describe("pow", function() {

  it("주어진 숫자의 n 제곱", function() {
    assert.equal(pow(2, 3), 8);
  });

});

스펙(=명세서)의 3가지 주요 구성 요소

  1. describe("title", function() { ... }): 구현하고자 하는 기능에 대한 설명한다.
  2. it("유스 케이스 설명", function() { ... }): 첫 번째 인수에는 특정 케이스에 대한 설명한다. 누구나 읽을 수 있는 자연어로 적어준다.
  3. assert.equal(value1, value2): 기능이 정상적이라면 it 블록 내 코드가 정상 실행된다.

테스트 케이스 추가하기

  1. 기존 it 블록에 assert 하나 더 추가하기
describe("pow", function() {

  it("주어진 숫자의 n 제곱", function() {
    assert.equal(pow(2, 3), 8);
    assert.equal(pow(3, 4), 81);
  });

});
  1. it 블록을 하나 더 추가하기
describe("pow", function() {

  it("2를 세 번 곱하면 8입니다.", function() {
    assert.equal(pow(2, 3), 8);
  });

  it("3을 네 번 곱하면 81입니다.", function() {
    assert.equal(pow(3, 4), 81);
  });

});
  1. for 반복문으로 it블록 추가하기
describe("pow", function() {

  function makeTest(x) {
    let expected = x * x * x;
    it(`${x}을/를 세 번 곱하면 ${expected}입니다.`, function() {
      assert.equal(pow(x, 3), expected);
    });
  }

  for (let x = 1; x <= 5; x++) {
    makeTest(x);
  }

});
  1. 중첩 describe
    새로운 테스트 하위 그룹을 정의할 때 사용한다. 이렇게 새롭게 정의한 하위 그룹은 들여쓰기 형태로 출력된다.
describe("pow", function() {

  describe("x를 세 번 곱합니다.", function() {
    ...
  }
}

before/afterbeforeEach/afterEach
before/after는 전체 테스트가 실행되기 전, 후에 실행되고, beforeEach/afterEachit이 실행되기 전, 후에 매번 실행된다.
대개 초기화 용도로 사용되고 count변수나, 테스트가 바뀔 때마다 해줘야 하는 작업이 있을 때 이를 사용할 수 있다.

스펙 확장하기

n이 음수이거나 정수가 아닐 때 등의 기존에 고려하지 않은 케이스들을 추가해주면 테스트를 실패하게 된다.
이제 이렇게 추가한 테스트 케이스에 대해서도 통과할 수 있도록 코드를 개선하면 된다.

다양한 assertion

  • assert.equal(value1, value2): 동등성을 확인한다.(value1 == value2)
  • assert.strictEqual(value1, value2): 일치성을 확인한다.(value1 === value2)
  • assert.notEqual, assert.notStrictEqual: 비 동등성, 비 일치성을 확인한다.
  • assert.isTrue(value): true임을 확인한다.
  • assert.isFalse(value): false임을 확인한다.

6. 폴리필

바벨

명세서에 등록된 지 얼마 안 된 기능을 사용하려는데 특정 엔진에서 해당 기능을 제공하지 않는 경우가 있을 수 있다. 이럴 때 바벨을 사용하면 된다.

  • 바벨: 트랜스파일러(transpiler)로, 모던 javascript 코드를 구 표준을 준수하는 코드로 바꿔준다.

바벨의 주요 역할은 다음과 같다.

  1. 트랜스파일러: 최신 명세서 내용으로 작성된 코드를 구 버전 표준을 준수하는 코드로 바꿔준다. 변경된 코드 상태로 웹사이트 사용자에게 전달되어 구버전 엔진에서도 정상적으로 웹이 동작할 수 있게 된다.
  2. 폴리필: 명세서에 새로운 문법이나 기존에 없는 내장 함수가 추가되는 경우에 사용된다. 새로운 표준에 추가된 함수에서 직접 함수를 구현해 사용해야 하는 경우 폴리필을 사용한다.

트랜스파일러 vs 폴리필 차이

  • 폴리필은 런타임에 필요한 기능을 주입한다. (브라우저 실행 시점)
  • Babel은 컴파일 타임에 코드를 구 브라우저에서 사용 가능하게 변환해준다.
  • Babel은 ES6 이상에서 새롭게 추가된 Promise, Map, Set같은 전역 객체는 컴파일 타임 코드변환으로 해결이 어려워 폴리필이 필요하다.

좋은 웹페이지 즐겨찾기