JavaScript 스타일 가이드

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

Airbnb에 의해 설정된 스타일 가이드 외에도 몇 가지 구체적인 규칙이 있습니다.

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

forEach 피하기

데이터를 변형할 때 forEach를 피하십시오. 데이터를 변형할 때는 map, reduce 또는 filter를 사용해주세요. 함수 내에서의 변형을 최소화하여 Airbnb 스타일 가이드와 일치시키기 위함입니다.

// 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);

참고: 만약 문자열이 정수가 아닐 수 있는 경우(즉, 소수점이 포함된 경우), parseInt를 사용하지 마십시오. 대신 NumberparseFloat를 고려해주세요.

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

CSS 클래스가 요소를 참조하는 데에만 사용된다면, 클래스 이름에 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 Single File Components (SFCs) - Vuex 변이 파일

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

CommonJS 모듈 구문

우리의 Node 구성에는 CommonJS 모듈 구문이 필요합니다. 이름 있는 내보내기를 선호해주세요.

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

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

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

가져오고 있는 모듈이 2단계 이상 위에 있는 것이 아니라면 상대 경로를 사용해주세요.

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

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

가져오고 있는 모듈이 2단계 이상 위에 있는 경우, 대신 절대 경로를 사용해주세요.

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

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

추가적으로, 글로벌 네임스페이스에 추가하지 마십시오.

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

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

XSS 피하기

컨텐츠를 설정하기 위해 innerHTML, append() 또는 html()을 사용하지 마십시오. 너무 많은 취약점으로 연결됩니다.

ESLint

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

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

IIFE(즉시 실행 함수 표현식) 사용을 피하십시오. 우리에게는 IIFE로 내용을 랩핑하는 파일의 많은 예제가 있지만, 이는 더 이상 Sprockets에서 webpack으로의 전환 이후에는 더이상 필요하지 않습니다. 더 이상 사용하지 않고, 레거시 코드를 리팩토링할 때 제거해주세요.

글로벌 네임스페이스

글로벌 네임스페이스에 추가하지 마십시오.

// 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 API 또는 GraphQL 가이드를 따라 오류 객체를 소비하는 방법을 따르세요.