자기 참조 할당은 하지 않는다.
no-self-assign
자기 참조는 효과가 없으며, 불완전한 리팩터링으로 인한 오류일 수 있다.
참고 ESLint - no-self-assign
// Bad
foo = foo;
[a, b] = [a, b];
[a, ...b] = [x, ...b];
({a, b} = {a, x});
// Good
foo = bar;
let foo = foo;
[foo = 1] = [foo];
배열과 객체
배열과 객체는 반드시 리터럴로 선언한다.
no-new-object
리터럴 표기법은 생성자 함수보다 짧고 명확하며 실수를 줄일 수 있다.
참고 ESLint - no-new-object
// Bad
const emptyArr = new Array();
const arr = new Array(1, 2, 3, 4, 5);
// Bad - 객체 생성자 사용
const emptyObj = new Object();
const obj = new Object();
// Good
const emptyArr = [];
const arr = [1, 2, 3, 4, 5];
// Good
const emptyObj = {};
const obj = {
pro1: 'val1',
pro2: 'val2'
};
배열 복사 시 순환문을 사용하지 않는다.
Spread Operator
복잡한 객체를 복사할 때 전개 연산자를 사용하면 좀 더 명확하게 정의할 수 있고 가독성이 좋아진다.
참고 mdn – Spread Operator
const len = items.length;
let i;
// Bad
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// Good
const itemsCopy = [...items];
ES5의 환경에서는
Array.prototype.slice
를 사용한다. (ES5)
// Good
itemsCopy = items.slice();
객체의 메서드 표현 시 축약 메소드 표기
를 사용한다.
object-shorthanded
복잡한 객체 리터럴을 보다 명확하게 정의할 수 있다.
참고 ESLint - object-shorthanded
// Bad
const atom = {
value: 1,
addValue: function(value) {
return atom.value + value;
}
};
// Good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
}
};
메서드 문법
사용 시 메서드 사이에 개행을 추가한다.
lines-between-class-members
참고 ESLint - lines-between-class-members
// Bad
class MyClass {
foo() {
//...
}
bar() {
//...
}
}
// Good
class MyClass {
foo() {
//...
}
bar() {
//...
}
}
함수
함수는 사용 전에 선언해야 하며, 함수 선언문은 변수 선언문 다음에 오도록 한다.
함수 표현식으로 생성된 함수는 호이스팅 시 값이 할당되지 않으므로 선언 이전에 사용하면 오류가 발생한다.
// Bad - 선언 이전에 사용
const sumedValue = sum(1, 2);
const sum = function(param1, param2) {
return param1 + param2;
};
// Bad - 선언 이전에 사용
const sumedValue = sum(1, 2);
function sum(param1, param2) {
return param1 + param2;
};
// Good
const sum = function(param1, param2) {
return param1 + param2;
};
const sumedValue = sum(1, 2);
화살표 함수
함수 표현식 대신 화살표 함수를 사용한다.
prefer-arrow-callback
화살표 함수는 별도의 this 바인딩 없이 상위 컨텍스트에 바인딩되기 때문에 함수 표현식보다 혼란이 적으며 덜 장황하고 추론이 쉽다.
참고 ESLint - prefer-arrow-callback
// Bad
[1, 2, 3].map(function (x) {
const y = x + 1;
return x * y;
});
// Good
[1, 2, 3].map(x => {
const y = x + 1;
return x * y;
});
화살표 함수의 파라미터가 하나이면 괄호를 생략한다.
arrow-parens
파라미터가 하나일 때 괄호를 생략하면 화살표 함수의 장점을 살릴 수 있다.
참고 ESLint - arrow-parens
// Bad
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// Good
[1, 2, 3].map(x => x * x);
// Good
[1, 2, 3].reduce((y, x) => x + y);
암시적 반환을 사용할 경우 함수 본문 전에 개행을 하지 않는다.
implicit-arrow-linebreak
실수로 인한 return문 누락과 암시적 반환을 판단하는데 혼란을 피할 수 있다.
참고 ESLint - implicit-arrow-linebreak
// Bad
(foo) =>
bar;
(foo) =>
(bar);
(foo) =>
bar =>
baz;
// Good
(foo) => bar;
(foo) => (bar);
(foo) => bar => baz;
(foo) => (
bar()
);
템플릿 문자열
변수 등을 조합해서 문자열을 생성하는 경우 템플릿 문자열을 이용한다.
자바스크립트에서 문자열을 보다 쉽고 명확하게 다룰 수 있어 코드 복잡도가 낮아진다.
prefer-template
참고 ESlint - prefer-template
// Bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// Bad
function sayHi(name) {
return ['How are you, ', name, '?'].join();
}
// Bad - 일반적인 경우, 홑따옴표를 사용
function sayHi(name) {
return `How are you name?`;
}
// Good
function sayHi(name) {
return `How are you, ${name}?`;
}
모듈
항상 import
와 export
를 이용한다.
다른 모듈 로드 방법과 혼용하여 사용하면 코드의 일관성이 없어진다.
// Best
import {es6} from './AirbnbStyleGuide';
export default es6;
// Bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;
// Good
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
wildcard import는 사용하지 않는다.
with
문법을 지양해야 하는 것과 같은 이유로, 이름을 지정하지 않으면 모듈이 변경될 때마다 식별자 충돌이 발생할 수 있다.// Bad
import * from './AirbnbStyleGuide';
// Good
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
import문으로부터 직접 export하지 않는다.
한 줄로 표현되어 간결하기는 하지만, import와 export 하는 방법을 명확하게 구분함으로써 일관성을 유지하자.
// Bad
export {es6 as default} from './airbnbStyleGuide';
블록 구문
한 줄짜리 블록일 경우라도 {}를 생략하지 않으며 명확히 줄 바꿈 하여 사용한다.
한 줄짜리 블록일 경우 {}를 생략할 수 있지만, 이는 코드 구조를 애매하게 만든다. 당장은 두 줄을 줄일 수 있겠지만 이후 오류 발생 확률이 높아 잠재된 위험 요소가 된다.
참고 ESLint - brace-style ESLint - curly
// Bad
if(condition) doSomething();
// Bad
if (condition) doSomething();
else doAnything();
// Bad
for(let prop in object) someIterativeFn();
// Bad
while(condition) iterating += 1;
// Good
if (condition) {
...
}
// Good
if (condition) {
...
} else {
...
}
키워드와 조건문 사이에 빈칸을 사용한다.
키워드와 조건문 사이가 빼곡하면 코드를 읽기 어렵다.
참고 ESLint - keyword-spacing
// Bad
var i = 0;
for(;i<100;i+=1) {
someIterativeFn();
}
// Good
var i = 0;
for (; i < 100; i += 1) {
someIterativeFn();
}
do-while
문 사용 시 while문 끝에 세미콜론을 쓴다.
// Bad
do statement while(condition)
// Good
do {
...
} while (condition);
switch-case 사용 시 첫 번째 case문을 제외하고 case문 사용 이전에 개행한다.
// Good
switch (value) {
case 1:
doSomething1();
break;
case 2:
doSomething2();
break;
case 3:
return true;
default:
throw new Error('This shouldn\'t happen.');
}
switch-case 사용 시 각 구문은 break
, return
, throw
중 한 개로 끝나야 하며 default문이 없으면 // no default
표시를 해준다.
여러 케이스가 하나의 기능을 한다면
break
를 생략해도 좋지만, 조금이라도 다른 기능을 포함한다면 break
를 생략하지 말고 다른 방식으로 코드를 수정한다.참고 ESLint - no-fallthrough
// Bad - 케이스 1 과 2 가 서로 다른 처리를 하지만 break가 생략됨
switch (value) {
case 1:
doSomething1();
case 2:
doSomething2();
break;
case 3:
return true;
// no default
}
// Bad - default문이 없지만 아무런 표기가 없음
switch (value) {
case 1:
doSomething1();
break;
case 2:
doSomething2();
break;
case 3:
return true;
}
// Good - 여러 케이스가 하나의 처리를 할 때는 break생략 허용
switch (value) {
case 1:
case 2:
doSomething();
break;
case 3:
return true;
// no default
}
데이터형 확인하기
미리 약속된 데이터형 확인법을 사용한다. → proptypes 사용
미리 약속한 판별법으로 코드를 예측하기 쉽도록 한다. FE개발랩의 코드 유틸리티인 Toast UI CodeSnippet사용을 권장한다.
// 문자열
typeof variable === 'string'
tui.util.isString(variable)
// 숫자
typeof variable === 'number'
tui.util.isNumber(variable)
// 불린
typeof variable === 'boolean'
tui.util.isBoolean(variable)
// 객체
typeof variable === 'object'
tui.util.isObject(variable)
// 배열
Array.isArray(arrayObject)
tui.util.isArray(variable)
// 널 Null
variable === null
tui.util.isNull(variable)
// 미할당 Undefined
typeof variable === 'undefined'
variable === undefined
tui.util.isUndefined(variable)
// 엘리먼트 노드
element.nodeType === 1
tui.util.isHTMLNode(element)
조건 확인하기
삼중 등호 연산자인 ===
, !==
만 사용한다. [eqeqeq]
==
이나 !=
는 암묵적 캐스팅으로 타입에 관계없이 판단되어 조건문의 의도를 파악하기 어렵고 버그로 이어진다.참고 ESLint - eqeqeq
const numberB = 777;
// Bad
if (numberB == '777') {
...
}
// Good
if (numberB === 777) {
...
}
미리 약속된 조건 확인법을 사용한다.
미리 약속한 판별법을 사용하면 코드를 예측하기 쉽다. FE개발랩에서 만든 코드 유틸리티인 Toast UI CodeSnippet을 사용하는 것을 권장한다.
// 문자열 - 빈 문자열이 아닌가?
if (string) ...
// 문자열 - 빈 문자열인가?
if (!string) ...
// 배열 - 순회할 요소가 있는가?
if (array.length) ...
// 배열 - 빈 배열인가?
if (!array.length) ...
// 객체 - 순회할 속성이 있는가?
if (Object.hasOwnProperty) ...
// 객체 - 빈 객체인가?
if (tui.util.isEmpty(object)) ...
// 할당된 값이 있는가?
if (tui.util.isExisty(variable)) ...
// 참조변수가 참(true)인가?
if (variable) ...
// 참조변수가 거짓(false)인가?
if (!variable) ...
반환하기
return
문 바로 위는 한 칸 비워 놓는다. [newline-before-return]
다른 명령과
return
문이 붙어있으면 가독성이 좋지 않으므로 return
문 전에 한 줄 띄운다참고 ESLint - padding-line-between-statements
// Bad
function getResult() {
...
return someDataInFalse;
}
// Good
function getResult() {
...
return someDataInFalse;
}
주석
주석은 설명하려는 구문에 맞춰 들여쓰기 한다.
// Bad
function someFunction() {
...
// statement에 관한 주석
statements
}
// Good
function someFunction() {
...
// statement에 관한 주석
statements
}
문장 끝에 주석을 작성할 경우, 한 줄 주석
을 사용하며 공백을 추가한다.
// Bad
var someValue = data1;//주석 표시 전후 공백
// Bad
var someValue = data1; /* 여러 줄 주석 */
// Good
var someValue = data1; // 주석 표시 전후 공백
여러 줄 주석
을 작성할 때는
의 들여쓰기를 맞춘다. 주석의 첫 줄과 마지막 줄은 비워둔다.
// Bad - '*' 표시의 정렬
/*
* 주석내용
*/
// Bad - 주석의 첫 줄에는 기술하지 않는다
...
/* var foo = '';
* var bar = '';
* var quux;
*/
// Good - '*' 표시의 정렬을 맞춘다
/*
* 주석내용
*/
코드 블럭 주석 처리
를 위해서는 한 줄 주석을 사용한다.
// Bad - 여러 줄 주석을 사용
...
/*
* var foo = '';
* var bar = '';
* var quux;
*/
// Good - 한 줄 주석 사용
...
// var foo = '';
// var bar = '';
// var quux;
공백
키워드, 연산자와 다른 코드 사이에 공백이 있어야 한다. [keyword-spacing]
빼곡한 연산자와 키워드가 있는 코드는 읽기 어렵다.
// Bad
var value;
if(typeof str==='string') {
value=(a+b);
}
// Good
var value;
if (typeof str === 'string') {
value = (a + b);
}
시작 괄호 바로 다음과 끝 괄호 바로 이전에 공백이 있으면 안 된다.
[object-curly-spacing] []
구조분해 할당과의 설정 충돌? 링크
// Bad - 괄호 안에 공백
if ( typeof str === 'string' )
// Bad - 괄호 안 공백
var arr = [ 1, 2, 3, 4 ];
// Good
if (typeof str === 'string') {
...
}
// Good
var arr = [1, 2, 3, 4];
콤마 다음에 값이 올 경우 공백이 있어야 한다.
콤마로 구분된 아이템 간에 간격을 두면 가독성이 향상된다. FE개발랩에서는 콤마 바로 뒤에 공백을 추가한다.
// Bad - 콤마 뒤 공백
var arr = [1,2,3,4];
// Good
var arr = [1, 2, 3, 4];
맺음말
지금까지 코딩 컨벤션에 대해 알아보았다. 코딩 컨벤션은 자바스크립트 프로젝트에서 선택이 아닌 필수로 가지고 있어야 하는 코딩 스타일 규약이다. 이 가이드는 FE개발랩에서 사용하는 자바스크립트의 기본 스타일 규칙을 문서화했다. 사용하는 프레임워크와 프로젝트 성격에 따라 더 상세한 코딩 컨벤션을 적용할 수 있다. 이 가이드가 가독성 좋고 관리가 용이한 코드를 작성하는데 도움이 되길 바란다.
번호 | 규칙명 | 내용 |
1 | object-curly-spacing | 시작 괄호 바로 다음과 끝 괄호 바로 이전에 공백이 있으면 안 된다. |
2 | keyword-spacing | 키워드, 연산자와 다른 코드 사이에 공백이 있어야 한다. |
3 | newline-before-return | return 문 바로 위는 한 칸 비워 놓는다. |
4 | eqeqeq | 삼중 등호 연산자인 === , !== 만 사용한다. |
5 | prefer-template | 변수 등을 조합해서 문자열을 생성하는 경우 템플릿 문자열을 이용한다. |
6 | implicit-arrow-linebreak | 암시적 반환을 사용할 경우 함수 본문 전에 개행을 하지 않는다. |
7 | arrow-parens | 화살표 함수의 파라미터가 하나이면 괄호를 생략한다. |
8 | prefer-arrow-callback | 함수 표현식 대신 화살표 함수를 사용한다. |
9 | lines-between-class-members | 메서드 문법 사용 시 메서드 사이에 개행을 추가한다. |
10 | object-shorthanded | 객체의 메서드 표현 시 축약 메소드 표기 를 사용한다. |
11 | no-var | var 변수를 사용하지 않는다. |
12 | no-self-assign | 자기 참조 할당은 하지 않는다. |