문제 정의
-
pnpm run dev를 실행 후 해당 페이지에 접속하면, 아래와 같이 Compiling 단계에서 멈춘다.
-
5분 정도 기다리니 아래와 같은 에러가 뜬다. 그와 동시에, 화면은 정상적으로 뜬다.
https://tailwindcss.com/docs/using-with-preprocessors#nesting
사실 수집
모노레포에서, 각 루트 package.json에서 관리하던 각 외부 패키지들을 각 패키지의 package.json으로 모두 옮겼다. 모두 typescript 기반이기에 tsconfig.json파일과 typescript는 각 패키지에 추가해주었다. 그리고 ui, hook같은 react가 필요한 패키지는 해당 패키지를 설치해주었다. 그 이후 이런 에러가 발생한 것이다.
원인 추론
-
현재, 페이지에 접속 자체가 안되어서(로딩 창만 계속 뜬다) 콘솔 창에 아무것도 안뜬다. 디버깅을 할 수가 없다. 이 말은 즉, next.js 서버에서 에러가 뜨고 있다는 것을 의미한다. middleware의 문제인가? middleware파일을 제거해보자.
-
모노레포 문제인가? 다른 앱을 실행해보자.
docs 앱은 즉시 실행이 된다.
모노레포에 문제가 있다는 것은 아니다.
-
contentLayer라이브러리가 컨텐츠를 만들던 중 발생한 문제일까?
빌드 내용 중에 contentLayer metaData가 부적절한 글이 있다고 했는데, 그것이 원인인가?
metaData 제대로 된 글 하나만 남겨두었는데도 안되는 것을 보니 content의 문제는 아니다.
-
모듈을 설치한 이후 발생한 에러이니, 의존성 문제인 듯하다. docs에 @guesung/ui를 설치를 해서 정상 동작하는 지 체크해보자
Spacing 컴포넌트가 잘 동작한다.
-
globalcss파일의 문제인가? import를 제거해보자
// apps/layout.tsx import './globals.css';위 문을 제거하니 해결이 되었다. ✅
그리고 또한, global.css를 import하고, 해당 파일에서 tailwind를 설정하는 아래 옵션을 제거하니 해결이 되었다.
@tailwind base;
@tailwind components;
@tailwind utilities;
확실한 것은, tailwindcss의 문제라는 것이다. 왜냐하면 tailwind 옵션을 globals.css에서 제거하니 해결이 되었기 때문이다.
해결 방안 수립
1. turbo 공식문서에서 tailwindcss를 검색한다.
tailwindcss 공식문서를 참고하라고 한다.
-
tailwindcss, postcss, autoprefixer를 devDependencies(개발 의존성)에 설치한다.
npm install -D tailwindcss postcss autoprefixer -
tailwindcss 설정을 초기화한다.
npx tailwindcss init -p -
tailwind.config.ts파일로 tailwind구성을 설정한다.
/** @type {import('tailwindcss').Config} */ module.exports = { content: [ "./app/**/*.{js,ts,jsx,tsx,mdx}", "./pages/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}", // Or if using `src` directory: "./src/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { extend: {}, }, plugins: [], } -
CSS파일에서 필요한 tailwind 모듈을 import한다.
@tailwind base; @tailwind components; @tailwind utilities;
위와 같은 방법으로 이미 설정이 되어 있다.
2. turbo tailwind example
-
tailwind 설치 : tailwindcss는 web, config-tailwind, ui 각 tailwind 설정 패키지, 혹은 tailwind를 사용하는 패키지에 설치되었다.
-
tailwind config 파일 : tailwind가 설치된 web, config-tailwind, ui 각 패키지에 설정이 되어 있다.
config-tailwind의 tailwind.config.ts 파일을 각 패키지에서 확장하여 사용하는 방식이다.
// packages/config-tailwind/tailwind.config.ts const config: Omit<Config, "content"> = { theme: { extend: { // .. }, }, plugins: [], }; export default config;// apps/web/tailwind.config.ts const config: Pick<Config, "content" | "presets"> = { content: ["./src/app/**/*.tsx"], presets: [sharedConfig], }; export default config;
정리하면, tailwind를 사용하는 앱, 패키지, tailwind 설정 패키지에 설치되어있고, 각 tailwind.config파일이 존재한다.
-
globalscss 파일 : web/docs같은 tailwindcss를 직접적으로 사용하는 패키지
// apps/web/src/app/global.css, packages/ui/src/app/global.css @tailwind base; @tailwind components; @tailwind utilities;
3. Next.js SSR 디버깅
Next.js SSR을 디버깅을 위해서는 아래와 같은 파일을 작성하면 된다.
// launch.json
{
"version": "0.0.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"type": "node-terminal",
"request": "launch",
"command": "pnpm run dev --filter blog",
},
]
}
call stack에 하나씩 쌓인다.
- Next.js debug를 실행
- turbo 명령어 실행(
turbo dev --filter blog) - next 명령어 실행 (
next dev) - Next 서버 실행
하지만, 이는 컴파일이 된 후 디버깅이 가능하다. 컴파일(TS→JS 트랜스파일, JSX → JS 트랜스파일 등)이 애초에 되지 않았기에 디버깅이 불가능하다.
4. 나와 유사한 이슈를 겪은 사람을 체크한다.
tailwindcss사용자 중 나와 같은 이슈를 겪은 사람이 있는 지 구글링을 해보았다.
두 번째 github issue도 나와 같은 이슈를 겪은 것이었다.
여기서, 떠올랐다. 혹시, tailwindcss에서 설정한 파일이 너무 많나? tailwindcss는 컴파일 과정에서 className을 css로 변환해주기에, 이 과정에서 시간이 너무 오래 걸려, 컴파일이 오래 걸린다. 일리가 있는 설명이다.
세 번째의 ‘packages/ui의 모든 파일에 tailwindcss를 적용한다’가 원인이었다. 해당 코드를 제거하니 해결되었다.
조치 및 구현
원인은 확실해졌다. tailwind.config.ts의 content에 ui패키지를 추가했는데, ui패키지의 모든 파일에 적용이 되는데 이 때 node_modules도 적용이 된 것이었다.
그렇다면, 이전에 packages/ui의 모든 파일에 적용할 때는 왜 문제가 되지 않았을까?
에러가 발생하기에 앞서 이번에 작업 중이던 것은 루트에서 관리하던 외부 패키지를 turbo 공식문서에 따라, 각 패키지에서 외부 패키지를 관리하도록 마이그레이션하는 것이었다. 이러던 과정에서 ui에도 여러 패키지가 설치되었고, ui폴더 안의 node_modules에 설치된 것이다. 예상치 못하게 node_modules폴더 안의 모든 항목에 대해 tailwindcss에서 컴파일을 하던 것이었고, 이 과정에서 오래 걸린 것이었다.
blog/tailwind.config.ts에서 ui패키지를 확장하던 기존 코드는 다음과 같다.
module.exports = {
content: [
'src/**/*.{js,ts,jsx,tsx}',
'../../packages/ui/**/*.{js,ts,jsx,tsx}',
],
// ..
};
위는 ui패키지의 모든 js, ts, jsx, tsx 확장자를 가진 모든 파일에 컴파일을 진행한다. 즉 node_modules에 속한 모든 파일에도 컴파일을 진행하는 것이다. 대부분이 js,jsx,ts,tsx확장자 중에 속하기에 컴파일이 되기는 하지만, 양이 너무 많다보니 오래 걸린 것이었다.
위 코드를 아래와 같이 수정하였다.
module.exports = {
content: [
'src/**/*.{js,ts,jsx,tsx}',
'../../packages/ui/src/**/*.{js,ts,jsx,tsx}',
],
// ..
};
tailwindcss 적용이 필요한 ui의 경로를 추가해주었다.
결과 관찰
앞으로 확장성을 고려한 코드를 작성하자.
a/**/*b는 위험한 코드이다. a와 b 사이의 모든 파일들을 포함한다. 모든 파일을 포함할 경우 프로젝트가 확장 됨에 다라 어떤 사이드 이펙트를 일으킬 지 모른다. 다음부터, 이런 와일드 카드를 사용할 때는 반드시 주의하도록 하자.
tailwindcss cli를 이용하여 미리 컴파일한 후 import하는 방식으로 바꾸면 어떨까?
자세한 내용 : tailwindcss를 미리 CSS로 컴파일한 패키지를 다른 앱에서 import하여 사용하기
Next.js Compile중에 발생한 에러라면, Next.js 컴파일에는 어떤 과정이 일어나는 지 먼저 파악했으면 어땠을까?
Next.js컴파일러는 Rust기반의 SWC이며, Next.js 코드를 경량화하여 Javascript코드로 변환하는 역할을 한다. SWC는 개별 파일용 Babel과 출력 번들을 최소화하는 Terser을 대체한다.
즉, Next.js에서 컴파일은 Next.js 코드를 JS코드로 변환하는 것을 의미한다. 브라우저가 이해할 수 있는 언어는 Javascript이기 때문에 변환을 하는 것이다.
tailiwindcss는 zero runtime 언어이다. 이 말은 런타임 과정에서 동적으로 스타일을 만들지 않는다는 말인데, 이는 컴파일 과정에서 만들기 때문이다. 그래서 Next.js 컴파일 과정에서 tailwindcss 컴파일도 함께 이뤄졌던 것이다. 만약 Next.js 컴파일 과정에서 어떤 과정이 일어나는 지 파악을 해도 정확한 원인이 tailwindcss의 컴파일로 범위를 좁혀야했기에 위와 같은 과정을 거쳤을 거라 생각을 한다.