모노레포의 기술적 요구사항 (2) - Global Lint / Prettier

2022. 8. 7. 15:25Frontend

 

Overview

모노레포의 문화적 의의를 다룬 이전 포스팅에 이어, 이번 포스팅 시리즈에서는 실제로 모노레포를 팀에 도입하기 위해 거쳐왔던 여러 기술적인 고려 사항들을 간단하게 이야기해보려고 합니다. "모노레포는 이렇게 운영하는 것이 좋다"라는 가이드라기보다는 프론트엔드 팀이 모노레포로 전환하는 과정에서 겪은 여러 문제들과, 이를 해결해 나가는 과정에 대한 기록에 가까울 것 같습니다. 글은 다음과 같은 순서로 작성되었습니다.

 

  1. Workspaces & Dependencies
  2. Global Lint & Prettier
  3. Deploy & Branch Management
  4. Plugin
  5. (Optional) Sparse Checkout

 

 

 

Global Lint & Prettier Settings

모노레포로 이전하면서 콴다 프론트엔드 팀이 사용하는 lint 설정들을 한 군데서 관리할 수 있게 되었습니다. 이는 모든 프로젝트들이 동일한 lint / prettier 설정을 override 해서 사용할 수 있다는 것을 의미하며, 코드 스타일을 일관되게 관리할 수 있음을 의미합니다. 거기에 simple-git-hooks lint-staged와 같은 도구들을 사용하여 항상 lint 테스트를 통과하는 코드들만 커밋될 수 있도록 강제하였고, 이에 따라 항상 팀의 lint설정에 맞는 코드들이 추가되는 것을 보장할 수 있었습니다.

 

 

기존에는 각 서비스의 package.json에 eslint, eslint-config-next, prettier와 같은 린트와 스타일 관련 설정들을 모아두었지만, 모노레포로 이전하면서는 린트 관련된 패키지들은 전부 Root Dependency(global package.json)에만 설치하도록 변경하였습니다. (이에 따라 개별 서비스의 package.json에는 린트와 스타일 관련 패키지들은 하나도 명시되어 있지 않습니다.)

 

// global package.json의 일부
"devDependencies": {
    "@typescript-eslint/eslint-plugin": "^4.29.2",
    "@typescript-eslint/parser": "^4.29.2",
    "@yarnpkg/builder": "^4.0.0-rc.2",
    "eslint": "^7.32.0",
    "eslint-config-airbnb-base": "^14.2.1",
    "eslint-config-next": "^11.1.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-import-resolver-typescript": "^2.4.0",
    "eslint-plugin-import": "^2.24.1",
    "eslint-plugin-prettier": "^3.4.1",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "eslint-plugin-simple-import-sort": "^7.0.0",
    "lint-staged": "^12.1.7",
    "prettier": "^2.6.2",
    "simple-git-hooks": "^2.7.0",
    "typescript": "^4.3.5"
},
"simple-git-hooks": {
    "pre-commit": "yarn lint-staged"
},

 

 

또한 프론트엔드 팀에서 권장하는 린트 설정들을 다음과 같이 Root Directory 하단에 명시하고, 각 서비스의. eslintrc나. eslintrc.json 파일에 이를 override 해서 사용할 수 있도록 하였습니다. 레거시 프로젝트들과 같이 기존에 너무 다른 lint 포맷으로 개발되어 팀에서 권장하는 린트 설정들을 그대로 사용하기 어려운 프로젝트들의 경우 이 설정을 override 한 후에 아래와 같이 커스텀 설정을 추가해서 사용하고, 해당 설정들을 하나씩 지워 나가면서 점진적으로 팀의 컨벤션에 맞춰나가도록 수정하고 있습니다.

 

 

 

// serviceA .eslintrc file
{
  "root": true,
  "extends": ["../../eslintConfig/next.js"],
  "rules": {
    "class-methods-use-this": "off",
    "import/no-unresolved": "off",
    "no-restricted-imports": "off",
    "consistent-return": "off",
    "no-nested-ternary": "off"
  }
}

 

lint-staged #

 

 

lint-staged는 staged 상태의 파일들, 즉 git add를 통해 staging area에 들어가 있는 파일들에 대해서 lint / prettier 검사를 해주는 도구로, 각 서비스의 package.json에 다음과 같이 lint-staged 시에 검사를 수행할 파일의 패턴과 검사 명령어를 명시할 수 있습니다. 아래 simple-git-hooks를 다룬 섹션에서 설명하겠지만, 커밋 시에는 항상 Root Directory에서 pre-commit hook이 동작하게 되고, 이 pre-commit hook이 staged 된 파일에 대해서 동작할 때, 각 파일이 포함된 서비스의 package.json을 찾아서 거기에 명시되어 있는 lint-staged 명령어를 실행하게 됩니다. 

 

// service A's pacakge.json
"lint-staged": {
    "**/*.{js,jsx,ts,tsx}": [
      "eslint --max-warnings 0 --fix --no-ignore",
      "prettier --write"
    ]
 },

 

 

예를 들어 Service A의 파일 하나와 Service B의 파일 하나가 staging area로 들어갔고, 여기서 commit을 하게 된다면, pre-commit hook이 동작하여 두 파일에 대해 lint-staged를 실행하게 되는데, 이때 Service A 파일에 대해서는 Service A의 package.json의 lint-staged 스크립트를 실행하고, Service B 파일에 대해서는 Service B의 package.json의 lint-staged 스크립트를 실행한다는 의미입니다.

 

simple-git-hooks #

simple-git-hooks를 사용하면 해당 프로젝트의. git/hooksdirectory에 hook을 만들어줍니다. 위에서 언급했던 것처럼, 우리는 staged 상태에 있는 파일들이 commit 되기 전에 lint 검사를 수행하고 싶은 것이므로 "pre-commit" 단계에서 staged 된 파일들에 대해 lint 검사를 수행하도록 설정하였습니다.

 

 

git hooks lifecycle

 

// Root package.json
"simple-git-hooks": {
   "pre-commit": "yarn lint-staged"
 },

 

 

 

 

해당 설정을 추가한 후 yarn simple-git-hooks 명령어를 통해 hook을 추가하면 다음과 같이 pre-commit hook이 추가되며, lint-staged 설정에 따라 매 커밋 이전, staged 된 파일들에 대해 린트 검사가 수행됩니다. 

 

 

Conclusion

모노레포의 가장 큰 장점 중 하나는 "모든 프로젝트가 동일한 lint / prettier설정을 공유할 수 있고, 따라서 초기 프로젝트 세팅 시간을 줄이고, 프로젝트 간의 진입 장벽을 낮출 수 있다"라고 생각합니다. 이를 잘 활용하기 위해 simple-git-hooks / lint-staged와 같은 도구들을 사용했고, 이로 인해 모노레포의 코드 스타일이 항상 일관되게 유지됩니다. 다음 포스팅에서는 모노레포의 배포 전략과 브랜치 전략에 대해 살펴보도록 하겠습니다.

반응형