turborepo와 vscode, 그리고 eslint

김장남·2022년 11월 6일
8

비단 turborepo만의 문제는 아니고 monorepo를 사용하면서 vscode를 사용하는 경우에 일어 날 수 있는 문제라고 생각합니다.

급한 분들을 위한 해결책 먼저: 대부분 개발환경으로 vscode를 사용 할 것이고 eslint 플러그인을 사용 할 것이다. 사실 이 문제를 정확히 해결 하기 어려웠던 이유는 여러가지 문제가 뒤섞여 일어나기 때문이다. 문제를 간단히 해결 하기 위해서는 eslint plugin의 workingDirectories옵션에 대해 잘 읽고 vscode workspace 설정에 추가하고 문제를 풀어나가면 생각보다 쉽게 풀 수 있을 것이다.

  • vscode eslint extension의 캐시
  • turborepo의 캐시
  • monorepo환경에 맞는 vscode eslint의 설정
    이것들의 대 환장 콜라보다.

모노레포에서 eslint설정이 쉽게 꼬인 적이 있을 것 이다.
1. 모노레포 설정을 잘 못 한건가?
2. eslint 설정을 잘 못 한건가?
3. 엥 갑자기 tsconfig?!
를 계속 뱅글뱅글 돌며 문제를 해결 하지 못했다.

하나를 해결하면 다른 문제가 튀어나오는 상황의 반복

나의 경우에는 터보레포 base 프로젝트에 airbnb를 추가하기 위해 typescript관련 eslint를 추가하면서 문제는 시작됐다.

  1. 타입스크립트 파서를 사용하게 되면 아래의 오류가 튀어나온다.
ui:lint: 
ui:lint: Oops! Something went wrong! :(
ui:lint: 
ui:lint: ESLint: 7.32.0
ui:lint: 
ui:lint: Error: Error while loading rule '@typescript-eslint/dot-notation': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
ui:lint: Occurred while linting /Users/soorokim/work/playground/packages/ui/Button.tsx
ui:lint:     at getParserServices (/Users/soorokim/work/playground/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:22:15)
ui:lint:     at create (/Users/soorokim/work/playground/node_modules/@typescript-eslint/eslint-plugin/dist/rules/dot-notation.js:85:81)
ui:lint:     at Object.create (/Users/soorokim/work/playground/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:41:20)
ui:lint:     at createRuleListeners (/Users/soorokim/work/playground/node_modules/eslint/lib/linter/linter.js:765:21)
ui:lint:     at /Users/soorokim/work/playground/node_modules/eslint/lib/linter/linter.js:937:31
ui:lint:     at Array.forEach (<anonymous>)
ui:lint:     at runRules (/Users/soorokim/work/playground/node_modules/eslint/lib/linter/linter.js:882:34)
ui:lint:     at Linter._verifyWithoutProcessors (/Users/soorokim/work/playground/node_modules/eslint/lib/linter/linter.js:1181:31)
ui:lint:     at Linter._verifyWithConfigArray (/Users/soorokim/work/playground/node_modules/eslint/lib/linter/linter.js:1280:21)
ui:lint:     at Linter.verify (/Users/soorokim/work/playground/node_modules/eslint/lib/linter/linter.js:1235:25)
docs:lint: ERROR: command finished with error: command (/Users/soorokim/work/playground/apps/docs) yarn run lint exited (1)
web:lint: Error while loading rule '@typescript-eslint/dot-notation': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
web:lint: Occurred while linting /Users/soorokim/work/playground/apps/web/pages/index.tsx
command (/Users/soorokim/work/playground/apps/docs) yarn run lint exited (1)

 Tasks:    0 successful, 1 total
Cached:    0 cached, 1 total
  Time:    1.36s 

 ERROR  run failed: command  exited (1)

타스파서를 사용하려면 parserOptions.project를 설정 해주어야 한다는 내용의 에러이다.

"이정도 에러는 나줘야 개발 할 맛 나지" 하며 google에 해당 에러문구를 검색했고 간단하게 parserOptions.project를 설정 해주고 다시 yarn lint를 실행했다.

// packages/eslint-config-custom/index.js

  extends: [
    'next',
    'airbnb',
    'airbnb-typescript',
    'plugin:import/recommended',
    'plugin:import/typescript',
    'prettier',
    'plugin:react/jsx-runtime',
  ],
  plugins: ['@typescript-eslint', 'import'],
  parser: '@typescript-eslint/parser',
  parserOptions: { // <-이 설정 해줘야 합니다.
    project: ['tsconfig.json'],
  },


깔끔하게 문제가 해결 '된 것 처럼 보였다.'(이 글을 쓰기위해 다시 복기하면서 테스트 하는것인데도 또 속았다)

Turborepo의 cache

린트설정을 이리 저리 바꿔 가며 yarn lint를 실행 시켜가며 lint설정을 끼워 맞춰보던 중에 turborepo에서 최적화를 위해 코드의 변경사항이 없으면 캐싱된 결과를 보여주는 듯 했다. 그래서 설정이 잘 맞아 린트가 한번 통과 되면 다음 설정이 제대로 적용 됐는지 알기 어려운 경우가 몇번 있었다.
이 글에 모든 과정을 적을 순 없지만 정말 많은 방법을 시도 했고

vscode extension cache(?)

린트가 잘 적용되고 있는것인지 확인 하기 위해 린트에 걸릴만한 코드를 적어본다.
airbnb를 적용하게 되면 function 키워드를 사용해서 함수 컴포넌트를 생성하면 에러를 벹는다.

??...? 에러가 안나네? 뭐지? 다시 yarn lint를 실행해서 확인 해봤다.
기대한 대로 error가 잘 난다. eslint는 잘 동작하고 있었다. 근데 vscode에서는 문제가 없는것 처럼보인다. 그 말은 개발 하는 도중에는 문제를 확인 할 수 없다는 말인데 그건 너무 불편하다.

vscode extension은 설정을 변화를 못 알아채는 경우를 많이 겪었기 때문에
Restart Extension host를 실행했다. 시간이 조금 흐른뒤 extension들이 재시작 됐고 파일을 다시 확인 해 보니 예상대로 에러가 발생 했다......??? (예상했던것과는 다른 에러인데?)
기대 했던 문구가 아닌 tsconfig.json을 프로젝트의 root에서 찾을 수 없다는 에러에 멘탈이 터졌다.

근데 에러 내용을 읽어보면 뭔가 이상하다.
'왜 tsconfig.json을 왜 root에 가서 찾지?'

turborepo의 이슈중 이런 내용을 발견하고 각 프로젝트의 eslint설정에 모두 parserOptions.project를 설정 해주었지만 vscode는 정상적으로 동작 하지 않았다.(물론 extenstion 재시작도 해봤다.)(내가 위에 적어둔 설정으로 충분히 동작 한다.ㅠㅠ)

도무지 이해가 가지 않아지만 yarn lint는 정상적으로 동작 했다.
오늘 오전 11시 30분 부터 오후 4시 35분 문제를 해결 하기까지 위 2개의 과정을 미친듯이 반복했다.
(중간에 떡하니 있는 오후 2시 47분의 커밋은 turborepo의 캐싱에 속아 잘못된 설정을 자랑스럽게 푸시 한 나의 모습)
구글링 -> eslint 설정 변경 -> 새로고침 or vscode 껏다 켜기를
무한반복 하다가 갑자기 생각이 다른 방향으로 뻣어 나갔다.

'eslint는 잘못이 없는 것 같은데...?(더 이상 잘못할 건덕지가 없잖아.. 있어선 안돼..)'

vscode eslint extension setting 이슈

혹시나 싶어서 eslint extension에 대해서 검색 해봤다. 왠지 터보레포에만 국한되지는 않을거 같았다.
키워드는 'vscode monorepo eslint'

그 결과 요런 글을 발견 했다.

하지만 내 문제랑 그렇게 크게 상관 있어 보이지 않았다.
대충 뭐 workspace라는 단어 나오고 하니까(영어 잘 못함)
vscode로 프로젝트 설정하는건가..하고 일단 킵했다.
속으로 '꺌꺌 이렇게 쉬운거면 검색하면 나왔겠지! 꺌꺌 참 이런것도 있구만 대충 머리속에 넣어놔야지'
하고 탭은 켜둔 채로 계속 문제를 찾으러 다녔다.(멍청한 나놈...)

한 5분 정도 더 찾다가 다시 열어둔 탭으로 돌아와서 자세히 읽어 봤고
혹시나 싶어서 vscode extension탭에서 eslint를 찾아서 document를 읽어봤다.

근데...!! 이것이 진짜 나에게 필요한 정보 였다.이 부분을 보고 할렐루야를 외쳤다. 내가 찾아 헤매던 상황이 떡하니 나와있었다.
document를 읽어보면 workingDriectories를 입력하는 방법은 2가지가 있는데
mode를 지정하는것, string array로 직접 위치를 지정하는 것

mode: 'location'은 워킹디렉토리를 워크스페이스의 폴더 위치거나 워크스페이스가 아니면 파일이 열린 위치로 정하는것 이라고 한다.
mode: "auto"는 package.json, .eslintignore, eslintrc* 파일 들의 위치를 기반으로 워킹디렉토리가 설정되는 것이라고 한다.

mode: "auto"의 경우 대체로 잘 동작하지만 예상치 못한 상황이 발생 할 수 있으므로 string[]를 사용해 수동으로 적용 해 줄 수 있단다. (꺌꺌꺌 신난다. 왠지 이것으로 많은것이 해결 될것 같은 기분이 들었다.)

해결방법

바로 적용 가자..
(workspace 작업영역 설정 여세욤)
워킹디렉토리 설정 찾고 settings.json에서 편집을 누릅니다.
오토 갑시다 오토!
다시 extensions를 재시작 해주고 기다렸다.

그 결과는??
아 너무 행복하다. 생각 했던대로 동작 잘 했습니다.

yarn lint 명령어도 잘 동작하고 vscode에서도 오류를 잘 잡아냈다.

추가

  • 수동으로 사용 할 수도 있다.
    (상대 경로를 설정 하는것이 좀 헷갈렸는데 .vscode 폴더에 settings.json파일이 존재 하기 때문에 '../apps/*/' 일거라고 생각 했지만 './apps/*/'라고 해주면 됐다. document에 적혀있는 !pwd 옵션이 아마 이 상대경로의 위치와 관련된 옵션 같지만 영어를 못하니 몸으로 떼웠다.)
{
  "eslint.workingDirectories": [{ "pattern": "./apps/*/" }, { "pattern": "./packages/*/" }]
}
  • project root에 tsconfig.json을 추가 하고 싶은 유혹이 엄청났지만 뭔가 문제를 해결 하는 근본적인 해결책이 아닐것 같아서 좀 더 찾아봤는데 생각보다 결과가 좋았다.

생각 해 볼 점

  • turborepo의 기본 셋팅에서는 문제가 생기지 않는다.
    airbnb 린트 설정을 사용하기 위해 '@typescript-eslint/parser'를 사용 하면서parserOptions.project를 설정 해줘야 하는 상황에서 발생 하는 문제
  • 모노레포 환경이 아니더라도 프로젝트의 상위 폴더에서 vscode를 실행 한 경험이 있는데 이럴 때는 어떤 기준으로 eslint를 잡아주나? 궁금했던 적이 있는데 오늘 알게된 설정을 적용 하고 다시 그런 상황이 오면 걱정없이 코드를 작성 할 수 있을 것 같다.
  • 우연히 문제를 해결해 나간 부분이라 추측이 틀릴 수도 있습니다. 좀더 정확한 이유를 아시는 분은 댓글 부탁드립니다. 흑흑 읽어주셔서 감사합니다.

후속 수사 예정

  • 내가 맨날 작업을 완료하고 나면 체크하는 동료가 있는데 이번에도 그 동료한테 이야기 해봤다.
  • 또 나만 겪는 상황이라고 해서 매우 빡쳐있는 상황 꺌꺌 내가 더 정확하게 밝혀내겠다.
profile
React 개발자

2개의 댓글

comment-user-thumbnail
2023년 1월 1일

안녕하세요! 비슷한 에러가 발생해 해당 글 보고 해결 중인데요. 혹시 깃허브에 올려 놓으신 코드가 있을까요? 😂

1개의 답글