tawilnd shadcn
이전에 내가 알고있던 배경 지식은 vercel 디자인 ai v0이 shadcn/ui 기반인 것만 알고있었다.
부트스트랩 같이 미리 디자인 된 컴포넌트를 제공해주는 라이브러리 같다. 이러한 라이브러리는 수없이 많은데 shadcn을 왜 쓰는지 조사해보겠다.
컴포넌트 라이브러리가 아니다?
공식문서에 보면 처음부터 shadcn을 이렇게 소개한다.
This is NOT a component library.
부트스트랩 처럼 공통 컴포넌트를 지원하는 컴포넌트 라이브러리인줄 알았는데, 컴포넌트 라이브러리가 아니라고 선을 그었다. 그럼 도대체 무엇이 다를까?
shadcn은 이렇게 말한다.
It's a collection of re-usable components that you can copy and paste into your apps.
I mean you do not install it as a dependency. It is not available or distributed via npm.
바로 앱에 복사하여 붙여넣을 수 있고, 재사용성이 가능한 컴포넌트 모음이라는 말이다.
컴포넌트 라이브러리가 아닌 이유는 종속성을 설치하지 않아서 npm을 통해 사용할 수 없거나 배포되지 않는다.
필요한 컴포넌트가 생기면 그에 맞게 코드를 붙여넣고 커스텀을 할 수 있다.
부트 스트랩도 커스텀할 수 있지 않나? 아직은 shadcn에 대한 의문이 생긴다.
부트스트랩 비교
shadcn 사용법을 알기 전에 유사한 라이브러리인 부트스트랩에 대해서 알아보겠다.
부트스트랩이란 미리 만들어진 컴포넌트들을 가져와 사용하는 프레임워크이다.
<link rel="stylesheet" href="<https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css>">
이렇게 cdn을 가져와 그에 맞는 컴포넌트를 복사 혹은 미리 정의된 클래스 이름을 불러와 공통 컴포넌트를 사용한다.
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Accordion Item#1
</button>
</h2>
</div>
</div>
장점
반응형 지원
미리 만들어진 컴포넌트를 사용함으로 빠른 개발 시간으로 높은 생산성
단점
디자인의 개성이 떨어짐
사용하지 않는 컴포넌트라도 템플릿을 모두 다 가져오기 때문에 낮은 성능
shadcn은 부트스트랩의 가장 큰 단점인 낮은 성능이라는 근본적인 문제를 해결하기 위해 바로 종속성을 없애는 것이다.
그래서 shadcn은 자신을 This is NOT a component library.라고 소개한 것이기도 하다.
자주 묻는 질문
Why copy/paste and not packaged as a dependency?(왜 복사붙여 넣기를 하고, 종속성을 패키지하지 않은 이유는 무엇인가요?)
몇가지의 세팅으로만 통해 필요에 맞는 컴포넌트를 커스텀화 할 수 있기 때문입니다.
Do you plan to publish it as an npm package? (npm패키지로 출시할 계획이 있나요?)
이에 대해서는 shadcn은 단호하게 없다고 한다.
npm으로 출시하면 무슨 단점이 있길래 이렇게 단호하게 말할 수 있는 것일까?
npm패키지로 컴포넌트를 패키징하는 것의 단점 중 하나는 스타일이 구현과 결합된다는 것이다. 컴포넌트의 디자인은 구현과 분리되어야 한다고 주장한다.
설치 & 사용 방법
공식 문서에도 잘 나와있어, 핵심적인 부분만 작성했다.
Next.js Install and configure Next.js. ui.shadcn.com
npx create-next-app@latest my-app --typescript --tailwind --eslint
npx shadcn-ui@latest init
신기하게 명령어를 통해서 컴포넌트를 불러오는 것 같았다.
npm으로 배포하지 않는다는 것을 보고 package.json 파일을 보았는데, shadcn에 대한 명시가 되어있지 않았다.
npx shadcn-ui@latest add button
버튼을 추가하는 명령어를 입력했더니 패키지에 추가되는게 아니라 컴포넌트 폴더 > 파일에 추가되었다.
처음에 컴포넌트들의 스타일이 적용이 안되는 문제가 발생했었는데 tailwind css세팅이 되지 않았다.
import "../app/globals.css";
컴포넌트 파일에 해당 css 파일을 불러오니 해결 되었다.
css 파일에는 다음과 같은 선언이 있었기 때문에 스타일이 적용된다.
@tailwind base;
@tailwind components;
@tailwind utilities;
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
...
}
)
export interface ButtonProps
...
}
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}
/>
)
}
)
...
이런 식으로 코드가 자동적으로 불러와지는 것을 볼 수 있다.
tailwind와 같이 사용되는 이유?
먼저 처음 생각나는건 zero runtime이기 때문일 것 같다.
server component에서 css in js 라이브러리인 styled-components 나 emotion css를 사용하게 된다면 실행되지 않는다. 왜냐하면 두 라이브러리는 런타임 환경 설정이 필요하기 때문이다.
하지만, tailwind css는 zero run time으로 따로 런타임 환경이 필요하지 않기 때문에 가장 큰 장점으로 보았다.
(”shadcn을 제작한 회사 vercel은 next도 제작하였기 때문에 해당 app dir 환경에서도 돌아갈 수 있도록 구성하기 위해 tailwind를 채택하지 않았을까?” 라고 추측 해보았다.)
추가로 tailwind를 사용하는 이유를 알기 위해서 tailwind의 장점을 찾아보았다.
스타일 클래스의 이름을 생각할 필요가 없습니다.
클래스 이름 자체가 스타일 적용 문법이기 때문에 따로 해당 컴포넌트에 맞는 클래스 이름을 제작할 필요가 없다는 것이다.
스타일 파일과 컴포넌트 구성 파일이 동일합니다.
컴포넌트 제작 파일에서 스타일도 적용하기 때문에 코드 사이의 컨텍스트 전환이 훨씬 적다.
Shadcn/ui를 사용하는 이유
tailwind css를 사용한다면 shadcn/ui를 안쓸 이유가 없다. 별도의 npm 설치가 필요한 것이 아니기 때문에 번들 사이즈가 커지지도 않고, 커스텀하기 쉽기 때문이다.
shadcn이 왜 컴포넌트 라이브러리라고 주장하지 않는지 알 것 같다.
별도의 설치가 필요 없음
bootstrap은 별도의 스타일 파일이 필요하지만, shadcn은 tailwind 만 있으면 직접 구현 가능
필요한 구현 부분안 쉽게 가져다 쓰기 때문에 커스텀이 쉬움
우리 회사에서 shadcn을 사용하는 이유?
빠른 개발 생산성이 가장 큰 이유이지 않을까 싶다.
파일 구성도 명령어로 빠르게 가능하고, shadcn이 구성한 디자인이 아니더라도 tailwind css만 알고 있다면 빠르게 커스터마이징이 가능하기 때문에 사용하는 것 같다.
또한 복잡한 구현이 필요한 아코디언, 툴팁, 페이지네이션 등 이러한 컴포넌트를 바로 구현해줄 수 있다. 물론 디자인은 그에 맞게 변경을 해야하지만 shadcn이 구현을 다 해주기 때문에 개발자는 구현보다 스타일에 집중할 수 있다.
Reference
[CSS] 왜 Next.js는 tailwind를 추천할까