참고: 이 가이드는 Airbnb JavaScript style Guide에서 eslint에서 더 이상 사용하지 않는 규칙, node.js 버전이 업그레이드 됨에 따라 필요 없어진 규칙, 이견이 많은 규칙들을 제거하였습니다.
-
1.1 모든 참조에는
var
대신const
를 사용하세요.
why? 참조를 재할당 할 수 없게 함으로써, 이해하기 어려운 동시에 버그로 이어지는 코드를 방지합니다.
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
-
1.2 만약 참조를 재할당 해야 한다면
var
대신let
을 사용하세요.
why?
var
처럼 함수스코프를 취하는 것 보다는 블록스코프를 취하는let
이 더 낫습니다.// bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; }
-
1.3
let
과const
는 둘 다 블록스코프라는 것을 유의하세요.// const와 let은 선언된 블록 안에서만 존재합니다. { let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError
-
2.1 객체를 생성할 때는 리터럴 문법을 사용하세요.
// bad const item = new Object(); // good const item = {};
-
2.2 동적 속성을 갖는 객체를 생성할 때는 속성 계산명을 사용하세요.
why? 이를 통해 객체의 모든 속성을 한 곳에서 정의할 수 있습니다.
function getKey(k) { return `a key named ${k}`; } // bad const obj = { id: 5, name: 'San Francisco', }; obj[getKey('enabled')] = true; // good const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true, };
-
2.3 메소드의 단축구문을 사용하세요.
// bad const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { value: 1, addValue(value) { return atom.value + value; }, };
-
2.4 속성의 단축구문을 사용하세요.
why? 더 짧고 설명적입니다.
const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, };
-
2.5 유효하지 않은 식별자에만 따옴표 속성을 사용하세요.
why? 더 읽기 쉽습니다. 이렇게 하면 구문 하이라이팅이 잘 되고, 많은 자바스크립트 엔진이 더 쉽게 최적화 할 수 있습니다.
// bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // good const good = { foo: 3, bar: 4, 'data-blah': 5, };
but! 숫자 리터럴을 속성 키로 사용하는 경우에는 따온표를 사용하는게 좋습니다. 의도치 않은 결과가 발생할 수 있습니다. 아래와 같은 경우 속성 이름으로 사용되기 전에 문자열로 강제 변환되기 때문에 둘 다 속성이 "100"이됩니다.
// bad var object = { 1e2: 1, 100: 2 }; // 중복된 속성 이름을 가질 수 없기에 마지막으로 지정한 값으로 대체 console.log(object) // {'100': 2 } // good var object = { '1e2': 1, '100': 2 }; console.log(object) // { "100": 2, "1e2": 1 }
-
2.6 객체에 대해 얕은 복사를 할 때는
Object.assign
대신 객체 전개 구문을 사용하세요. 특정 속성이 생략된 새로운 개체를 가져올 때는 객체 나머지 연산자(object rest operator)를 사용하세요.
// very bad const original = { a: 1, b: 2 }; const copy = Object.assign(original, { c: 3 }); // `original`을 변조합니다 ಠ_ಠ delete copy.a; // 그래서 이렇게 합니다 // bad const original = { a: 1, b: 2 }; const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } // good const original = { a: 1, b: 2 }; const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
-
3.1 배열을 생성할 때 리터럴 구문을 사용하세요.
why? Array 생성자를 사용하면 몇 가지 문제가 발생할 수 있습니다.
첫째, Array 생성자를 사용할 때 한 가지 인수만을 전달할 수 있습니다. 이것은 오해의 소지가 있을 수 있고 버그를 일으킬 수 있습니다.
둘째, Array라는 전역 객체가 다른 목적으로 재정의될 수 있으므로 코드가 예상대로 동작하지 않을 수 있습니다.// bad const items = new Array(); // good const items = [];
-
3.2 매핑할 때는 전개 구문
...
대신Array.from
을 사용하세요. 중간 배열 생성을 방지와 성능 상의 문제가 있기 때문입니다.// bad const baz = [...foo].map(bar); // good const baz = Array.from(foo, bar);
-
3.3 배열 메소드 콜백에는 리턴 구문을 사용하세요.
why? 종종 리턴 구문을 넣어야하는 데 실수로 넣지 않을 때가 있습니다. 만약 반환을 사용하고 싶지 않거나 반환된 결과가 필요하지 않은 경우에는
forEach
사용을 고려하세요.// good [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); // good [1, 2, 3].map(x => x + 1); // bad - no returned value means `acc` becomes undefined after the first iteration [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); acc[index] = flatten; }); // good [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); acc[index] = flatten; return flatten; }); // bad inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } else { return false; } }); // good inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } return false; });
-
4.1 하나의 객체에서 여러 속성에 접근할 때는 객체 비구조화를 사용하세요.
why? 비구조화는 속성들을 위한 임시 참조를 만들지 않도록 해주고, 객체의 반복적인 접근을 방지합니다. 반복적인 객체 접근은 중복 코드와 실수를 만들어내고, 더 많은 코드를 읽게 합니다. 또한 객체 비구조화는 블록에서 사용되는 객체의 구조를 저으이하는 단일한 위치를 제공함으로써 어떤 것이 사용되는지 알아내기 위해 모든 블록을 읽지 않아도 되도록 해줍니다.
// bad function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return `${firstName} ${lastName}`; } // good function getFullName(user) { const { firstName, lastName } = user; return `${firstName} ${lastName}`; } // best function getFullName({ firstName, lastName }) { return `${firstName} ${lastName}`; }
-
4.2 배열 비구조화를 사용하세요.
const arr = [1, 2, 3, 4]; // bad const first = arr[0]; const second = arr[1]; // good const [first, second] = arr;
-
5.1 문자열을 생성하는 경우, 문자열 연결 대신 템플릿 문자열을 사용하세요.
why? 템플릿 문자열은 덧붙이기와 줄바꿈을 제공하는 간결한 문법으로 가독성을 높여줍니다.
// 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}?`; }
-
5.3 문자열에 불필요한 이스케이프 문자를 사용하지 마세요.
why? 백슬래시는 가독성을 해치기 때문에 필요할 때만 사용해야 합니다.
// bad const foo = '\'this\' \i\s \"quoted\"'; // good const foo = '\'this\' is "quoted"'; const foo = `my name is '${name}'`;
-
6.1 즉시 호출 함수 표현식을 괄호로 감싸세요.
why? 즉시 호출 함수 표현식은 하나의 단위이며, 괄호로 이것을 감싸면 괄호 안의 표현을 명확하게 해주기 때문입니다. 모듈을 어디에서나 사용한다면 즉시 호출 표현식은 전혀 필요하지 않다는 점을 주의하세요.
// 즉시 호출 함수 표현식 (IIFE) (function () { console.log('Welcome to the Internet. Please follow me.'); }());
-
6.2 함수 이외의 불록(
if
,while
, 등)에서 함수를 선언하지 마세요. 브라우저는 이를 허용하겠지만, 모두 다르게 해석합니다.no-loop-func
- 참고 : ECMA-262 명세는
블록
을 구문의 일종으로 정의하고 있지만 함수선언은 구문이 아닙니다.
// bad if (currentUser) { function test() { console.log('Nope.'); } } // good let test; if (currentUser) { test = () => { console.log('Yup.'); }; }
-
6.3 절대 매개변수 이름을
arguments
라고 짓지 마세요. 이것은 함수 스코프에 전해지는arguments
객체의 참조를 덮어써 버립니다.
// bad function foo(name, options, arguments) { // ... } // good function foo(name, options, args) { // ... }
-
6.4 절대
arguments
를 사용하지마세요. 대신 나머지 문법(rest syntax)...
를 사용하세요.
why?
...
을 사용하면 몇 개의 매개변수를 이용하고 싶은지 확실히 할 수 있습니다. 더 나아가, 나머지 인자(rest arguments)는arguments
와 같은 Array-like 객체가 아닌 진짜 Array입니다.// bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); }
-
6.5 기본 매개변수는 항상 뒤쪽에 두세요.
// bad function handleThings(opts = {}, name) { // ... } // good function handleThings(name, opts = {}) { // ... }
-
6.6 절대로 새로운 함수를 만들기 위해 함수 생성자를 사용하지 마세요.
why? 이러한 방법으로 문자열을 평가해 함수를 만드는 것은
eval()
과 같은 수준읜 취약점을 만듭니다.// bad var add = new Function('a', 'b', 'return a + b'); // still bad var subtract = Function('a', 'b', 'return a - b');
-
6.7 함수 시그니처에 공백을 넣으세요.
why? 일관성을 갖는 것이 좋습니다. 그리고 이렇게 하면 이름을 추가하거나 지울 때 공백을 건드릴 필요가 없게 됩니다.
// bad const f = function(){}; const g = function (){}; const h = function() {}; // good const x = function () {}; const y = function a() {};
-
6.8 절대로 매개변수를 바꾸지 마세요.
why? 매개변수로 전달된 객체를 조작하면 원래 호출처에서 원치 않는 사이드 이펙트를 일으킬 수 있습니다.
// bad function f1(obj) { obj.key = obj.key || 1; // Mutate to normalize // Use obj.key } // good function f2(obj) { const key = obj.key || 1; // Normalize without mutating // Use key }
해당 규칙에는 이견이 많습니다.
관련 대안
다음 이름으로 정의한 매개변수는 이 규칙을 무시합니다.acc
accumulator
e
ctx
context
req
request
res
response
$scope
staticContext
-
6.9 절대로 매개변수를 재할당하지 마세요.
why? 매개변수를 재할당하는 것은 예측할 수 없는 결과를 불러 일으킵니다. 특히
arguments
객체에 접근할 때 말이죠. 또한 V8에서 최적화 문제를 일으킬 수도 있습니다.// bad function f1(a) { a = 1; // ... } function f2(a) { if (!a) { a = 1; } // ... } // good function f3(a) { const b = a || 1; // ... } function f4(a = 1) { // ... }
-
6.10 가변 인자 함수를 호출할 때는 전개 구문
...
을 사용하세요.
why? 훨씬 더 깔끔합니다. 컨텍스트를 제공할 필요도 없고,
apply
로new
를 쉽게 구성할 수도 없습니다.// bad const x = [1, 2, 3, 4, 5]; console.log.apply(console, x); // good const x = [1, 2, 3, 4, 5]; console.log(...x); // bad new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); // good new Date(...[2016, 8, 5]);
-
6.11 여러 줄의 시그니처 또는 호출을 취하는 함수는 이 가이드에 있는 다른 것들처럼 들여쓰기가 되어야 합니다. 한줄에 각 항목을 하나씩 두고, 마지막 항목에 쉼표를 넣습니다.
// bad function foo(bar, baz, quux) { // ... } // good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, );
-
7.1 (인라인 콜백을 전달할 때 같이) 익명함수를 사용할 때는 화살표 함수 표현을 사용하세요.
why? 화살표 함수는 그 컨텍스트의
this
에서 실행하는 버전의 함수를 만들기 때문입니다. 이것은 보통 원하는대로 작동하고, 보다 간결합니다.// 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; });
-
7.2 하나의 식으로 구성된 함수가 사이드 이펙트 없는 표현식을 반환하는 경우, 중괄호를 생략하고 암시적 반환을 사용할 수 있습니다. 그 외에는 중괄호를 그대로 두고,
return
문도 사용하세요.
why? Syntactic sugar이기 때문입니다. 여러 함수가 연결된 경우 읽기 쉬워집니다.
// bad [1, 2, 3].map(number => { const nextNumber = number + 1; `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map(number => `A string containing the ${number}.`); // good [1, 2, 3].map((number) => { const nextNumber = number + 1; return `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map((number, index) => ({ [index]: number, })); // 암시적 반환없이 사이드 이펙트를 수반합니다 function foo(callback) { const val = callback(); if (val === true) { // callback이 참을 반환하면 뭔가를 수행합니다 } } let bool = false; // bad foo(() => bool = true); // good foo(() => { bool = true; });
-
7.3 명확성과 일관성을 위해 항상 인자를 괄호로 감싸세요.
why? 인자를 추가하거나 제거할 때 변경 사항을 최소화할 수 있습니다.
// bad [1, 2, 3].map(x => x * x); // good [1, 2, 3].map((x) => x * x); // bad [1, 2, 3].map(number => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // good [1, 2, 3].map((number) => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // bad [1, 2, 3].map(x => { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; });
-
7.4 화살표 함수 구문(
=>
)과 비교 연산자(<=
,>=
)를 헷갈리게 하지 마세요.
// bad const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; // bad const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; // good const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); // good const itemHeight = (item) => { const { height, largeSize, smallSize } = item; return height > 256 ? largeSize : smallSize; };
-
7.5 암시적 반환을 하는 화살표 함수 몸체의 위치를 적절히 설정하세요.
// bad (foo) => bar; (foo) => (bar); // good (foo) => bar; (foo) => (bar); (foo) => ( bar )
-
8.1 클래스는 생성자가 명시되지 않은 경우 기본 생성자를 갖습니다. 빈 생성자 함수나 부모 클래스로 위임하는 함수는 불필요합니다.
// bad class Jedi { constructor() {} getName() { return this.name; } } class Rey extends Jedi { constructor(...args) { super(...args); } } // good class A { } class A { constructor () { doSomething(); } } class B extends A { constructor() { super('foo'); } } class B extends A { constructor() { super(); doSomething(); } }
-
8.2 중복되는 클래스 멤버를 만들지 마세요.
why? 중복된 클래스 멤버를 선언하면 암묵적으로 마지막 멤버가 적용됩니다. 중복은 확실히 버그입니다.
// bad class Foo { bar() { return 1; } bar() { return 2; } } // good class Foo { bar() { return 1; } } // good class Foo { bar() { return 2; } }
-
8.3 클래스 메소드는 외부 라이브러리나 프레임워크가 구체적으로 비정적 메소드를 요구하지 않는 이상
this
를 사용하거나 해당 메소드를 정적 메소드로 만들어야 합니다. 인스턴스 메서드는 수신자의 속성에 따라 다르게 동작함을 나타내야 합니다.
// bad class Foo { bar() { console.log('bar'); } } // good - this를 사용했습니다 class Foo { bar() { console.log(this.bar); } } // good - constructor가 면제됩니다 class Foo { constructor() { // ... } } // good - 정적 메소드는 this를 사용하지 않는다고 예상할 수 있습니다 class Foo { static bar() { console.log('bar'); } }
-
9.1 같은 경로는 한 곳에서 import하세요.
why? 같은 경로에서 import하는 여러 줄의 코드는 유지보수를 어렵게 만듭니다.
// bad import foo from 'foo'; // … 또 다른 imports … // import { named1, named2 } from 'foo'; // good import foo, { named1, named2 } from 'foo'; // good import foo, { named1, named2, } from 'foo';
-
9.2 가변 바인딩을 export하지 마세요.
why? 변조는 일반적으로 피해야 하지만, 가변 바인딩을 export할 때는 특히 그렇습니다. 이 기술이 어떤 특별한 상황에 필요할 수도 있지만, 일반적으로는 상수 참조만 export되어야 합니다.
// bad let foo = 3; export { foo }; // good const foo = 3; export { foo };
-
9.3 한가지만 export하는 모듈에서는 이름 붙여진 export보다는 default export를 사용하세요.
why? 하나만 export하는 파일의 가독성과 유지보수성이 더 좋기 때문입니다.
// bad export function foo() {} // good export default function foo() {}
-
9.4 모든
import
구문을 다른 구문들 위에 두세요.
why?
import
구문은 호이스트되기 때문에 이것을 가장 위에 두면 예상치 못한 결과를 막을 수 있습니다.// bad import foo from 'foo'; foo.init(); import bar from 'bar'; // good import foo from 'foo'; import bar from 'bar'; foo.init();
-
9.5 여러 줄에 걸친 import는 여러 줄의 배열이나 객체 리터럴처럼 들여쓰기하세요.
why? 스타일 가이드에 있는 다른 모든 중괄호 블록들 처럼 중괄호는 같은 들여쓰기 규칙을 따릅니다. 콤마가 그렇듯이 말이죠.
// bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path';
-
9.6 모듈 import 구문에서 Webpack loader 구문을 사용하지 마세요.
why? import에서 Webpack 구문을 사용하면 이 코드가 모듈 번들러에 연결되기 때문입니다. loader 구문은
webpack.config.js
에서 사용하세요.// bad import fooSass from 'css!sass!foo.scss'; import barCss from 'style!css!bar.css'; // good import fooSass from 'foo.scss'; import barCss from 'bar.css';
-
9.7 자바스크립트 파일 확장자를 명시하세요.
why? Node.js에 관해 후회하는 10가지-Ryan Dahl
airbnb/javascript#2469 (comment)import foo from './foo.js'; import bar from './bar.json'; import Component from './Component.jsx'; import express from 'express/index.js';