코드를 작성하는 것보다 어려운 일이 있다면, 그것은 바로 이름을 짓는 것이다.
특히 파일명과 함수명의 네이밍 컨벤션은 프로젝트의 규모가 커질수록 더욱
중요해진다. 오늘은 왜 파일명은 kebab-case를 사용하고 함수명은 camelCase를
사용하는지, 그리고 이런 컨벤션이 실제 개발에 어떤 영향을 미치는지 살펴보겠다.
프로젝트를 살펴보면 다음과 같은 패턴을 볼 수 있다:
📁 use-test-options.ts (kebab-case)
🔧 useTestOptions() (camelCase)
📁 get-options.api.ts (kebab-case)
🔧 getTestOptions() (camelCase)
📁 option-list.tsx (kebab-case)
🔧 OptionList() (PascalCase)
이런 차이가 생긴 이유는 파일 시스템의 현실 때문이다.
Windows와 macOS는 기본적으로 대소문자를 구분하지 않는다. 이는 다음과 같은 상황을
만들어낸다:
# 개발자 A가 생성한 파일
useTestOptions.ts
# 개발자 B가 생성한 파일 (다른 파일이라고 생각함)
UseTestOptions.ts
# 하지만 Windows/macOS에서는 같은 파일로 인식됨!
Linux에서는 두 파일이 다르게 인식되지만, Windows나 macOS에서는 같은 파일로
취급된다. 이런 불일치는 배포 환경에서 예상치 못한 오류를 발생시킨다.
Git도 이런 문제에서 자유롭지 않다:
# 파일명을 변경했지만 Git이 인식하지 못하는 경우
git mv useTestOptions.ts UseTestOptions.ts
# 때로는 제대로 추적되지 않는다
# kebab-case는 이런 문제가 없다
git mv use-test-options.ts user-test-options.ts
# 항상 명확하게 추적된다
웹 개발에서 파일 경로는 종종 URL과 직결된다:
# 읽기 쉬운 URL
/features/option-list/use-test-options
# 읽기 어려운 URL
/features/optionList/useTestOptions
kebab-case는 URL에서 단어 구분이 명확해 가독성이 좋다.
터미널에서 파일을 다룰 때도 차이가 난다:
# 간단명료
vim use-test-options.ts
ls option-*
# 대소문자 신경쓰며 입력해야 함
vim useTestOptions.ts
ls option* # 찾기 어려움
그렇다면 함수명은 왜 camelCase를 사용할까? 이는 언어의 관습 때문이다.
JavaScript는 태생부터 camelCase를 사용해왔다:
// JavaScript 내장 메서드들
Array.prototype.forEach;
String.prototype.toLowerCase;
Document.getElementById;
// React Hook 패턴
useState, useEffect, useCallback;
함수명에서 camelCase는 가독성을 높인다:
// 자연스러운 읽기
const userData = useUserData();
const isLoading = getLoadingState();
// 어색한 읽기
const user_data = use_user_data();
const is_loading = get_loading_state();
Feature-Sliced Design에서는 이런 컨벤션이 더욱 중요하다:
features/
├── user-management/ # kebab-case
│ ├── api/
│ │ ├── get-users.api.ts # kebab-case
│ │ └── create-user.api.ts
│ ├── model/
│ │ ├── use-user-list.ts # kebab-case
│ │ └── use-user-form.ts
│ └── ui/
│ ├── user-list.tsx # kebab-case
│ └── user-form.tsx
// 파일 내부에서는 camelCase/PascalCase
export const getUserList = () => { ... } // camelCase
export const useUserList = () => { ... } // camelCase
export const UserList = () => { ... } // PascalCase
export const UserForm = () => { ... } // PascalCase
use_test_options.ts
get_options_api.ts
Python 개발자들에게는 친숙하지만, JavaScript 생태계에서는 어색하다.
UseTestOptions.ts
GetOptionsApi.ts
파일명으로 사용하기에는 앞서 언급한 대소문자 문제가 있다.
useTestOptions.ts
getOptionsApi.ts
일부 팀에서 사용하지만, 파일 시스템 호환성 문제가 있다.
일관된 네이밍 컨벤션을 위해서는 수동 검토보다는 자동화된 도구를 사용하는
것이 효과적이다. ESLint의 eslint-plugin-check-file 플러그인을 사용하면
파일명과 폴더명 규칙을 자동으로 검증할 수 있다.
pnpm add -D eslint-plugin-check-file
// eslint.config.js
import checkFile from 'eslint-plugin-check-file';
export default tseslint.config(
// ... 기타 설정
{
plugins: {
'check-file': checkFile,
},
rules: {
// 파일 네이밍 컨벤션 규칙
'check-file/filename-naming-convention': [
'error',
{
'**/*.{ts,tsx}': 'KEBAB_CASE',
},
{
// 예외 파일들
ignoreMiddleExtensions: true,
ignore: [
'src/App.tsx', // 메인 App 컴포넌트
'src/main.tsx', // 엔트리 포인트
'vite.config.ts',
'vitest.config.ts',
],
},
],
'check-file/folder-naming-convention': [
'error',
{
'src/**/': 'KEBAB_CASE',
},
],
},
}
);
실시간으로 네이밍 규칙을 확인하기 위해 VS Code 설정을 추가한다:
// .vscode/settings.json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
]
}
이제 pnpm lint 명령어로 네이밍 컨벤션을 검증할 수 있다:
pnpm lint
# 결과 예시
src/App.tsx
1:1 error Filename 'App.tsx' does not match the naming convention check-file/filename-naming-convention
src/pages/HomePage.tsx
1:1 error Filename 'HomePage.tsx' does not match the naming convention check-file/filename-naming-convention
src/widgets/OptionList.tsx
1:1 error Filename 'OptionList.tsx' does not match the naming convention check-file/filename-naming-convention
VS Code에서는 파일 생성 시점부터 빨간 밑줄로 규칙 위반을 표시해주어 즉시 수정할
수 있다.
기존 프로젝트에서는 한 번에 모든 파일을 바꾸기 어려우므로 점진적 접근을
권장한다:
// 단계별 적용
'check-file/filename-naming-convention': [
'warn', // 먼저 경고로 시작
{
'**/*.{ts,tsx}': 'KEBAB_CASE',
},
{
ignore: [
// 기존 파일들을 임시로 예외 처리
'src/components/UserList.tsx',
'src/hooks/useUserData.ts',
// ... 점진적으로 줄여나감
],
},
],
# CLI 도구로 일관된 파일 생성
npx create-feature user-management
# 자동으로 kebab-case 파일들이 생성됨
기존 프로젝트에서 네이밍 컨벤션을 바꾸는 것은 쉽지 않다. 특히 import 경로가 모두
바뀌어야 하기 때문이다:
// Before
import { useTestOptions } from './useTestOptions';
// After
import { useTestOptions } from './use-test-options';
하지만 ESLint 자동화를 도입하면서 다음과 같은 이점을 경험했다:
# 파일 생성 즉시 규칙 위반 감지
touch src/components/UserProfile.tsx
# VS Code에서 즉시 빨간 밑줄과 함께 경고 표시
# GitHub Actions 예시
- name: Lint check
run: pnpm lint
# 네이밍 규칙 위반 시 빌드 실패
네이밍 컨벤션 관련 리뷰 코멘트가 90% 감소했다. 개발자들은 더 중요한 로직과
아키텍처에 집중할 수 있게 되었다.
명확한 네이밍 컨벤션과 ESLint 자동화가 있으면 새로운 팀원이 프로젝트에 적응하기
쉽다:
# 새 팀원이 실수로 잘못된 네이밍을 사용해도
touch src/components/UserList.tsx
# 즉시 자동으로 알려준다
src/components/UserList.tsx
1:1 error Filename 'UserList.tsx' does not match the naming convention
# 올바른 방법을 학습하게 된다
mv src/components/UserList.tsx src/components/user-list.tsx
파일을 찾는 시간이 줄어들고, 어디에 무엇을 만들어야 할지 직관적으로 알 수 있다.
특히 ESLint가 실시간으로 가이드를 제공하므로 학습 곡선이 크게 단축된다.
kebab-case 사용.api.ts, .types.ts, .utils.ts)camelCase 사용 (일반 함수)PascalCase 사용 (React 컴포넌트)kebab-case 사용user not users)user-management, option-list)파일 네이밍 컨벤션은 사소해 보이지만 개발 생산성에 큰 영향을 미친다.
kebab-case 파일명과 camelCase 함수명의 조합은 다음과 같은 이유로 최적의
선택이다:
그리고 이런 컨벤션을 ESLint로 자동화하면:
좋은 컨벤션은 개발자의 인지 부하를 줄이고, 팀의 생산성을 높인다. 파일을 찾기
위해 헤매는 시간, 대소문자 때문에 발생하는 버그, 일관성 없는 명명으로 인한 혼란.
이 모든 것들을 방지하는 것이 바로 올바른 네이밍 컨벤션과 자동화 도구의 힘이다.
"There are only two hard things in Computer Science: cache invalidation and
naming things." - Phil Karlton
이 유명한 격언처럼, 좋은 이름을 짓는 것은 어렵다. 하지만 일관된 규칙을 정하고 팀
전체가 따르면, 그 어려움을 크게 줄일 수 있다. 오늘부터라도 프로젝트의 네이밍
컨벤션을 점검해보는 것은 어떨까.