[트러블 슈팅] Next.js와 tailwindcss로 모노레포 구성하는 법 (2)
모노레포TailwindCSSNext.js

[트러블 슈팅] Next.js와 tailwindcss로 모노레포 구성하는 법 (2)

pnpm-workspace 기반 모노레포를 구성하는 과정에서 발생한 트러블 슈팅을 다룬다.

7views·0likes

배경

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에 작성되어 있는 것처럼, 파란 배경의 버튼이 나올 것을 기대하고 있지만, 다음과 같이 스타일이 전혀 먹지히 않은 모습을 볼 수 있다.

image.png

원인 분석 및 해결 과정

원인 분석

스타일이 적용되지 않은 원인은 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번째 시도방법을 해본 결과, 정상적으로 스타일링이 적용되었다.

image.png

Comments