서론
React 애플리케이션에서 코드 품질, 일관성 및 확장성을 유지하기 위해 프로젝트 표준을 강제하는 것은 매우 중요하다. 최고의 표준을 설정하고 준수함으로써 개발자는 코드베이스를 깨끗하고, 조직적이며 유지보수하기 쉽게 유지할 수 있다.
ESLint
ESLint는 코드 품질을 유지하고 코딩 표준을 준수하는 데 매우 유용한 도구이다. .eslintrc.js
파일에서 규칙을 설정함으로써, ESLint는 일반적인 오류를 식별하고 방지하는 데 도움을 준다. 이는 초기 단계에서 실수를 발견하는 데 도움을 주며, 전체 코드베이스에 걸쳐 일관성을 증진시키고 코드의 품질과 가독성을 높인다.
ESLint 구성 예시 코드
module.exports = {
root: true,
env: {
node: true,
es6: true,
},
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
ignorePatterns: [
'node_modules/*',
'public/mockServiceWorker.js',
'generators/*',
],
extends: ['eslint:recommended'],
plugins: ['check-file'],
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
settings: {
react: { version: 'detect' },
'import/resolver': {
typescript: {},
},
},
env: {
browser: true,
node: true,
es6: true,
},
extends: [
'eslint:recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:prettier/recommended',
'plugin:testing-library/react',
'plugin:jest-dom/recommended',
'plugin:tailwindcss/recommended',
'plugin:vitest/legacy-recommended',
],
rules: {
'import/no-restricted-paths': [
'error',
{
zones: [
// disables cross-feature imports:
// eg. src/features/discussions should not import from src/features/comments, etc.
{
target: './src/features/auth',
from: './src/features',
except: ['./auth'],
},
{
target: './src/features/comments',
from: './src/features',
except: ['./comments'],
},
{
target: './src/features/discussions',
from: './src/features',
except: ['./discussions'],
},
{
target: './src/features/teams',
from: './src/features',
except: ['./teams'],
},
{
target: './src/features/users',
from: './src/features',
except: ['./users'],
},
// enforce unidirectional codebase:
// e.g. src/app can import from src/features but not the other way around
{
target: './src/features',
from: './src/app',
},
// e.g src/features and src/app can import from these shared modules but not the other way around
{
target: [
'./src/components',
'./src/hooks',
'./src/lib',
'./src/types',
'./src/utils',
],
from: ['./src/features', './src/app'],
},
],
},
],
'import/no-cycle': 'error',
'linebreak-style': ['error', 'unix'],
'react/prop-types': 'off',
'import/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'object',
],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
'import/default': 'off',
'import/no-named-as-default-member': 'off',
'import/no-named-as-default': 'off',
'react/react-in-jsx-scope': 'off',
'jsx-a11y/anchor-is-valid': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
'@typescript-eslint/explicit-function-return-type': ['off'],
'@typescript-eslint/explicit-module-boundary-types': ['off'],
'@typescript-eslint/no-empty-function': ['off'],
'@typescript-eslint/no-explicit-any': ['off'],
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
'check-file/filename-naming-convention': [
'error',
{
'**/*.{ts,tsx}': 'KEBAB_CASE',
},
{
ignoreMiddleExtensions: true,
},
],
},
},
{
plugins: ['check-file'],
files: ['src/**/!(__tests__)/*'],
rules: {
'check-file/folder-naming-convention': [
'error',
{
'**/*': 'KEBAB_CASE',
},
],
},
},
],
};
Prettier
Prettier는 프로젝트에서 코드의 일관된 포맷을 유지하는 데 유용한 도구다. IDE에서 "저장 시 자동 포맷" 기능을 활성화하면, 미리 설정된 .prettierrc
파일에 따라 코드가 자동으로 정리된다. 이 기능은 전체 코드베이스에 걸쳐 일관된 스타일을 보장하고 코드 문제에 대한 유용한 피드백을 제공한다. 만약 자동 포맷팅이 실패한다면, 이는 잠재적인 구문 오류가 있을 수 있음을 알려주는 신호가 될 수 있다. 더욱이, Prettier는 ESLint와 통합되어 코딩 규칙을 효과적으로 적용하며 동시에 코드 포맷팅 작업을 처리한다.
Prettier 구성 예시 코드
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false
}
TypeScript
ESLint는 JavaScript에서 언어 관련 버그를 감지하는 데 효과적이다. 그러나 JavaScript의 동적 특성 때문에, 특히 복잡한 프로젝트에서는 ESLint가 모든 런타임 데이터 문제를 잡아내지 못할 수 있다. 이를 해결하기 위해 TypeScript를 권장한다. TypeScript는 대규모 리팩토링 과정에서 눈에 띄지 않을 수 있는 문제들을 식별하는 데 유용하다. 리팩토링할 때는 먼저 타입 선언을 업데이트하고, 그 후 프로젝트 전반에 걸쳐 TypeScript 오류를 해결하는 것이 중요하다. TypeScript는 빌드 시 타입 검사를 수행하여 개발자의 자신감을 높이지만, 런타임 오류를 방지하지는 않는다.
Husky
Husky는 워크플로에서 git 훅을 구현하고 실행하는 데 유용한 도구다. 각 커밋 전에 코드 검증을 실행하면, 코드가 높은 표준을 유지하고 결함 있는 커밋이 저장소에 푸시되지 않도록 할 수 있다. 또한 린팅, 코드 포맷팅, 타입 검사 등 다양한 작업을 코드 푸시 전에 수행할 수 있다.
절대 경로
절대 경로 임포트는 항상 구성하고 사용해야 한다. 이렇게 하면 파일을 이동하기 쉬워지고 ../../../component
와 같은 복잡한 임포트 경로를 피할 수 있다. 파일을 어디로 이동하든 모든 임포트가 그대로 유지된다.
설정 방법은 다음과 같다.
JavaScript (jsconfig.json
) 프로젝트의 경우
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
TypeScript (tsconfig.json
) 프로젝트의 경우:
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
여러 폴더(@components
, @hooks
등)에 대해 여러 경로를 정의할 수도 있지만, @/*
를 사용하는 것이 매우 효과적이다. 경로가 짧아서 여러 경로를 구성할 필요가 없고, 다른 종속성 모듈과 구별되므로 node_modules
에서 오는 것과 소스 폴더에서 오는 것을 혼동하지 않게 한다. 즉, src 폴더에 있는 모든 파일은 @
를 통해 접근할 수 있다. 예를 들어 src/components/my-component
에 있는 파일은 ../../../components/my-component
대신 @/components/my-component
를 사용해 접근할 수 있다.
파일 명명 규칙
프로젝트에서 파일 명명 규칙과 폴더 명명 규칙도 강제할 수 있다. 예를 들어, 모든 파일을 케밥 케이스(kebab-case)로 명명하도록 강제할 수 있다. 이렇게 하면 코드베이스를 일관되게 유지하고 탐색하기 쉽게 만든다.
Eslint 설정
'check-file/filename-naming-convention': [
'error',
{
'**/*.{ts,tsx}': 'KEBAB_CASE',
},
{
// ignore the middle extensions of the filename to support filename like bable.config.js or smoke.spec.ts
ignoreMiddleExtensions: true,
},
],
'check-file/folder-naming-convention': [
'error',
{
// all folders within src (except __tests__)should be named in kebab-case
'src/**/!(__tests__)': 'KEBAB_CASE',
},
],