Next 와 Styled-components를 함께 사용하려고 확인을 해보니 Warning 단어를 발견했습니다!
직역해보면,
경고: 런타임 자바스크립트가 필요한 CSS-in-JS 라이브러리는 현재 Server Components에서 지원되지 않습니다. Server Components 및 Streaming과 같은 최신 React 기능과 함께 CSS-in-JS를 사용하려면 라이브러리 작성자가 동시 렌더링을 포함한 최신 버전의 React를 지원해야 합니다.
React Server Components 및 스트리밍 아키텍처를 지원하는 CSS 및 JavaScript 자산을 처리하기 위해 React 팀과 함께 업스트림 API에 대해 협력하고 있습니다.
라고 합니다.
Next.js 에서 app router 방식을 이용하게 되면 기본적으로 모든 컴포넌트가 서버 컴포넌트가 된다고 합니다.
서버 컴포넌트 안에 클라이언트 컴포넌트를 포함시키는건 가능하지만,
그 반대는 안됩니다.
클라이언트가 런타임 일 때, 스타일시트를 생성하고 <style/>
요소로 DOM에 주입합니다.
즉 , 프로젝트가 실행중 일 때 스타일링이 DOM요소에 주입됩니다.
그래서 이 방식이 SSR에서 동작하게 된다면?
SSR은 서버에서 HTML을 만들게 되는데,
Styled-component의 style은 서버가 아닌 클라이언트 런타임때 생성되고 주입되기 때문에,
깜빡임이 존재하게 됩니다.
심지어 emotion은 app router 방식에서 완벽하게 지원되고 있지 않습니다.
Babel Plugin과 Webpack Loader를 사용해서 빌드될 때 별도의 CSS 파일을 생성하게 됩니다. 이 파일 안에서 prop이나 state등에 의한 값들을 CSS Variable로 정의하고 CSS Variable의 값을 변경시킴으로써 동적 스타일링을 구현했습니다.
정리하자면, 런타임에 스타일을 생성하지 않고 JS 파일에 작성된 스타일 코드를 빌드 타임에 CSS 파일로 분리해서 적용합니다.
SSR에서 Runtime CSS-in-JS보단 문제가 적게 됩니다.
하지만 ,
Next.js 13 버전에서는 해당 방식도 클라이언트 컴포넌트에서만 사용이 가능하다고 합니다.
Next.js 는 자체 CSS 라이브러리를 제공했었습니다.
하지만 이후 CSS Module을 기본적으로 지원하게 되었습니다. 그 뒤 SASS 문법과 외부 리액트 CSS 라이브러리 또한 지원해주게 되었습니다.
이유는 아래와 같습니다.
우리는 일부 CSS를 기반으로 하는 기존 스타일과 디자인 시스템을 재사용해야 한다는 필요성이 있음을 발견했습니다. 일반적으로, 이것은 next-css 플러그인을 설치해야 한다는 것을 의미합니다. 우리는 Next.js 사용자의 약 50%가 이 플러그인을 추가한다는 것을 발견했습니다.
빌드 타임에 온전한 파일로 변환되어 추가적 구문 분석이 필요하지 않습니다. 하지만 CSS-in-JS는 코드가 입력되기 때문에 페이지 로딩 시 구문 분석 과정이 필요하게 됩니다.
따라서 CSS-in-JS 보다 성능적으로 좋게 됩니다. (특히 scripting 부분에서)
해당 성능에 대한 부분은 여기블로그 내용을 참고해주세요!(실습하신 내용이 들어가 있습니다!)
공통으로 사용하는 유틸리티 코드를 따로 만들 필요가 없고, 스타일을 module에 넣을지 유틸리티 코드에 넣을지 고민하지 않게 해줍니다.
더불어
shadcn ui, mui, headless ui 등의 대부분의 컴포넌트 라이브러리들이 tailwind 기반으로 작성되어져 있기 때문에 해당 CSS를 사용하는 것이 편하다고 생각 합니다!
저 또한 최종적으론 Tailwind CSS를 선택했습니다!
추가로, shadcn ui 코드를 첨부합니다.
Button 컴포넌트
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }
[React] Server Component, 리액트 서버 컴포넌트 무엇이고 어떻게 활용할까
Next.js 환경에서 CSS in JS Styled-Component의 치명적인 단점
Next.js에서 지원되는 스타일링 기법을 알아보고 최종적으로 TailwindCSS를 선택하는 과정이 좋네요!!
Next.js App Router Example
Next.js App Router Example code
좋은 사이트가 있어서 공유해요