InfoGrab DocsInfoGrab Docs

JavaScript 스타일 가이드

요약

대부분의 JavaScript 스타일 지침은 Airbnb JavaScript 스타일 가이드와 함께 제공되는 린터를 사용하여 관리합니다. Airbnb에서 정한 스타일 지침 외에도, 아래에 몇 가지 특정 규칙이 추가로 나열되어 있습니다.

대부분의 JavaScript 스타일 지침은 Airbnb JavaScript 스타일 가이드와 함께 제공되는 린터를 사용하여 관리합니다.

Airbnb에서 정한 스타일 지침 외에도, 아래에 몇 가지 특정 규칙이 추가로 나열되어 있습니다.

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

forEach 사용 지양#

데이터를 변경(mutate)할 때는 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', () => {
    // handle callback here
  });
}

생성자에 엘리먼트 컨테이너 전달#

클래스가 DOM을 조작하는 경우, 엘리먼트 컨테이너를 매개변수로 받으세요. 이렇게 하면 유지 관리성과 성능이 향상됩니다.

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

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

문자열을 정수로 변환#

문자열을 정수로 변환할 때, Number는 의미적으로 더 읽기 쉽습니다. 둘 다 허용되지만 Number는 유지 관리성 면에서 약간의 이점이 있습니다.

parseInt는 반드시 기수(radix) 인수를 포함해야 합니다.

// bad (missing radix argument)
parseInt('10');

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

// good
Number("106");
// bad (missing radix argument)
things.map(parseInt);

// good
things.map(Number);

문자열이 정수가 아닌 값(소수점을 포함한 숫자)을 나타낼 수 있는 경우, parseInt를 사용하지 마세요. 대신 Number 또는 parseFloat을 사용하는 것을 고려하세요.

CSS 셀렉터 - js- 접두사 사용#

CSS 클래스가 JavaScript에서 엘리먼트 참조용으로만 사용되는 경우, 클래스 이름 앞에 js-를 붙이세요.

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

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

ES 모듈 문법#

대부분의 JavaScript 파일에서는 ES 모듈 문법을 사용하여 모듈을 가져오거나 내보내세요. 이름 일관성을 높이기 위해 named export를 권장합니다.

// bad (with exceptions, see below)
export default SomeClass;
import SomeClass from 'file';

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

기본 export(default export)는 다음과 같은 특정 상황에서 허용됩니다:

  • Vue Single File Components (SFCs)

  • Vuex mutation 파일

자세한 내용은 RFC 20을 참고하세요.

CommonJS 모듈 문법#

Node 설정에서는 CommonJS 모듈 문법이 필요합니다. named export를 권장합니다.

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

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

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

가져오려는 모듈이 두 단계 미만 상위에 있는 경우 상대 경로를 사용하세요.

// 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/* 디렉터리에서 로드되는 모듈에서만 허용됩니다. 이 모듈들은 webpack으로 동적으로 로드되기 때문입니다.

XSS 방지#

콘텐츠를 설정하기 위해 innerHTML, append(), html()을 사용하지 마세요. 너무 많은 취약점을 열어놓게 됩니다.

ESLint#

ESLint 동작에 대한 내용은 툴링 가이드에서 확인할 수 있습니다.

IIFEs#

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 조작을 수행하지 마세요. 대신 별도의 함수로 이동시키세요. 이렇게 하면 테스트 작성이 쉬워지고 단일 책임 원칙(Single Responsibility Principle)을 위반하지 않게 됩니다.

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

순수 함수와 데이터 변경#

작고 순수한 함수를 많이 작성하고, 변경이 발생하는 위치를 최소화하도록 노력하세요.

// bad
const values = {foo: 1};

function impureFunction(items) {
  const bar = 1;

  items.foo = items.a * bar + 2;

  return items.a;
}

const c = impureFunction(values);

// good
var values = {foo: 1};

function pureFunction (foo) {
  var bar = 1;

  foo = foo * bar + 2;

  return foo;
}

var c = pureFunction(values.foo);

상수를 원시값으로 내보내기#

공통 네임스페이스를 가진 상수 원시값을 객체로 내보내는 것보다 개별 내보내기를 권장합니다. 이렇게 하면 컴파일 타임에 더 나은 참조 검사가 가능하고 런타임에서 우발적인 undefined 발생을 방지하는 데 도움이 됩니다. 또한 번들 크기를 줄이는 데도 도움이 됩니다.

상수를 반복 처리(iterate)해야 할 경우(예: prop 검증기)에만 컬렉션(배열 또는 객체)으로 내보내세요.

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

// good
export const VARIANT_WARNING = 'warning';
export const VARIANT_ERROR = 'error';

// good, if the constants need to be iterated over
export const VARIANTS = [VARIANT_WARNING, VARIANT_ERROR];

오류 처리#

서버가 500을 반환하는 내부 서버 오류의 경우, 일반적인 오류 메시지를 반환해야 합니다.

백엔드가 오류를 반환할 때, 오류는 사용자에게 다시 표시하기에 적합해야 합니다.

어떤 이유로 이것이 어려운 경우, 최후의 수단으로 접두사를 사용하여 특정 오류 메시지를 선택할 수 있습니다:

  • 백엔드가 다음과 같이 사용자에게 표시될 오류 메시지에 접두사를 붙이도록 하세요:
Gitlab::Utils::ErrorMessage.to_user_facing('Example user-facing error-message')
  • 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 가이드를 따르세요.

JavaScript 스타일 가이드

GitLab v19.1
원문 보기
요약

대부분의 JavaScript 스타일 지침은 Airbnb JavaScript 스타일 가이드와 함께 제공되는 린터를 사용하여 관리합니다. Airbnb에서 정한 스타일 지침 외에도, 아래에 몇 가지 특정 규칙이 추가로 나열되어 있습니다.

대부분의 JavaScript 스타일 지침은 Airbnb JavaScript 스타일 가이드와 함께 제공되는 린터를 사용하여 관리합니다.

Airbnb에서 정한 스타일 지침 외에도, 아래에 몇 가지 특정 규칙이 추가로 나열되어 있습니다.

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

forEach 사용 지양#

데이터를 변경(mutate)할 때는 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', () => {
    // handle callback here
  });
}

생성자에 엘리먼트 컨테이너 전달#

클래스가 DOM을 조작하는 경우, 엘리먼트 컨테이너를 매개변수로 받으세요. 이렇게 하면 유지 관리성과 성능이 향상됩니다.

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

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

문자열을 정수로 변환#

문자열을 정수로 변환할 때, Number는 의미적으로 더 읽기 쉽습니다. 둘 다 허용되지만 Number는 유지 관리성 면에서 약간의 이점이 있습니다.

parseInt는 반드시 기수(radix) 인수를 포함해야 합니다.

// bad (missing radix argument)
parseInt('10');

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

// good
Number("106");
// bad (missing radix argument)
things.map(parseInt);

// good
things.map(Number);

문자열이 정수가 아닌 값(소수점을 포함한 숫자)을 나타낼 수 있는 경우, parseInt를 사용하지 마세요. 대신 Number 또는 parseFloat을 사용하는 것을 고려하세요.

CSS 셀렉터 - js- 접두사 사용#

CSS 클래스가 JavaScript에서 엘리먼트 참조용으로만 사용되는 경우, 클래스 이름 앞에 js-를 붙이세요.

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

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

ES 모듈 문법#

대부분의 JavaScript 파일에서는 ES 모듈 문법을 사용하여 모듈을 가져오거나 내보내세요. 이름 일관성을 높이기 위해 named export를 권장합니다.

// bad (with exceptions, see below)
export default SomeClass;
import SomeClass from 'file';

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

기본 export(default export)는 다음과 같은 특정 상황에서 허용됩니다:

  • Vue Single File Components (SFCs)

  • Vuex mutation 파일

자세한 내용은 RFC 20을 참고하세요.

CommonJS 모듈 문법#

Node 설정에서는 CommonJS 모듈 문법이 필요합니다. named export를 권장합니다.

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

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

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

가져오려는 모듈이 두 단계 미만 상위에 있는 경우 상대 경로를 사용하세요.

// 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/* 디렉터리에서 로드되는 모듈에서만 허용됩니다. 이 모듈들은 webpack으로 동적으로 로드되기 때문입니다.

XSS 방지#

콘텐츠를 설정하기 위해 innerHTML, append(), html()을 사용하지 마세요. 너무 많은 취약점을 열어놓게 됩니다.

ESLint#

ESLint 동작에 대한 내용은 툴링 가이드에서 확인할 수 있습니다.

IIFEs#

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 조작을 수행하지 마세요. 대신 별도의 함수로 이동시키세요. 이렇게 하면 테스트 작성이 쉬워지고 단일 책임 원칙(Single Responsibility Principle)을 위반하지 않게 됩니다.

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

순수 함수와 데이터 변경#

작고 순수한 함수를 많이 작성하고, 변경이 발생하는 위치를 최소화하도록 노력하세요.

// bad
const values = {foo: 1};

function impureFunction(items) {
  const bar = 1;

  items.foo = items.a * bar + 2;

  return items.a;
}

const c = impureFunction(values);

// good
var values = {foo: 1};

function pureFunction (foo) {
  var bar = 1;

  foo = foo * bar + 2;

  return foo;
}

var c = pureFunction(values.foo);

상수를 원시값으로 내보내기#

공통 네임스페이스를 가진 상수 원시값을 객체로 내보내는 것보다 개별 내보내기를 권장합니다. 이렇게 하면 컴파일 타임에 더 나은 참조 검사가 가능하고 런타임에서 우발적인 undefined 발생을 방지하는 데 도움이 됩니다. 또한 번들 크기를 줄이는 데도 도움이 됩니다.

상수를 반복 처리(iterate)해야 할 경우(예: prop 검증기)에만 컬렉션(배열 또는 객체)으로 내보내세요.

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

// good
export const VARIANT_WARNING = 'warning';
export const VARIANT_ERROR = 'error';

// good, if the constants need to be iterated over
export const VARIANTS = [VARIANT_WARNING, VARIANT_ERROR];

오류 처리#

서버가 500을 반환하는 내부 서버 오류의 경우, 일반적인 오류 메시지를 반환해야 합니다.

백엔드가 오류를 반환할 때, 오류는 사용자에게 다시 표시하기에 적합해야 합니다.

어떤 이유로 이것이 어려운 경우, 최후의 수단으로 접두사를 사용하여 특정 오류 메시지를 선택할 수 있습니다:

  • 백엔드가 다음과 같이 사용자에게 표시될 오류 메시지에 접두사를 붙이도록 하세요:
Gitlab::Utils::ErrorMessage.to_user_facing('Example user-facing error-message')
  • 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 가이드를 따르세요.