JavaScript 스타일 가이드

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

Airbnb에서 설정한 스타일 가이드라인 외에도 아래에 나열된 몇 가지 특정 규칙이 있습니다.

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

forEach 피하기

데이터를 변형할 때 forEach를 피하세요. 데이터 변형시 forEach 대신 map, reduce 또는 filter를 사용하세요.

이것은 함수 내에서의 변형을 최소화하며, Airbnb 스타일 가이드와 일치합니다.

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

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

매개변수 수 제한

함수나 메서드에 매개변수가 3개를 초과하면, 매개변수로 객체를 사용하세요.

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

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

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

클래스의 유일한 목적이 DOM 이벤트를 바인딩하고 콜백을 처리하는 경우, 함수를 사용하는 것을 선호하세요.

// 나쁨
class myClass {
  constructor(config) {
    this.config = config;
  }

  init() {
    document.addEventListener('click', () => {});
  }
}

// 좋음

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

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

클래스가 DOM을 조작할 때, 요소 컨테이너를 매개변수로 받으세요.

이것은 더 유지보수하기 쉽고 성능이 뛰어납니다.

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

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

문자열을 정수로 변환하기

문자열을 정수로 변환할 때, Number는 의미적이며 읽기 쉬울 수 있습니다. 둘 다 허용되지만, Number가 조금 더 유지보수에 유리합니다.

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

// 나쁨 (진수 인자 누락)
parseInt('10');

// 좋음
parseInt("106", 10);

// 좋음
Number("106");
// 나쁨 (진수 인자 누락)
things.map(parseInt);

// 좋음
things.map(Number);
note
문자열이 비정수(즉, 소수 포함)를 표현할 수 있는 경우 parseInt를 사용하지 마세요. 대신 Number 또는 parseFloat를 고려하세요.

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

CSS 클래스가 JavaScript에서 요소를 참조하는 목적만 위해 사용되는 경우, 클래스 이름에 js-를 접두사로 붙이세요.

// 나쁨
<button class="add-user"></button>

// 좋음
<button class="js-add-user"></button>

ES 모듈 구문

대부분의 JavaScript 파일에 대해, ES 모듈 구문을 사용하여 모듈에서 가져오거나 내보내세요. 이름 일관성을 높이기 위해 명명된 내보내기를 선호하세요.

// 나쁨 (예외가 있음, 아래 참조)
export default SomeClass;
import SomeClass from 'file';

// 좋음
export { SomeClass };
import { SomeClass } from 'file';

기본 내보내기를 사용하는 것은 몇 가지 특정 상황에서 허용됩니다:

  • Vue 단일 파일 구성 요소(SFCs)
  • Vuex 변이 파일

자세한 내용은 RFC 20를 참조하세요.

CommonJS 모듈 구문

우리의 Node 구성은 CommonJS 모듈 구문을 필요로 합니다. 명명된 내보내기를 선호하세요.

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

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

모듈의 절대 경로 vs 상대 경로

모듈을 가져올 때 해당 모듈이 두 레벨 이하일 경우 상대 경로를 사용하세요.

// 나쁨
import GitLabStyleGuide from '~/guides/GitLabStyleGuide';

// 좋음
import GitLabStyleGuide from '../GitLabStyleGuide';

모듈을 가져올 때 해당 모듈이 두 레벨 이상일 경우 절대 경로를 사용하세요:

// 나쁨
import GitLabStyleGuide from '../../../guides/GitLabStyleGuide';

// 좋음
import GitLabStyleGuide from '~/GitLabStyleGuide';

추가로, 전역 네임스페이스에 추가하지 마세요.

페이지가 아닌 모듈에서 DOMContentLoaded를 사용하지 마세요

가져온 모듈은 매번 로드될 때 동일하게 작동해야 합니다. DOMContentLoaded 이벤트는 /pages/* 디렉토리에 로드된 모듈에서만 허용됩니다. 이 모듈들은 webpack을 사용하여 동적으로 로드되기 때문입니다.

XSS 피하기

innerHTML, append() 또는 html()를 사용하여 콘텐츠를 설정하지 마세요. 이러한 방법은 너무 많은 취약점을 열어줍니다.

ESLint

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

IIFE

IIFE(즉시 실행 함수 표현식)를 사용하지 마세요. 비록 우리가 IIFE로 내용을 감싸는 파일의 많은 예가 있지만, Sprockets에서 webpack으로 전환한 이후에는 더 이상 필요하지 않습니다. 더 이상 사용하지 마세요. 레거시 코드를 리팩토링할 때 이를 제거해도 괜찮습니다.

전역 네임스페이스

전역 네임스페이스에 추가하는 것을 피하세요.

// 나쁨
window.MyClass = class { /* ... */ };

// 좋음
export default class MyClass { /* ... */ }

부작용

최상위 부작용

export가 포함된 스크립트에서는 최상위 부작용이 금지됩니다:

// 나쁨
export default class MyClass { /* ... */ }

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

생성자에서 부작용을 피하세요

constructor에서 비동기 호출, API 요청 또는 DOM 조작을 하지 마세요. 대신 별도의 함수로 이동하세요. 이는 테스트 작성을 더 쉽게 하고 단일 책임 원칙을 위반하는 것을 피합니다.

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

// 좋음
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를 피할 수 있습니다. 또한 번들 크기를 줄이는 데 도움이 됩니다.

반복할 필요가 있는 경우(예: prop 검증기)만 상수를 컬렉션(배열 또는 객체)으로 내보내세요.

// 나쁨
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 가이드를 따라 오류 객체를 소비하는 방법을 확인하십시오.