배경
1편에서 Next.js와 tailwindcss 그리고 백엔드는 Nest.js로 이루어진 모노레포로 구성된 프로젝트를 진행했으며, pnpm-workspace.yaml 모노레포의 패키지 별 의존성 문제를 해결했다. 그러나 tailwindcss를 사용하는 과정에서 새로운 문제가 발생했다. 그래서 2편에서는 tailwindcss 관련해서 어떤 문제가 발생했는지, 어떻게 해결했는지를 공유하고자 한다.
기술 스택
기술 스택은 1편과 동일하다.
프론트엔드
- 언어: TypeScript
- 프레임워크: Next.js v16
- 스타일링: Tailwindcss v4
- 패키지 매니저: pnpm
백엔드
- 언어: TypeScript
- 프레임워크: Nest.js v11
- ORM: Typeorm
- 데이터 베이스: PostgreSQL
- 패키지 매니저: pnpm
문제 발생
tailwindcss는 apps/frontend에 설치되어 있으며, 문제 원인인 초기 global.css는 이렇게 작성되어 있었다.
@import 'tailwindcss';
@utility hover-bg-smooth {
@apply transition-colors duration-200 ease-in-out;
}tailwindcss v4 방식의 엔트리 포인트와, background-color가 서서히 변하는 애니메이션이 적용된 유틸리티가 작성되어 있다.
그래서 apps/frontend 에서 tailwind 방식의 스타일링을 적용하면 스타일이 반영되지만, packages/ui 에 작성된 스타일링은 반영되지 않는다. 다음은 실제로 ui 패키지에서 구현한 Button 컴포넌트이다.
import { ButtonHTMLAttributes } from 'react';
type ButtonSize = 'sm' | 'md' | 'lg' | 'full';
type ButtonVariant = 'default' | 'outline' | 'ghost';
interface ButtonInterface extends ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
size?: ButtonSize;
variant?: ButtonVariant;
}
const sizes = {
sm: 'px-3 py-1 rounded-lg',
md: 'px-5 py-2 rounded-lg',
lg: 'px-6 py-3 rounded-xl',
full: 'w-full rounded-2xl',
};
const variants = {
default: 'bg-blue-500 hover:bg-blue-600 text-white',
outline: 'bg-transparent hover:bg-blue-50 border border-blue-500 text-blue-500',
ghost: 'bg-transparent hover:bg-blue-50 text-blue-500',
};
export function Button({ children, size = 'md', variant = 'default', ...props }: ButtonInterface) {
return (
<button
{...props}
className={`whitespace-nowrap cursor-pointer disabled:cursor-not-allowed disabled:bg-gray-200 hover-bg-smooth ${sizes[size]} ${variants[variant]} ${props.className}`}
>
{children}
</button>
);
}vaiants에 작성되어 있는 것처럼, 파란 배경의 버튼이 나올 것을 기대하고 있지만, 다음과 같이 스타일이 전혀 먹지히 않은 모습을 볼 수 있다.

원인 분석 및 해결 과정
원인 분석
스타일이 적용되지 않은 원인은 packages/ui 패키지에서 tailwind 방식의 스타일링을 사용할 것임을 인지하지 못한 것이라고 생각했다. 그래서 packages/ui 패키지가 제대로 인식하기 위한 여러 방법을 시도했다.
1번째 시도 - tailwind 의존성 명시 (실패)
packages/ui는 tailwind를 import하지 않기 때문에, 의존성을 명시할 필요는 없지만, 스타일링 방식을 사용하기 위해서 의존성을 명시해야 된다고 생각했다.
그래서 packages/ui/package.json 파일에 의존성을 명시했다.
"dependencies": {
// frontend 패키지를 참조
"frontend": "workspace:*"
},frontend 패키지는 tailwind를 정상적으로 사용할 수 있기에 frontend 패키지를 참조하면 tailwind의 엔트리 포인트가 존재하는 global.css의 영향으로 해결될 줄 알았다. 지금 생각해보면 조금 말이 안되는 해결 방법 이었던 것 같다.
2번째 시도 - global.css 생성 (실패)
2번째 시도로는 packages/ui 패키지에도 global.css를 생성하는 것이다.
// packages/ui/global.css
@import 'tailwindcss';
@utility hover-bg-smooth {
@apply transition-colors duration-200 ease-in-out;
}중복 코드를 작성해서 찝찝하긴 하지만, ui 패키지에도 tailwindcss 엔트리 포인트를 선언했으니 정상적으로 작동할 것으로 예상했다. 그러나 이것도 실패했다.
3번째 시도 - @source 디렉티브 사용 (성공)
Tailwindcss의 공식문서를 읽어보다가 @source 디렉티브를 알게 되었다. 요약하자면, Tailwind가 자동으로 찾지 못하는 파일/폴더를 @source 디렉티브를 활용해서 직접 명시하라는 내용이다.
https://tailwindcss.com/docs/functions-and-directives#source-directive
그래서 packages/ui 패키지에서 tailwind를 사용하기 위해 @source 디렉티브를 통해 직접 명시했다.
@import 'tailwindcss';
@source "../../../packages/ui";
@utility hover-bg-smooth {
@apply transition-colors duration-200 ease-in-out;
}결과
3번째 시도방법을 해본 결과, 정상적으로 스타일링이 적용되었다.

![[트러블 슈팅] Next.js와 tailwindcss로 모노레포 구성하는 법 (2)](/_next/image?url=https%3A%2F%2Fuzsgwbfhiofnnodlhafc.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fposts%2F1774936588490-2875c387-2826-4444-bb01-ff66626329f3-w08j0m.png&w=3840&q=75)