7. 도구 설정
자동 검사할 항목은 biome으로 설정하고, 도구가 검사하지 못하는 항목은 리뷰에서 확인합니다.
7.1 Biome 설정으로 자동 검사할 규칙을 명시합니다
자동 검사와 리뷰의 역할을 구분해 판단이 필요한 내용에 집중합니다.
설정이 담당하는 것
기계적으로 판정할 수 있는 규칙은 아래 Biome 설정으로 검사하고, 의미 판단은 리뷰에서 확인합니다.
| Biome 규칙 | 담당 컨벤션 |
|---|---|
style/noEnum, style/useAsConstAssertion | 규칙 1.7 |
style/noRestrictedImports | 규칙 2.5. 심볼 없는 상대경로 예외는 ./*.css 패턴으로 근사합니다 |
style/useNamingConvention, style/useFilenamingConvention | 규칙 2.3의 심볼, 파일 표기 |
style/noParameterAssign, style/useConst, style/noNestedTernary | 규칙 3.7의 재할당, 중첩 삼항 제한 |
correctness/noUnusedFunctionParameters | 규칙 1.5 |
complexity/useMaxParams | 규칙 3.2의 인자 세 개 기준 |
style/noMagicNumbers | 규칙 4.4 |
suspicious/noExplicitAny, style/noNonNullAssertion | 규칙 1.6 |
plugins의 GritQL 파일 | 규칙 5.1의 ??, || 오른쪽 리터럴 |
규칙 2.4의 가져오기, 이름 붙인 내보내기, 배럴 제한은 아래 규칙이 담당합니다.
style/useImportTypestyle/noDefaultExportperformance/noNamespaceImportperformance/noBarrelFileperformance/noReExportAll
기본 매개변수와 삼항의 대체 리터럴은 GritQL 검사 밖이므로 리뷰합니다.
recommended에 이미 든 규칙도 담당 컨벤션을 드러내려고 설정에 명시합니다.
리뷰가 담당하는 것
| 대상 | 도구 한계 | 처리 |
|---|---|---|
모듈 const, 객체 키의 역할 | 허용된 snake_case는 불변 데이터 상수와 그 키에만 적용됨 | 함수, 스키마, 요청 객체와의 구분은 리뷰합니다 |
허용된 PascalCase의 용도 | 합성 컴포넌트의 {Root, Header, Footer}와 컴포넌트 선언 때문에 허용됨 | 일반 함수, 지역 변수의 camelCase는 리뷰합니다 |
| 폴더 이름 | 단수 kebab-case는 파일 이름 검사 대상이 아님 | 리뷰합니다 |
const 화살표 선언, 이름 붙인 함수의 블록 본문 | style/useConsistentArrowReturn의 style: "always"는 예외까지 막음 | 켜지 않고 규칙 3.1를 리뷰합니다 |
넓은 스코프에서 push로 누적 | useConst는 재할당만 확인함 | 리뷰합니다 |
| 사용하지 않는 매개변수를 아예 생략 | 검사는 남겨 둔 매개변수만 봄 | 리뷰합니다 |
as, @ts-expect-error | 의도를 구분하지 못함 | 위의 타입 좁히기 규칙에 따라 리뷰합니다 |
한 줄 문서 블록 /** … */ | 대응 검사가 없음 | 규칙 6.4를 리뷰합니다 |
config/env.ts 밖의 import.meta.env, process.env | 대응 검사가 없음 | 규칙 2.6에 따라 리뷰합니다 |
설정 예외
| 설정 예외 | 적용 범위와 이유 |
|---|---|
테스트의 noMagicNumbers 해제 | 리터럴 자체가 기대 계약인 값에 적용합니다. 설정값끼리 비교하도록 바꾸지 않으며, 소스가 이미 이름 붙인 값은 테스트도 가져다 씁니다 |
도구 설정 파일의 noDefaultExport 해제 | vite.config.ts처럼 도구가 default를 요구하는 진입점에 적용합니다. 내보내기 규칙의 예외를 설정에 반영합니다 |
style/useFragmentSyntax 비활성 | recommended에 없으며 별도로 켜지 않습니다. 프레임워크 규칙이 <Fragment>를 요구합니다 |
컨벤션 항목을 설정으로 고정합니다.
{
"linter": {
"enabled": true,
"rules": {
"preset": "recommended",
"complexity": {"useMaxParams": {"level": "error", "options": {"max": 3}}},
"correctness": {"noUnusedFunctionParameters": "error"},
"suspicious": {"noExplicitAny": "error"},
"performance": {"noNamespaceImport": "error", "noBarrelFile": "error", "noReExportAll": "error"},
"style": {
"noDefaultExport": "error",
"noEnum": "error",
"noMagicNumbers": "error",
"noNestedTernary": "error",
"useAsConstAssertion": "error",
"noNonNullAssertion": "error",
"noParameterAssign": "error",
"useConst": "error",
"useImportType": "error",
"useFilenamingConvention": {
"level": "error",
"options": {"filenameCases": ["kebab-case"]}
},
"noRestrictedImports": {
"level": "error",
"options": {
"patterns": [
{"group": ["../**", "./**", "!./*.css"], "message": "Import with the absolute alias. Only a bare file import may use ./ for the same folder."}
]
}
},
"useNamingConvention": {
"level": "error",
"options": {
"strictCase": false,
"conventions": [
{"selector": {"kind": "typeLike"}, "formats": ["PascalCase"]},
{"selector": {"kind": "const", "scope": "global"}, "formats": ["camelCase", "PascalCase", "snake_case"]},
{"selector": {"kind": "objectLiteralProperty"}, "formats": ["camelCase", "PascalCase", "snake_case"]},
{"selector": {"kind": "typeProperty"}, "formats": ["camelCase"]},
{"selector": {"kind": "variable"}, "formats": ["camelCase", "PascalCase"]}
]
}
}
}
}
},
"overrides": [
{
"includes": ["**/*.test.ts"],
"linter": {"rules": {"style": {"noMagicNumbers": "off"}}}
},
{
"includes": ["**/*.config.ts", "**/*.config.js"],
"linter": {"rules": {"style": {"noDefaultExport": "off"}}}
}
]
}
??, || 오른쪽 리터럴은 GritQL 플러그인으로 잡습니다.
{
"plugins": ["./no-literal-fallback.grit"]
}
language js
or {
`$left ?? $right`,
`$left || $right`
} where {
$right <: or { string(), number(), `true`, `false`, `[]`, `{}` },
register_diagnostic(span = $right, message = "Do not put a literal on the right of ?? or ||. Reference a declared name.")
}