-
Notifications
You must be signed in to change notification settings - Fork 7
๐ชต 6. Tailwind CSS๋ก ๋์์ธ ์์คํ ๋ฐ UI ์ปดํฌ๋ํธ ์ธํ
์๋ ๊ธฐ์ค ํ๊ฒฝ ํ์์ ์์ฑ๋์ต๋๋ค.
- vite: 5.4.10
- react: 18.3.1
- typescript: 5.6.2
- tailwind: 3.4.14
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ํ ๊ธฐ์ค์ ์ด ํ์ด์ง๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
ํจ์จ์ ์ธ UI ์ปดํฌ๋ํธ ์คํ์ผ๋ง: Tailwind CSS + cn.ts
์ด ๊ธ์ ๊ธฐ์ค์ผ๋ก ์ค์นํ๋ ๋ด์ฉ์ ๋ค๋ฃจ๊ณ ์์ต๋๋ค.
ํ๋ก์ ํธ์ ์คํ์ผ๋ง์ ๋น ๋ฅด๊ฒ ๊ตฌํํ๊ธฐ ์ํด Tailwind CSS๋ฅผ ์ค์นํ์ต๋๋ค.
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
์์ฑ๋ tailwind.config.js
ํ์ผ์ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ค๋๋ค. /src
์ ๋ชจ๋ ์ปดํฌ๋ํธ์ ํ์ด์ง์์ Tailwind CSS๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ์ธํ
ํฉ๋๋ค.
// tailwind.config.js
module.exports = {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
src/index.css
ํ์ผ์ Tailwind CSS์ ๊ธฐ๋ณธ ๋๋ ํฐ๋ธ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Tailwind CSS ํด๋์ค๋ค์ ์๋์ผ๋ก ์ ๋ ฌํด์ฃผ๋ ํ๋ฌ๊ทธ์ธ์
๋๋ค. ์ฝ๋ ์ผ๊ด์ฑ์ ๋์ด๊ธฐ ์ํด prettier-plugin-tailwindcss
ํ๋ฌ๊ทธ์ธ์ ์ถ๊ฐํฉ๋๋ค.
npm install -D prettier prettier-plugin-tailwindcss
์ค์น ํ, .prettierrc
ํ์ผ์ ์๋์ ๊ฐ์ด ์ค์ ์ ์ถ๊ฐํ์ธ์.
{
"plugins": ["prettier-plugin-tailwindcss"]
}
Tailwind CSS์ theme.extend.colors
์ต์
์ ํ์ฉํด ํ๋ก์ ํธ์์ ์ฌ์ฉํ ์ปฌ๋ฌ ํ๋ ํธ๋ฅผ ์ค์ ํฉ๋๋ค.
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
violet: {
50: '#EBE9FF',
100: '#DAD7FF',
200: '#BEB6FF',
// ...
},
halfbaked: {
50: '#F0FAFB',
100: '#D9F2F4',
200: '#B8E5E9',
// ...
},
// ํ์ํ ์ถ๊ฐ ์์ ์ ์
},
},
},
}
์ด ์ปฌ๋ฌ ์์คํ ์ ๋์์ธ ๊ฐ์ด๋์ ์ผ๊ด๋ ์์์ ์ ์ฉํ ์ ์๋๋ก ํฉ๋๋ค. ํผ๊ทธ๋ง์ ์ปฌ๋ฌ ๋ณ์๊ฐ ์๋ค๋ฉด ๋์ฑ ์ฝ๊ฒ ๋๊ฒ ๋ค์.
๋จผ์ ํจ์จ์ ์ธ ์ปดํฌ๋ํธ ์คํ์ผ๋ง์ ์ํ ํต์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ค์นํฉ๋๋ค.
npm install clsx tailwind-merge class-variance-authority
๊ทธ๋ฆฌ๊ณ src/lib/utils/cn.ts
ํ์ผ์ ์์ฑํด cn
ํจ์๋ฅผ ๋ง๋ญ๋๋ค.
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
๊ณตํต ์ปดํฌ๋ํธ๋ก ์ ๋ช
ํ Button
์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด๋ด
๋๋ค.
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/utils/cn';
const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 whitespace-nowrap 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 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
{
variants: {
variant: {
proimary: 'bg-violet-500 border-2 border-violet-950 hover:bg-violet-600',
secondary: 'bg-eastbay-900 border-2 border-violet-950 hover:bg-eastbay-950',
},
size: {
sm: 'h-11 w-full text-2xl font-medium text-stroke-md',
lg: 'h-14 rounded-2xl w-full text-2xl font-medium text-stroke-md',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'proimary',
size: 'lg',
},
},
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({ className, variant, size, ...props }, ref) => {
return <button className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
});
Button.displayName = 'Button';
export { Button, buttonVariants };
<Button onClick={handleSubmit} className="font-neodgm-pro text-2xl font-normal">์ ์ถ</Button>
-
์ฒด๊ณ์ ์ธ ๋์์ธ ์์คํ
๊ตฌํ
- ์ผ๊ด๋ ํด๋์ค ์ฒ๋ฆฌ ํจํด์ผ๋ก ์ปดํฌ๋ํธ ์คํ์ผ๋ง
- ํ๋ก์ ํธ ์ ๋ฐ์ ๋์์ธ ์ผ๊ด์ฑ ํ๋ณด
- ์ฝ๋ ์คํ์ผ์ ๊ท๊ฒฉํ ๋ฌ์ฑ
-
์๋ํ๋ ์คํ์ผ ๊ด๋ฆฌ
- ์ปดํฌ๋ํธ ๋ด๋ถ/์ธ๋ถ ์คํ์ผ ์ฐ์ ์์ ์๋ ์กฐ์
- ํด๋์ค ์ถฉ๋ ์๋ ์์ ํ ์คํ์ผ ํ์ฅ
- ์กฐ๊ฑด๋ถ ์คํ์ผ๋ง์ ๊ฐ์ํ
-
๊ฐ๋ฐ ์์ฐ์ฑ ํฅ์
- ํ์
์์คํ
์ ํตํ
props
์๋ ์์ฑ - ์ปดํฌ๋ํธ
variants
์size
๋ฑ์ ํ์ ์์ ์ฑ - ๋น๋ ํ์ ์ต์ ํ๋ก ๋ฐํ์ ์ฑ๋ฅ ๋ณด์ฅ
- ํ์
์์คํ
์ ํตํ
-
ํ์ฅ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ ์ํคํ
์ฒ
- ์คํ์ผ ๋ณํ์ ์ฌ์ฌ์ฉ์ฑ ํ๋ณด
- ๊ธฐ์กด ์ปดํฌ๋ํธ์ ์ ์ฐํ ํ์ฅ
- ์๋ก์ด ๋์์ธ ์๊ตฌ์ฌํญ์ ๋น ๋ฅธ ๋์
- 1. ๊ฐ๋ฐ ํ๊ฒฝ ์ธํ ๋ฐ ํ๋ก์ ํธ ๋ฌธ์ํ
- 2. ์ค์๊ฐ ํต์
- 3. ์ธํ๋ผ ๋ฐ CI/CD
- 4. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด Canvas ๊ตฌํํ๊ธฐ
- 5. ์บ๋ฒ์ค ๋๊ธฐํ๋ฅผ ์ํ ์์ CRDT ๊ตฌํ๊ธฐ
-
6. ์ปดํฌ๋ํธ ํจํด๋ถํฐ ์น์์ผ๊น์ง, ํจ์จ์ ์ธ FE ์ค๊ณ
- ์ข์ ์ปดํฌ๋ํธ๋ ๋ฌด์์ธ๊ฐ? + Headless Pattern
- ํจ์จ์ ์ธ UI ์ปดํฌ๋ํธ ์คํ์ผ๋ง: Tailwind CSS + cn.ts
- Tailwind CSS๋ก ๋์์ธ ์์คํ ๋ฐ UI ์ปดํฌ๋ํธ ์ธํ
- ์น์์ผ ํด๋ผ์ด์ธํธ ๊ตฌํ๊ธฐ: React ํ๊ฒฝ์์ ํจ์จ์ ์ธ ์น์์ผ ์ํคํ ์ฒ
- ์น์์ผ ํด๋ผ์ด์ธํธ ์ฝ๋ ๋ถ์ ๋ฐ ๊ณต์
- 7. ํธ๋ฌ๋ธ ์ํ ๋ฐ ์ฑ๋ฅ/UX ๊ฐ์
- 1์ฃผ์ฐจ ๊ธฐ์ ๊ณต์
- 2์ฃผ์ฐจ ๋ฐ๋ชจ ๋ฐ์ด
- 3์ฃผ์ฐจ ๋ฐ๋ชจ ๋ฐ์ด
- 4์ฃผ์ฐจ ๋ฐ๋ชจ ๋ฐ์ด
- 5์ฃผ์ฐจ ๋ฐ๋ชจ ๋ฐ์ด
- WEEK 06 ์ฃผ๊ฐ ๊ณํ
- WEEK 06 ๋ฐ์ผ๋ฆฌ ์คํฌ๋ผ
- WEEK 06 ์ฃผ๊ฐ ํ๊ณ