새로운 사이드 프로젝트를 시작하면서 프로젝트 세팅을 하게 되었다.
이전의 프로젝트들도 계속 내가 세팅을 해오긴 했으나, 따로 정리한 문서가 없어서 나중에 다시 찾아도 볼겸.. 정리를 해보려고 한다.
이번 프로젝트는 Next.js
의 App Router
방식을 사용하고 있으며, 커밋 컨벤션을 강제하기 위해 Husky
와 Commitizen
을 함께 도입했다.
프로젝트 세팅에 익숙해지고자 매번 처음부터 새로 설정했었는데, 이번 이후로는 모노레포를 구축하여 동일한 설정을 가진 프로젝트를 쉽게 세팅할 수 있는 방식으로 전환하고자 한다.
우선 여기에 나오는 세팅에서 목표하는 바는 크게 다음과 같다.
- ESLint와 Prettier 설정으로 일관된 코드 스타일 유지
- Husky와 lint-staged 설정으로 커밋 전 자동으로 코드 품질 검사를 수행하여 일관된 코드 품질을 유지
- Commitizen 설정으로 편리하게 커밋 컨벤션 지키기
- Commitlint: 일관된 커밋 메시지 형식을 강제하여 프로젝트 히스토리의 가독성 높이기 (git commit으로 직접 commit 하려는 경우에 대한 제한)
커밋 시도 시 Husky가 pre-commit 훅을 실행하고, pre-commit 훅에서 lint-staged가 실행된다.
lint-staged는 변경된 파일에 대해 Prettier와 ESLint를 실행하게되고,
커밋 메시지 작성 시 Husky가 commit-msg 훅을 실행하여 Commitlint로 메시지 형식을 검사하는 흐름이다.
그리고 커밋 메시지 작성 시, Commitizen
을 활용하여 보다 쉽게 컨벤션을 지키도록 했으며,
혹시라도 직접 git commit
을 하는 경우에는 메시지에 강제 규칙을 넣어 컨벤션을 지키게 했다.
yarn create next-app {프로젝트 이름} --typescript
타입스크립트를 세팅하고, 나머지는 다음과 같이 설정했다.
린팅으로는 기본적인 패키지만 포함시켰다.
yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser --dev
yarn add prettier-plugin-tailwindcss --dev
yarn add eslint-config-prettier --dev
yarn add eslint-plugin-prettier --dev
yarn add prettier --dev
.eslintrc.json
파일 생성:
{
"extends": [
"next/core-web-vitals",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"prettier"
],
"plugins": ["@typescript-eslint", "prettier"],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"prettier/prettier": "error",
"react/react-in-jsx-scope": "off"
}
}
.prettierrc
파일 생성:
*prettier 설정은 취향대로~
{
"endOfLine": "lf",
"printWidth": 100,
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"bracketSpacing": true,
"bracketSameLine": true,
"arrowParens": "always",
"useTabs": false,
"plugins": ["prettier-plugin-tailwindcss"]
}
package.json
에 스크립트 추가:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint './src/**/*.{ts,tsx,js,jsx}'",
"lint:fix": "eslint --fix './src/**/*.{ts,tsx,js,jsx}'",
"format": "prettier --check --ignore-path .gitignore .",
"format:fix": "prettier --write --ignore-path .gitignore ."
}
나같은 경우, yarn lint
를 하면 린팅이 걸리는데, VSCode에서 실시간으로는 감지가 안되는 오류
가 있었다.
찾아보니 VSCode의 settings.json
파일에서 ESLint 설정이 .eslintrc
파일을 참조하도록 되어있는 것을 확인했다. 이렇게 사용자마다 로컬 환경에서 특정 설정이 되어있는 경우를 커버하기 위해 다음과 같은 설정을 추가할 수 있다.
옵션: eslint 설정 파일 지정하기
프로젝트의 일관성을 유지하기 위해 VSCode의 ESLint 설정을 프로젝트 레벨에서 지정할 수 있는데, 이는 팀원들 간의 개발 환경 차이로 인한 문제를 예방하는 데 매우 유용하다.
다음 설정은 VSCode에게 프로젝트 루트의 .eslintrc.json
파일을 ESLint 구성 파일로 사용하도록 지정한다. 이를 통해 로컬 개발 환경 설정과 관계없이 모든 팀원이 동일한 ESLint 규칙을 적용받게 할 수 있다.
.vscode/settings.json
파일 생성:
{
"eslint.options": {
"overrideConfigFile": ".eslintrc.json"
}
}
yarn add -D husky
yarn add -D lint-staged
npx husky init
.husky/pre-commit
파일 생성:
#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
package.json
에 추가:
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint --fix"
]
}
yarn add @commitlint/config-conventional --dev
yarn add @commitlint/cli --dev
.husky/commit-msg
파일 생성:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no -- commitlint --edit ${1}
.commitlintrc.json
파일 생성:
아래 설정을 통해 commit message의 규칙을 강제할 수 있다.
난 @commitlint/config-conventional
플러그인을 기본 룰로 사용했다.
자세한 사용법 참고) https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [
2,
"always",
["feat", "style", "chore", "fix", "ci", "refactor", "merge", "docs"]
],
"type-case": [2, "always", "lower-case"],
"type-empty": [2, "never"],
"subject-empty": [2, "never"],
"subject-case": [2, "never", []]
}
}
package.json
에 스크립트 추가:
"scripts": {
"prepare": "husky",
"postinstall": "husky install"
}
yarn add -D commitizen
package.json
에 다음과 같이 추가 및 수정(commit-config.js 린팅 무시, commit 명령어 추가):
{
"config": {
"commitizen": {
"path": "./commit-config.js"
}
},
"scripts": {
"lint": "eslint './src/**/*.{ts,tsx,js,jsx}' --ignore-pattern 'commit-config.js'",
"lint:fix": "eslint --fix './src/**/*.{ts,tsx,js,jsx}' --ignore-pattern 'commit-config.js'",
"commit": "./commitizen.sh"
}
}
.eslintrc.json
에 린팅 무시 파일 추가:
{
"ignorePatterns": ["commit-config.js"]
}
1,2번에서 commit-config.js 파일에 대한 린팅을 무시하는 설정을 추가했다.
사실 ts로 수정하려다가 any 타입을 쓰거나 타입패키지를 추가하는 것 보다 그냥 개발 환경에 관련된 파일이니 린팅에서 무시하는게 나을거라고 판단했다. 이 부분은 나의 생각이 틀릴 수도 있으니, 취향에 맞게 설정하면 될 것 같다.
.commitlintrc.json
파일 수정 (커밋 라벨 동일하게 맞춰주는 작업):
앞에 붙일 라벨들은 자유로 설정 가능!!
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [
2,
"always",
[
"✨ feat",
"🐛 fix",
"♻️ refactor",
"🎨 design",
"💎 style",
"📦 chore",
"💬 comment",
"📚 docs",
"🚑 !HOTFIX",
"🚀 perf"
]
],
"type-case": [2, "always", "lower-case"],
"type-empty": [2, "never"],
"subject-empty": [2, "never"],
"subject-case": [2, "never", []],
"header-max-length": [2, "always", 100]
},
"parserPreset": {
"parserOpts": {
"headerPattern": "^(\\S+):\\s(.+)\\s\\(#\\d+\\)$",
"headerCorrespondence": ["type", "subject"]
}
}
}
commitizen.sh
파일을 생성하고 다음 내용을 추가:
1. yarn commit
으로 Commitizen을 통해 커밋하는 경우:
2. 일반 git commit
으로 커밋하는 경우:
#!/bin/sh
# Husky 환경 설정
. "$(dirname "$0")/.husky/_/husky.sh"
# pre-commit 훅 실행
if [ -f .husky/pre-commit ]; then
.husky/pre-commit
else
echo "Warning: .husky/pre-commit file not found. Skipping pre-commit hook."
fi
# pre-commit 훅이 성공적으로 실행되었다면 Commitizen 실행 (commit-msg 체크 건너 뜀)
# 직접 git commit 하는 경우가 있으니, 그때도 체크가 필요하기에 commit-msg 유지 필요
if [ $? -eq 0 ]; then
HUSKY=0 cz
else
echo "Pre-commit hook failed. Aborting commit."
exit 1
fi
commit-config.js
파일을 생성하고 다음 내용을 추가:
나는 라벨, 메시지, 이슈번호를 입력받아 다음과 같은 형태를 가지는 커밋 메시지 형태를 만들었다.
type: subject (#ticketNumber)
module.exports = {
prompter: (cz, commit) => {
const typeChoices = [
{ value: '✨ feat', name: '✨ feat:\t새로운 기능' },
{ value: '🐛 fix', name: '🐛 fix:\t버그 수정' },
{ value: '♻️ refactor', name: '♻️ refactor:\t코드 리팩토링' },
{ value: '🎨 design', name: '🎨 design:\tCSS 등 사용자 UI 디자인 변경' },
{
value: '💎 style',
name: '💎 style:\t코드 포맷팅, 코드 변경이 없는 경우',
},
{
value: '📦 chore',
name: '📦 chore:\t빌드 업무 수정, 패키지 매니저 설정, 자잘한 코드 수정',
},
{ value: '💬 comment', name: '💬 comment:\t주석 추가 및 변경' },
{ value: '📚 docs', name: '📚 docs:\t문서 수정' },
{
value: '🚑 !HOTFIX',
name: '🚑 !HOTFIX:\t급하게 치명적인 버그를 고치는 경우',
},
{ value: '🚀 perf', name: '🚀 perf:\t성능 개선' },
];
const questions = [
{
type: 'list',
name: 'type',
message: '1️⃣ 커밋 라벨을 선택하세요:',
choices: typeChoices,
},
{
type: 'input',
name: 'subject',
message: '2️⃣ 커밋 메시지를 입력하세요:',
validate: (input) => {
if (input.length === 0) {
return '커밋 메시지는 비워둘 수 없습니다.';
}
if (input.length > 100) {
return '커밋 메시지는 100자를 넘을 수 없습니다.';
}
return true;
},
},
{
type: 'input',
name: 'ticketNumber',
message: '3️⃣ 이슈 번호를 입력하세요 (숫자만):',
validate: (input) => {
if (!/^\d+$/.test(input)) {
return '유효한 숫자를 입력해주세요.';
}
return true;
},
},
];
cz.prompt(questions).then((answers) => {
const { type, subject, ticketNumber } = answers;
const message = `${type}: ${subject} (#${ticketNumber})`;
const divider = '='.repeat(50);
const decoratedMessage = `✅ 커밋 메시지가 다음과 같아요! 커밋할까요?
${divider}
${message}
${divider}
`;
// 확인 질문
cz.prompt([
{
type: 'confirm',
name: 'confirmCommit',
message: decoratedMessage,
default: false,
},
]).then((confirmAnswer) => {
if (confirmAnswer.confirmCommit) {
commit(message);
} else {
console.log('❌ 커밋이 취소되었습니다.');
}
});
});
},
};
chmod +x .husky/pre-commit
chmod +x .husky/commit-msg
chmod +x commitizen.sh
팀원에게 공유할 때는, 다음 스크립트를 프로젝트에 추가하고,
update_permissions.sh
스크립트 추가:
#!/bin/bash
# Husky 스크립트에 실행 권한 부여
chmod +x .husky/pre-commit
chmod +x .husky/commit-msg
# commitizen.sh 스크립트에 실행 권한 부여
chmod +x commitizen.sh
echo "File permissions updated successfully."
다음 명령어를 실행하라고 하면 된다.
그리고 추가로 권한 부여가 필요하면 update_permissions.sh
내 스크립트만 수정하면 된다.
chmod +x update_permissions.sh
./update_permissions.sh
yarn commit
1) yarn commit + 린팅 오류
2) yarn commit + 린팅 성공
3) git commit + 커밋 메시지 컨벤션 오류
다음은 github의 템플릿 설정과 CODEOWNER 설정을 통해 코드리뷰어 자동 세팅 하는 법을 정리해보려고 한다.
처음 프로젝트 세팅을 맡게 되었을 땐 막막했는데, 한두번 해보니 익숙해지고 재밌는 것 같다.
깊은 이해를 하려면 아직 멀었지만, 누군가에겐 프로젝트 세팅하는데 참고가 되었으면 좋겠다!
+추가로, 잘못된 내용이 있다면 알려주시면 감사하겠습니다!!!!