[자바스크립트 패턴] 기초2 - 유지보수 가능한 코드를 만들기 위한 기초 지식

jaemin·2021년 6월 27일
0
post-thumbnail

기초2

1. eval() 피하기

자바스크립트로 코딩하면서 항상 기억해야 할 주문이 있습니다.

eval()은 사악하다! (eval() is evil)

eval() 함수는 임의의 문자열을 받아 자바스크립트 코드로 실행합니다. 매개변수로 "console.log('eval!!')"이라는 문자열을 넘기면 이를 코드로 실행해줍니다. 대부분 eval() 없이 목표를 달성할 수 있는 더 나은 방법이 존재합니다.

eval()의 사용을 피하는 이유 중 하나는 eval()의 사용은 보안 문제와도 관련되기 때문입니다. 누군가 함부로 손댄(예를 들어 네트워크에서 가져온) 코드를 실행시키게 될 수도 있습니다.

또 하나, setInterval()setTimeout() 그리고 Function() 생성자의 매개변수로 문자열을 넘길 수 있는데 이 또한 사용을 자제해야 합니다.

// 문자열로 함수를 입력해도 eval() 처럼 작동합니다.
setTimeout("myFunc()", 1000);

2. parseInt()를 통한 숫자 변환

parseInt() 함수는 코딩테스트 문제를 풀 때 많이 사용됩니다. 이 함수를 사용하면 문자열로부터 숫자 값을 얻을 수 있습니다. parseInt()의 두 번째 매개변수로 기수를 받는데, 생략하는 경우가 많지만 그래서는 안됩니다.

(eslint의 잔소리에도 불구하고 귀찮고 불필요하다고 생각해 대부분 기수를 생략했는데 반성하는 계기가 되었습니다)

만약 두 번째 기수를 생략한다면 파싱할 문자열이 0으로 시작할 경우 문제가 생길 수 있습니다. 0으로 시작하는 문자열은 ES3에서(ES5에서 변경되었지만) 8진수로 다루어집니다. 일관성 없고 예측을 벗어나느 결과를 피하려면 항상 기수 매개변수를 지정해 주어야 합니다.

let month = '06';

month = parseInt(month, 10);

위 예제에서 기수를 생략한다면 parseInt(month, 8)로 작성한 것처럼 작동합니다.

문자열을 숫자로 변환하는 또 다른 방법으로 다음과 같은 것들이 있습니다.

/*
자바스크립트 암묵적 타입 변환을 사용한 방법인데, 
가독성도 좋고 코드도 짧아 즐겨 사용합니다.
*/
+"08"; 

// Number 생성자 함수를 사용한 방법
Number("08");

이 방법들은 대체로 parseInt()보다 빠릅니다. parseInt()는 단순히 변환만 하는 것이 아니라 파싱을 하기 때문입니다. 다른 방법보다 느리지만 "08hello" 같은 것을 변환할 때 유용하게 쓰일 수 있습니다. 다른 두 방법을 사용하면 NaN이 반환되어 사용할 수 없습니다.

3. 코딩 규칙

코딩 규칙을 만들고 지키는 것은 코드의 일관성이 유지되고 예측가능해지며 읽고 이해하기 쉽게 만들어줍니다. 코딩 규칙의 세세한 면들로 회의하고 격전이 벌어지기도 합니다. 그러나, 규칙을 만들고 지키는 것이 규칙의 세부 사항보다 훨씬 중요합니다.

3.1 들여쓰기

들여쓰기는 아주 중요합니다. 들여쓰기를 지키지 않는 것보다 나쁜 것은 일관성 없이 들여쓰기를 사용한 코드입니다. 규칙을 따르는 것처럼 보이지만 오히려 혼동이 발생할 수 있기 때문입니다.

어떤 상황에서 들여쓰기 해야 할까요?

중괄호 안에 있으면 들여쓰기 하세요

3.2 중괄호

중괄호는 생략할 수 있을 때도 항상 써야 합니다. if나 for문에 명령문이 한 줄 뿐일 경우 중괄호를 생략할 수 있지만, 그런 경우에도 중괄호를 써야 합니다. 이를 통해 코드의 일관성을 유지할 수 있고 수정하기도 쉬워집니다.

// 나쁜 습관
for (let i = 0; i < 10; i++)
  alert(i);

// 여기서 코드가 한 줄 추가된다면?
for (let i = 0; i < 10; i++)
  alert(i);
  alert(i + '한줄 더!');

들여쓰기 때문에 제대로 된 코드처럼 보이지만 두 번째 alert는 루프 바깥에 있습니다. 한 줄짜리 블록도 항상 중괄호를 사용하는 것이 장기적으로는 최선책이라고 할 수 있습니다.

3.3 여는 중괄호의 위치

여는 중괄호를 같은 줄에 둘지, 다음 줄에 둘 지는 개발자들의 선호에 따라 다릅니다.

if (true) {
 alert("같은 줄에 두는게 좋아요!"); 
}
// 또는
if (true)
{
 alert("다른 줄에 두는게 근본이지!")' 
}

취향 차이일 수 있지만 자바스크립트는 자동으로 세미콜론을 삽입해주기 때문에 함부로 줄바꿈을 사용했다가는 원하는 대로 동작하지 않을 수 있습니다.

결론적으로 말하자면,

항상 중괄호를 쓰고 여는 중괄호는 선행하는 명령문과 동일한 행에 두세요

(참고로, 세미콜론도 자바스크립트 파서가 대신 넣어주지만 빼먹지 말고 쓰세요.)

3.4 공백

공백을 활용하는 것만으로도 가독성과 일관성을 향상시킬 수 있습니다. 공백을 마음껏 쓰면 파일 크기가 늘어단다고 반대하는 의견도 있지만 이는 압축을 통해 해결할 수 있습니다. 메모리 아끼지말고 공백을 사용하세요.

공백은 다음과 같은 위치에 사용하면 좋습니다.

  • for 루프 세미콜론 다음
    - for (let i = 0; i < 10; i++)
  • 배열 원소 사이사이
    - const arr = [1, 2, 3];
  • 객체 프로퍼티들 사이사이와 프로퍼티 이름과 값 사이
    - const obj = {a: 1, b: 2};
  • 함수 인자 사이사이
    - myFunc(a, b, c)
  • 함수 정의 중괄호 전
    - function myFunc() {}
  • 익명 함수 표현식에서 function 다음
    - const myFunc = function () {}

또, 모든 연산자와 피연산자 사이에도 공백을 넣어주면 좋습니다.
다시 말해, +, -, *, =, <, >, ===, &&, += 앞 뒤에 공백을 사용하세요.

마지막으로, 중괄호 앞뒤에 공백을 사용하면 좋습니다.
if-else 객체 리터럴 여는 중괄호 반복문 등에 사용하면 좋습니다.

4. 명명 규칙

유지보수 하기 쉬운 코드를 만드는 또 다른 방법은 명명 규칙입니다. 변수와 함수의 이름을 일관된 방식으로 결정하는 것이 좋습니다.

4.1 생성자를 대문자로 시작하기

자바스크립트에서는 생성자 함수를 new 키워드와 함께 호출할 수 있습니다.

const jaemin = new Person();

생성자도 함수이기 때문에 함수 이름만 보고 생성자로 쓸 함수인지, 일반 함수인지 알 수 있다면 도움이 됩니다.

function MyConstructor() {}
function myFunction() {}

위 예제처럼 함수 이름을 보자마자 어떤 것이 생성자 함수인지 알 수 있습니다.

4.2 단어 구분

변수나 함수 이름이 여러 단어로 이루어져 있다면 단어를 구분하는 규칙을 지키는 것이 좋습니다. 함수의 경우 카멜 케이스를 사용하고 변수명에는 스네이크 케이스 (ex: first_name)를 사용하면 함수와 함수가 아닌 식별자를 구별하는데 도움이 됩니다.
함수 중에서도 생성자 함수는 대문자 카멜 케이스 MyConstructor(), 일반 함수는 소문자 카멜 케이스를 사용합니다. calculateArea()

4.3 그 외의 명명 패턴

언어의 기능을 만들어내거나 대체하기 위해 명명 규칙을 사용하기도 합니다.

예를 들어, 자바스크립트에서는 상수를 정의할 방법이 없기 때문에 프로그램의 생명주기 동안 값이 변경돼서는 안되는 변수에 이름을 붙일 때 모든 글자를 대문자 스네이크 케이스로 쓰는 규칙을 적용합니다.

// 완전 소중한 상수. 절대 변경하지 마세요
const PI = 3.14
const MAX_WIDTH = 800;

대문자 스네이크 케이스는 전역 변수명에도 사용됩니다. 이렇게 하면 전역 변수가 쉽게 구별되기 때문에 전역 변수의 개수를 최소화하는 실천 방법을 상기시켜 줍니다.


기능을 암시할 수 있도록 이름을 정하는 또 다른 규칙으로 비공개 멤버에 적용하는 규칙이 있습니다. 자바스크립트에 실질적으로 비공개 메서드를 구현하는 방법이 있지만, 이름을 통해 구별할 수 있다면 더 편할 수 있습니다.
cosnt person = {
 getName: function () {
   this._getFirst() + ' ' + this._getLast();
 },
 _getFirst: function () {},
 _getLast: function () {},
};

위 예제에서 getName()은 안정된 API에 속하는 공개 메서드를 뜻하지만 _getFirst()_getLast()는 비공개 메서드를 뜻합니다. 이 메서드들은 실제로 일반적인 공개 메서드이지만, 밑줄 접두어를 사용함으로써 직접 사용해서는 안된다는 사실을 person 객체 사용자에게 경고하고 있습니다.

5. 주석 작성

(주석을 잘 안다는 정말 안좋은 습관을 가지고 있는데 항상 염두에 두고 코딩합시다...)

자신의 코드를 나 이외에 누구도 건드리지 않을 것 같아도 코드에 주석을 달아야 합니다. 과도하게 주석을 달 필요는 없지만 일반적으로 모든 함수의 매개변수와 반환 값에 대해서는 문서화할 필요가 있습니다. 또, 특이한 알고리즘이나 기법을 사용했다면 주석을 달아줍시다.

5.1 API 문서 작성

대부분의 개발자들이 문서 작성을 좋아하지 않습니다. 그런데, 문서를 작성하지 않고도 문서화할 수 있는 방법이 있습니다. JSDoc 툴킷 이나 YUIDoc 같은 도구를 통해 자동으로 문서화할 수 있습니다.

다음과 같은 형식으로 주석을 달면 API 문서를 얻을 수 있습니다.
문자열의 순서를 뒤집는 reverse() 함수의 예제로 알아봅시다.

/**
 *
 * @param {String} 반전시킬 입력 문자열
 * @return {String} 반전된 문자열
 */
const reverse = function (input) {
  // ...
  return output;
};

매개변수를 표시하는 태그는 @param이고 반환 값을 표시하는 태그는 @return입니다.

이 방식으로 주석을 달면 위에서 언급한 툴킷을 통해 API 문서를 얻을 수 있습니다.

이렇게 주석을 작성하는 것은 참고 문서를 손쉽게 만들기 위해서만은 아닙니다. 이를 통해 코드를 여러 번 다시 보게 되고 이는 결과적으로 코드의 품질 개선에 도움이 됩니다.

요약

기초1과 기초2에서 유지보수 가능한 코드를 작성하는 의미를 알아보았습니다. 이는 프로젝트의 성공뿐만 아니라 개발자와 동료의 행복과 정신건강에도 중요한 문제입니다. 배운 내용을 정리하면 다음과 같습니다.

  • 전역 변수 사용을 최소화하세요. 애플리케이션 당 전역 변수 1개가 이상적입니다.
  • 함수 내 단일한 위치에 변수를 선언하세요. 호이스팅으로 인한 부작용을 방지할 수 있습니다.
  • eval()은 evil입니다.
  • 내장 생성자 프로토타입을 확장하지 마세요.
  • 공백, 들여쓰기는 일관성있게 사용하고, 중괄호와 세미콜론은 생략하지 마세요.
  • 생성자, 함수, 변수명에 명명 규칙을 준수하세요.

Reference

이 글은 ⌜JavaScript Patterns⌟ 를 읽고 정리했습니다.

profile
프론트엔드 개발자가 되기 위해 공부 중입니다.

0개의 댓글