JavaScript 스타일 가이드

우리는 에어비앤비 JavaScript 스타일 가이드와 그에 딸린 린터를 사용하여 대부분의 JavaScript 스타일 가이드를 관리합니다.

에어비앤비에서 설정한 스타일 가이드 외에도 몇 가지 특정 규칙이 있습니다.

note
yarn run lint:eslint:all 또는 yarn run lint:eslint $PATH_TO_FILE 명령을 실행하여 ESLint를 로컬에서 실행할 수 있습니다.

forEach 피하기

데이터를 변형할 때 forEach를 피하십시오. 데이터를 변형할 때는 map, reduce, 또는 filter를 사용하십시오. 함수 내의 변형을 최소화하여 에어비앤비 스타일 가이드와 일치시킵니다.

// bad
users.forEach((user, index) => {
  user.id = index;
});

// good
const usersWithId = users.map((user, index) => {
  return Object.assign({}, user, { id: index });
});

매개변수의 숫자 제한

함수 또는 메소드의 매개변수가 3개 이상인 경우 대신에 매개변수로 객체를 사용하십시오.

// bad
function a(p1, p2, p3, p4) {
  // ...
};

// good
function a({ p1, p2, p3, p4 }) {
  // ...
};

DOM 이벤트를 처리하기 위해 클래스 피하기

클래스의 유일한 목적이 DOM 이벤트를 바인딩하고 콜백을 처리하는 것이라면 함수를 사용하십시오.

// bad
class myClass {
  constructor(config) {
    this.config = config;
  }
  
  init() {
    document.addEventListener('click', () => {});
  }
}

// good

const myFunction = () => {
  document.addEventListener('click', () => {
    // 콜백 처리
  });
}

생성자에 요소 컨테이너 전달하기

클래스가 DOM을 조작하는 경우 요소 컨테이너를 매개변수로 받으십시오. 이것이 유지보수하기에 더 좋고 성능적으로 유리합니다.

// bad
class a {
  constructor() {
    document.querySelector('.b');
  }
}

// good
class a {
  constructor(options) {
    options.container.querySelector('.b');
  }
}

문자열을 정수로 변환하기

문자열을 정수로 변환할 때 Number가 의미론적이고 더 가독성이 있습니다. 둘 다 허용되지만 Number가 약간의 유지보수적 이점이 있습니다.

경고: parseInt 기수 인자를 포함해야 합니다.

// bad (기수 인자 누락)
parseInt('10');

// good
parseInt("106", 10);

// good
Number("106");
// bad (기수 인자 누락)
things.map(parseInt);

// good
things.map(Number);
note
문자열이 비정수를 나타낼 수 있는 경우(즉, 소수점을 포함하는 경우) parseInt를 사용하지 마십시오. 대신 Number 또는 parseFloat를 고려하십시오.

CSS 선택자 - js- 접두어 사용

CSS 클래스가 JavaScript에서 요소를 참조하는 경우 클래스 이름에 js- 접두어를 붙이십시오.

// bad
<button class="add-user"></button>

// good
<button class="js-add-user"></button>

ES 모듈 구문

대부분의 JavaScript 파일에서는 ES 모듈 구문을 사용하여 모듈을 가져오거나 내보냅니다. 이름있는 내보내기를 선호합니다. 이름 일관성을 개선하기 때문입니다.

// bad (예외가 있는 경우, 아래 참조)
export default SomeClass;
import SomeClass from 'file';

// good
export { SomeClass };
import { SomeClass } from 'file';

기본 내보내기는 몇 가지 특정한 상황에서 허용됩니다:

  • Vue 단일 파일 컴포넌트 (SFC)
  • Vuex 변이 파일

더 많은 정보는 RFC 20를 참조하십시오.

CommonJS 모듈 구문

우리의 Node 구성은 CommonJS 모듈 구문을 요구합니다. 이름 있는 내보내기를 선호합니다.

// bad
module.exports = SomeClass;
const SomeClass = require('./some_class');

// good
module.exports = { SomeClass };
const { SomeClass } = require('./some_class');

모듈에 대한 상대 및 절대 경로

가져오는 모듈이 두 단계 이하인 경우 상대 경로를 사용하십시오.

// bad
import GitLabStyleGuide from '~/guides/GitLabStyleGuide';

// good
import GitLabStyleGuide from '../GitLabStyleGuide';

가져오는 모듈이 두 단계 이상인 경우 절대 경로를 대신 사용하십시오:

// bad
import GitLabStyleGuide from '../../../guides/GitLabStyleGuide';

// good
import GitLabStyleGuide from '~/GitLabStyleGuide';

또한, 전역 네임스페이스에 추가하지 마십시오.

비페이지 모듈에서 DOMContentLoaded 사용하지 않기

가져온 모듈은 로드될 때마다 동일하게 작동해야 합니다. DOMContentLoaded 이벤트는 웹팩에서 동적으로 로드되는 /pages/* 디렉터리에 있는 모듈에서만 허용됩니다.

XSS 피하기

콘텐츠를 설정하기 위해 innerHTML, append() 또는 html()을 사용하지 마십시오. 이것은 너무 많은 취약점을 열어 둘 수 있습니다.

ESLint

ESLint 동작 방식은 도구 가이드에서 찾을 수 있습니다.

IIFE(즉시 실행 함수 표현식)

IIFE(즉시 실행 함수 표현식)를 사용하지 마십시오. 많은 파일이 IIFE로 그 내용을 래핑하고 있지만, 이것은 Sprockets에서 웹팩으로 전환한 후에는 더는 필요하지 않습니다. 더 이상 사용하지 마십시오. 레거시 코드를 리팩토링할 때 제거해도 괜찮습니다.

전역 네임스페이스

전역 네임스페이스에 추가하지 마십시오.

// bad
window.MyClass = class { /* ... */ };

// good
export default class MyClass { /* ... */ }

부작용

최상위 부작용

export를 포함하는 모든 스크립트에서 최상위 부작용을 금지합니다:

// bad
export default class MyClass { /* ... */ }

document.addEventListener("DOMContentLoaded", function(event) {
  new MyClass();
}

생성자에서 부작용 피하기

constructor에서 비동기 호출, API 요청 또는 DOM 조작을 피하십시오. 대신 별도의 함수로 이동하십시오. 이렇게 하면 테스트를 쉽게 작성할 수 있고 단일 책임 원칙을 위반하는 것을 피할 수 있습니다.

// bad
class myClass {
  constructor(config) {
    this.config = config;
    axios.get(this.config.endpoint)
  }
}

// good
class myClass {
  constructor(config) {
    this.config = config;
  }
  
  makeRequest() {
    axios.get(this.config.endpoint)
  }
}
const instance = new myClass();
instance.makeRequest();

순수 함수와 데이터 변이

많은 작은 순수 함수를 작성하고 변이가 발생하는 곳을 최소화하는 데 노력하세요.

// 나쁨
const values = {foo: 1};

function impureFunction(items) {
  const bar = 1;
  
  items.foo = items.a * bar + 2;
  
  return items.a;
}

const c = impureFunction(values);

// 좋음
var values = {foo: 1};

function pureFunction (foo) {
  var bar = 1;
  
  foo = foo * bar + 2;
  
  return foo;
}

var c = pureFunction(values.foo);

상수를 기본형으로 내보내기

객체를 내보내는 대신 공통 네임스페이스로 상수 기본형을 내보내는 것을 선호합니다. 이는 컴파일 시 참조 검사를 개선하고 런타임에서의 실수로 인한 undefined를 방지하는 데 도움이 됩니다. 또한 번들 크기를 줄이는 데도 도움이 됩니다.

상수를 컬렉션(배열 또는 객체)로 내보내는 것은 필요에 따라 반복해야할 때에만 수행하세요. 예를 들어 프롭 유효성 검사를 위해 반복해야 할 때입니다.

// 나쁨
export const VARIANT = {
  WARNING: 'warning',
  ERROR: 'error',
};

// 좋음
export const VARIANT_WARNING = 'warning';
export const VARIANT_ERROR = 'error';

// 필요한 경우 좋음
export const VARIANTS = [VARIANT_WARNING, VARIANT_ERROR];

에러 처리

서버가 500을 돌려줄 때 내부 서버 오류에 대해 일반적인 에러 메시지를 반환해야 합니다.

백엔드가 오류를 반환할 때, 해당 오류는 사용자에게 표시할 수 있어야 합니다.

만약 어떤 이유로 인해 그것이 어렵다면, 마지막 수단으로 접두어를 붙인 특정한 에러 메시지를 선택할 수 있습니다:

  1. 백엔드에서 표시할 에러 메시지에 접두어를 붙이도록 확인하세요:

    Gitlab::Utils::ErrorMessage.to_user_facing('예시 사용자용 에러 메시지')
    
  2. app/assets/javascripts/lib/utils/error_message.js에 포함된 에러 메시지 유틸리티 함수를 사용하세요.

이 유틸리티는 두 개의 매개변수를 받습니다. 서버 응답으로 받은 오류 개체 및 기본 오류 메시지입니다. 이 유틸리티는 오류 개체의 메시지를 분석하여 메시지가 사용자에게 표시될 것인지 여부를 나타내는 접두사를 확인합니다. 메시지가 사용자에게 표시될 목적이라면 해당 유틸리티에서 그대로 반환합니다. 그렇지 않으면 매개변수로 전달된 기본 오류 메시지를 반환합니다.

import { parseErrorMessage } from '~/lib/utils/error_message';

onError(error) {
  const errorMessage = parseErrorMessage(error, genericErrorText);
}

이 접두어는 API 응답에 사용해서는 안 됩니다. 대신 REST APIGraphQL 가이드에서 오류 개체를 사용하는 방법을 따르세요.