도구
GitLab v19.4요약
GitLab은 프론트엔드 코드 표준을 정의하고 강제하기 위해 ESLint를 사용합니다. 작업 환경과 IDE에서 환경 변수 REVEAL_ESLINT_TODO를 1로 설정하면 .eslint_todo/*.mjs로 제외된 미처리 eslint todo를 볼 수 있습니다.
ESLint#
GitLab은 프론트엔드 코드 표준을 정의하고 강제하기 위해 ESLint를 사용합니다. 설정은 gitlab-eslint-config 프로젝트에 있습니다.
작업 환경과 IDE에서 환경 변수 REVEAL_ESLINT_TODO를 1로 설정하면 .eslint_todo/*.mjs로 제외된 미처리 eslint todo를 볼 수 있습니다. 이를 통해 기존 eslint 예외를 드러내어 평소 작업 중에 함께 수정할 수 있습니다.
Yarn 스크립트#
이 절에서는 ESLint로 파일을 검사하고 자동 수정을 적용하는 yarn 스크립트를 설명합니다.
스테이징된 모든 파일(git diff 기준)을 ESLint로 검사하려면 다음 스크립트를 실행합니다.
yarn run lint:eslint:staged
발견된 문제 목록이 콘솔에 출력됩니다.
스테이징된 모든 파일(git diff 기준)에 ESLint 자동 수정을 적용하려면 다음 스크립트를 실행합니다.
yarn run lint:eslint:staged:fix
직접 수정해야 하는 항목이 있으면 변경 목록이 콘솔로 전달됩니다.
리포지터리의 특정 파일을 ESLint로 검사하려면 다음 스크립트를 실행합니다($PATH_TO_FILE을 바꿉니다).
yarn run lint:eslint $PATH_TO_FILE
리포지터리의 모든 파일을 ESLint로 검사하려면 다음 스크립트를 실행합니다.
yarn run lint:eslint:all
발견된 문제 목록이 콘솔에 출력됩니다.
리포지터리의 모든 파일에 ESLint 자동 수정을 적용하려면 다음 스크립트를 실행합니다.
yarn run lint:eslint:all:fix
직접 수정해야 하는 항목이 있으면 변경 목록이 콘솔로 전달됩니다.
전역 규칙 갱신에만 사용합니다. 그렇지 않으면 변경 규모가 매우 큰 머지 리퀘스트가 만들어질 수 있습니다.
새 파일에서 ESLint 비활성화#
새 파일을 만들 때는 ESLint를 비활성화하지 않습니다. 기존 파일은 레거시 호환 때문에 일부 규칙이 비활성화되어 있을 수 있으나, 이들은 리팩터링이 진행 중입니다.
특정 ESLint 규칙을 비활성화하지 않습니다. 기술 부채를 남기지 않기 위해, 기존 코드 모듈을 호출하거나 인스턴스화하는 경우에 한해 다음 규칙만 비활성화할 수 있습니다.
이 규칙들은 줄 단위로 비활성화합니다. 그래야 나중에 리팩터링하기가 쉬워집니다.
예를 들어 eslint-disable-next-line 이나 eslint-disable-line을 사용합니다.
단일 위반에 대해 ESLint 비활성화#
단일 위반 때문에 규칙을 비활성화해야 한다면, 필요한 최소 범위의 코드에만 적용합니다.
// bad
/* eslint-disable no-new */
import Foo from 'foo';
new Foo();
// better
import Foo from 'foo';
// eslint-disable-next-line no-new
new Foo();
todo 파일 생성#
새 ESLint 규칙을 켰을 때 코드베이스 전반에서 위반이 다수 발견된다면, todo 파일을 생성해 해당 위반을 일시적으로 무시하는 편이 수월할 수 있습니다. 이 방식에는 장단점이 있습니다.
장점:
- 특정 규칙을 위반하는 모든 파일에 대한 단일 진실 공급원(Single Source Of Truth, SSOT)이 생깁니다. 발생한 기술 부채를 갚는 데 필요한 작업을 추적하기 쉬워집니다.
- 위반 파일을 전부 수정할 필요가 없으므로, 규칙을 처음 켤 때의 변경 범위가 작아집니다.
단점:
- 파일 전체에 대해 규칙을 비활성화하면, 해당 파일에 같은 유형의 위반이 더 들어올 수 있습니다.
- 여러 머지 리퀘스트에서 동시에 위반을 수정하면 todo 파일에서 충돌이 자주 발생하고, MR 작성자가 브랜치를 리베이스해야 합니다.
todo 파일을 생성하려면 scripts/frontend/generate_eslint_todo_list.mjs 스크립트를 실행합니다.
node scripts/frontend/generate_eslint_todo_list.mjs <rule_name>
예를 들어 vue/no-unused-properties 규칙에 대한 todo 파일을 생성하는 명령은 다음과 같습니다.
node scripts/frontend/generate_eslint_todo_list.mjs vue/no-unused-properties
이 명령은 .eslint_todo/vue-no-unused-properties.mjs에 ESLint 설정을 만들고, 이 설정은
전역 설정에 자동으로 추가됩니다.
특정 규칙에 대한 todo 파일을 만들었다면, 해당 위반을 해소하는 데 필요한 작업을 반드시 계획합니다. todo 파일은 가능한 한 짧게 유지합니다. 해소할 수 없는 위반이 있다면 단일 위반에 대해 ESLint 비활성화로 전환해 인라인 무시를 사용합니다.
위반 파일을 모두 수정했다면 todo 파일과 함께 .eslint_todo/index.mjs의 export
구문도 제거합니다.
no-undef 규칙과 전역 변수 선언#
no-undef 규칙은 절대 비활성화하지 않습니다. 대신 /* global Foo */로 전역 변수를 선언합니다.
전역 변수를 여러 개 선언할 때는 변수마다 /* global [name] */ 줄을 하나씩 사용합니다.
// bad
/* globals Flash, Cookies, jQuery */
// good
/* global Flash */
/* global Cookies */
/* global jQuery */
import/no-deprecated로 함수 사용 중단 표시#
GitLab의 @gitlab/eslint-plugin Node 모듈에는 eslint-plugin-import 패키지가 들어 있습니다.
import/no-deprecated 규칙을 사용하면 @deprecated 태그가 붙은 JSDoc 블록으로 함수의 사용 중단을 표시할 수 있습니다.
/**
* Convert search query into an object
*
* @param {String} query from "document.location.search"
* @param {Object} options
* @param {Boolean} options.gatherArrays - gather array values into an Array
* @returns {Object}
*
*For example: "?one=1&two=2" into {one: 1, two: 2}
* @deprecated Please use `queryToObject` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/283982 for more information
*/
export function queryToObject(query, options = {}) {
...
}
다음 두 가지를 적극 권장합니다.
- 이 함수를 사용하려는 개발자를 위해 대체 경로를 제시합니다.
- 마이그레이션 진행 상황을 추적하는 이슈 링크를 제공합니다.
사용 중단된 함수를 다른 파일에서 import 하면 사용이 감지됩니다. 같은 파일 안에서 함수를 사용하는 경우에는 감지되지 않습니다.
이후 $ yarn eslint를 실행하면 사용 중단된 사용처 목록을 확인할 수 있습니다.
$ yarn eslint
./app/assets/javascripts/issuable_form.js
9:10 error Deprecated: Please use `queryToObject` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/283982 for more information import/no-deprecated
33:23 error Deprecated: Please use `queryToObject` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/283982 for more information import/no-deprecated
...
이 규칙을 비활성화한 사례를 grep으로 모으면 이슈를 만들 작업 목록을 얻을 수 있고, 사용 중단된 사용처를 제거하는 작업을 추적할 수 있습니다.
$ grep "eslint-disable.*import/no-deprecated" -r .
./app/assets/javascripts/issuable_form.js:import { queryToObject, objectToQuery } from './lib/utils/url_utility'; // eslint-disable-line import/no-deprecate
./app/assets/javascripts/issuable_form.js: // eslint-disable-next-line import/no-deprecated
내 파일에서 vue/multi-word-component-names가 비활성화된 경우#
Vue 스타일 가이드는 단어 하나짜리 컴포넌트 이름을 권장하지 않습니다.
이런 이름은 다른 HTML 컴포넌트와 혼동될 수 있어 문제가 됩니다. 예를 들어 컴포넌트 이름을
<table>로 지으면 HTML <table> 이 렌더링되지 않습니다.
이 문제를 해결하려면 .vue 파일과 그 참조의 이름을 최소 두 단어로 바꿉니다.
예를 들면 다음과 같습니다.
user/table.vue는user/users_table.vue로 이름을 바꿔UsersTable로 import 하고<users-table />로 사용할 수 있습니다.
GraphQL 스키마 및 오퍼레이션 검증#
GitLab은 GraphQL 스키마와 오퍼레이션을 린트하기 위해 @graphql-eslint/eslint-plugin
을 사용합니다. 이 플러그인이 정상 동작하려면 전체 스키마가 필요합니다.
따라서 로컬에서 ESLint를 실행할 때는 최신 스키마 덤프를 생성하는 것을 권장합니다.
./scripts/dump_graphql_schema 스크립트를 실행하면 됩니다.
Prettier로 포맷팅#
GitLab의 코드는 스타일 가이드를 따르도록 Prettier로 자동 포맷팅됩니다. Prettier는 표준 prettier 규칙에 따라 .js, .vue, .graphql, .scss 파일의 포맷팅을 담당합니다. Prettier 설정은 모두 .prettierrc에 있습니다.
에디터#
워크플로에 Prettier를 포함하는 권장 방법은 사용하는 에디터(주요 에디터는 모두 지원)를 그에 맞게 설정하는 것입니다. 파일을 저장할 때마다 Prettier가 실행되도록 설정하는 방식을 권장합니다. 선호하는 에디터에서 Prettier를 사용하는 방법은 Prettier 문서를 참고합니다.
전역 Yarn 스크립트와 동일한 파일 형식(.js, .vue, .graphql, .scss)만 Prettier가 포맷팅하도록 주의합니다. 예를 들어 Visual Studio Code 설정 파일에서 특정 파일 형식을 제외할 수 있습니다.
"prettier.disableLanguages": [
"json",
"markdown"
]
Yarn 스크립트#
전역 포맷팅에는 다음 yarn 스크립트를 사용할 수 있습니다.
yarn run lint:prettier:staged:fix
스테이징된 모든 파일(git diff 기준)을 Prettier로 갱신하고 필요한 변경을 저장합니다.
yarn run lint:prettier:staged
스테이징된 모든 파일(git diff 기준)을 Prettier로 검사하고, 직접 수정이 필요한 파일을 콘솔에 출력합니다.
yarn run lint:prettier
모든 파일을 Prettier로 검사하고, 직접 수정이 필요한 파일을 콘솔에 출력합니다.
yarn run lint:prettier:fix
리포지터리의 모든 파일을 Prettier로 포맷팅합니다.
VS Code 설정#
Prettier를 기본 포매터로 선택#
Prettier를 포매터로 선택하려면 User 또는 Workspace Settings에 다음 속성을 추가합니다.
{
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[graphql]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
저장 시 포맷팅#
Prettier로 파일을 자동 포맷팅하려면 User 또는 Workspace Settings에 다음 속성을 추가합니다.
{
"[html]": {
"editor.formatOnSave": true
},
"[javascript]": {
"editor.formatOnSave": true
},
"[vue]": {
"editor.formatOnSave": true
},
"[graphql]": {
"editor.formatOnSave": true
},
}
사용되지 않는 Vue provide 추적#
Vue 의존성 주입은 시간이 지나면서 낡습니다.
inject를 사용하는 컴포넌트를 리팩터링하거나 제거해도, 짝이 되는 provide 항목은
그에 값을 공급하는 el.dataset 읽기, HAML data: 속성, Ruby 헬퍼와 함께
그대로 남는 경우가 많습니다.
scripts/frontend/trace_provide_inject_usage.mjs 스크립트는 어떤 하위 컴포넌트도 주입하지 않는
provide 키를 찾아 주므로, 죽은 연결을 제거할 수 있습니다.
이런 정리 작업의 예시는 머지 리퀘스트
!242664와
!242662를 참고합니다.
provider 파일 하나 또는 여러 파일을 추적하려면 파일 경로나 glob을 하나 이상 지정해 스크립트를 실행합니다.
이 스크립트는 PATH에 ripgrep(rg)이 있어야 하며,
.vue와 .js 파일만 분석합니다.
node scripts/frontend/trace_provide_inject_usage.mjs <file|glob> [<file|glob>...]
예를 들어 진입점 하나를 추적한 다음 디렉터리 전체를 추적하는 명령은 다음과 같습니다(셸이 확장하지 않도록 glob은 따옴표로 감쌉니다).
node scripts/frontend/trace_provide_inject_usage.mjs app/assets/javascripts/ci/pipeline_details/pipeline_header.js
node scripts/frontend/trace_provide_inject_usage.mjs 'app/assets/javascripts/ci/**/*.js'
이 스크립트는 추적한 파일마다 분석 결과를 출력하고, 마지막에 제거 후보 목록을 제시합니다. 각 provide 키에는 다음 판정 중 하나가 부여됩니다.
| 판정 | 조치 | 설명 |
|---|---|---|
REMOVABLE |
제거합니다 | 어디에서도 해당 키를 주입하는 컴포넌트가 없으므로 provide가 죽어 있습니다. |
LIKELY-REMOVABLE |
제거한 뒤 확인합니다 | 주입자는 있으나, 모듈 import 그래프에서 이 provider 로부터 도달할 수 있는 주입자가 없습니다. 도달 가능성 판정은 일부 동적 import를 놓치는 휴리스틱이므로, 컴포넌트 스펙이나 페이지 로드로 확인합니다. |
IN USE |
유지합니다 | 이 provider 로부터 도달할 수 있는 주입자가 있습니다. 공유 모듈이 서로 무관한 컴포넌트를 연결할 수 있으므로, 이 결과는 사용 가능성이지 사용의 증거는 아닙니다. |
INCONCLUSIVE |
직접 조사합니다 | 신뢰할 수 있는 판정을 가로막는 요소가 있습니다. 동적 경계(Vue.component() 전역 등록 또는 해석되지 않은 동적 import), 벤더링된 npm 패키지 안에만 존재하는 주입자(node_modules를 넘어서는 도달 가능성은 판정할 수 없습니다), render()가 기본 슬롯만 전달하는 provider(실제 하위 컴포넌트는 이 파일의 import가 아니라 호출 지점에서 옵니다), provider와 같은 디렉터리에 있으면서 도달할 수 없는 주입자(도달 가능성 휴리스틱이 실제 관계를 놓쳤다는 강한 신호) 등이 여기에 해당합니다. |
스크립트가 REMOVABLE 또는 LIKELY-REMOVABLE로 보고한 키를 제거할 때는 그 키에 값을 공급하는
계층을 차례로 따라가되, 다른 소비처가 아직 사용하는 계층에서는 멈춥니다.
provide:항목을 제거합니다.el.dataset읽기와 그로부터 파생된 상수·import를 제거하되, 진입점의 다른 곳에서 사용되지 않는 경우에만 제거합니다. 해당 값이 스토어, 라우터, props, 형제 애플리케이션에도 공급된다면 그대로 둡니다.- 같은 마운트 요소를 읽는 다른 진입점이 없는지 확인한 뒤, HAML
data:속성을 제거합니다. app/과ee/app/양쪽에서 다른 호출자가 없는지 확인한 뒤, 속성 값을 만드는 Ruby 헬퍼를 제거합니다.- 제거한 키를 검증하는 RSpec 및 Jest 스펙을 갱신합니다.
하위 컴포넌트가 여전히 주입하는 provide를 제거하면 런타임에 injection not found 오류가
발생하므로, 영향을 받는 페이지를 로드하거나 해당 기능 스펙을 실행해 제거가 안전한지 확인합니다.
변경 범위는 provider 파일 하나로 한정합니다.