๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ๋ฐํ๋ค๋ณด๋ฉด, ์ปดํฌ๋ํธ๋ฅผ ์ด๋ป๊ฒ ๋ถ๋ฆฌํด์ผํ ๊ฒ์ธ๊ฐ๋ฅผ ๋ง์ด ๊ณ ๋ฏผํ๋ค.
๋, ๋ถ๋ฆฌํ์ฌ ์ด๋ ํ ํจํด์ ์ด์ฉํ์ฌ ์์ฑํ ์ง๋ ๊ณ ๋ฏผํ๋ค.
์ฐ์ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํ๋ ๊ธฐ์ค์ ๋ํด ์์๋ณด์.
๐ฆด ์ปดํฌ๋ํธ ๋ถ๋ฆฌ
1. โป๏ธ ์ฌ์ฌ์ฉ์ฑ (Resualbility)
๐๐ป ๋์ผํ UI๋ ๊ธฐ๋ฅ์ ์ฌ๋ฌ ๊ณณ์์ ์ฌ์ฉํ ๊ฐ๋ฅ์ฑ์ด ์๋ ๊ฒฝ์ฐ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํ๋ค.
๐๐ป ex. Button, Modal, Card, Input
์ฝ๋ ์ข ๋ ์ดํดํ๊ธฐ >> ์ ํธํจ์ cn(tailwind-merge)
tailwind-merge
Merge Tailwind CSS classes without style conflicts. Latest version: 3.1.0, last published: 3 days ago. Start using tailwind-merge in your project by running `npm i tailwind-merge`. There are 6014 other projects in the npm registry using tailwind-merge.
www.npmjs.com
cn: className์ ํฉ์ณ์ฃผ์ด ์์์์ className์ ๋ด๋ ค์ฃผ์์ ๋, ์์ ์ ์ํ ์คํ์ผ๋ง ๊ฐ์ ๋ฎ์ด์์ธ ์ ์๋ค
๐๐ป ํธ์ถํ๋ ๋ถ๋ชจ์์ ์คํ์ผ์ ๋ด๋ ค์ ์คํ์ผ์ ์์ ํ๊ธฐ์ ์ฉ์ดํ๋ค.
import { ComponentProps } from "react";
import { cn } from "../utils/style";
type ButtonProps = ComponentProps<"button"> & {
className?: string;
};
export default function Button({ children, className, ...props }: ButtonProps) {
return (
<button
className={cn(
"w-fit h-fit bg-black px-4 py-1 rounded-sm border-1 border-black-100",
className
)}
{...props}
>
{children}
</button>
);
}
์ฌ๋งํ๋ฉด ํ๋ก์ ํธ์๋ ์ผ๊ด๋๋ ๋์์ธ์ด ์ ์ฉ๋๋ค.
๋ฐ๋ผ์ Button์ด๋ผ๋ ํ๋์ ์ปดํฌ๋ํธ์ ๋ฒํผ Props๋ฅผ ๊ทธ๋๋ก ์ ์งํ๋ฉด์ ๋์์ธ์ ์ ํ๊ณ , ์ฌ์ฉ์ ํ์๋ก ํ๋ ๊ณณ์์ ๊ธฐ์กด button๊ณผ ๋์ผํ๊ฒ <Button></Button>์ ํธ์ถํ์ฌ ์ฌ์ฉํ๋ค.
import Button from "../components/Button";
export default function Main() {
return (
<div className="w-full h-full bg-bg-primary flex flex-col items-center justify-center gap-2">
<Button>Click Me</Button>
<Button>Double Click Me</Button>
<Button className="bg-orange-500 border-white">I'm Orange</Button>
</div>
);
}
๋ค์๊ณผ ๊ฐ์ด <Button></Button> ๋ง์ ๊ฐ๋จํ๊ฒ ํ์ฉํ์ฌ ์ผ๊ด์ ์ธ ๋์์ธ์ Button์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ํธ์ถํ ๋, className์ ๋ด๋ ค์ฃผ์ด ์คํ์ผ์ ๋ณ๊ฒฝํด์ค ์ ์๋ค.
2. ๐ฅ ๋จ์ผ ์ฑ ์ ์์น (Single Responsibility Principle, SRP)
ํ ์ปดํฌ๋ํธ๋ ํ๋์ ์ญํ ๋ง์ ์ํํ๋๋ก ํ๋ค.
๐๐ป ํ๋์ ์ปดํฌ๋ํธ๊ฐ ์ฌ๋ฌ ์ญํ ์ ์ํํ ๊ฒฝ์ฐ ์ ์ง๋ณด์๊ฐ ์ด๋ ต๋ค.
๐๐ป ์ปดํฌ๋ํธ๋ฅผ ์ดํดํ๊ธฐ ์ฌ์
๋ก๊ทธ์ธ์ ํ๊ธฐ ์ํ Form์ ๋ง๋ค์๋ค๊ณ ํ์์ ๋,
import Button from "./Button";
import Input from "./Input";
export default function LoginForm() {
return (
<form className="flex flex-col justify-center items-center px-5 py-3 gap-5 w-[40rem] h-[25rem] border-1 border-gray-400 rounded-lg bg-black-400 shadow-lg shadow-black-600 ">
<h1 className="text-2xl font-bold">Login</h1>
<Input placeholder="ID" />
<Input placeholder="Password" type="password" />
<Button type="submit" className="mt-10">
Sign In
</Button>
</form>
);
}
ํผ์ ๋ก๊ทธ์ธ ํ์ดํ, ์์ด๋, ๋น๋ฐ๋ฒํธ, ๋ก๊ทธ์ธ๋ฒํผ.. ๋ก๊ทธ์ธ ๋ด๋น ์ปดํฌ๋ํธ๋ผ๋ ๊ฒ์ ์ ์ ์๋ค.
ํ์๊ฐ์ ๊ธฐ๋ฅ์?
๋ฐ๋ก Auth๋ฅผ ๋ด๋นํ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด์
import { ComponentProps } from "react";
import LoginForm from "./LoginForm";
import Button from "./Button";
type AuthProps = ComponentProps<"div">;
export default function Auth({ children, ...props }: AuthProps) {
return (
<main
className="flex flex-col justify-center items-center px-5 py-3 gap-5 w-[40rem] h-[25rem] border-1 border-gray-400 rounded-lg bg-black-400 shadow-lg shadow-black-600 "
{...props}
>
<LoginForm />
<Button className="bg-gray-400">Sign Up</Button>
</main>
);
}
"์ธ์ฆ์์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ๊ณผ ํ์๊ฐ์ ๊ธฐ๋ฅ์ด ์๋ค."๊ฐ์ด ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํด์ฃผ์๋ค.
Auth๋ ๋ก๊ทธ์ธ๊ณผ ํ์๊ฐ์ ๊ธฐ๋ฅ์ ๋ ๊ฐ์ง ๊ธฐ๋ฅ์ ํ์ง๋ง ์ ์ฒด์ ์ผ๋ก ์ธ์ฆ์ด๋ผ๋ ํ๋์ ๊ธฐ๋ฅ์ ๋ด๋นํ๋ค.
โญ์ปดํฌ๋ํธ ๋ค์ด๋ฐ๋ ์ค์ํ๋ค.
3. ๐ ๊ฐ๋ ์ฑ (Readability)
๐๐ป ์ฝ๋๊ฐ ํ ๋์ ๋ค์ด์ค๊ณ ์ฝ๊ฒ ์ดํดํ ์ ์๋๋ก ๊ตฌ์กฐ๋ฅผ ์ก๋ ๊ฒ์ด ์ค์ํ๋ค.
๐๐ป ๋๋ฌด ๊ธด ์ฝ๋๊ฐ ์๊ธด๋ค๋ฉด ๊ฐ๋ ์ฑ์ ๋จ์ด๋จ๋ฆฌ๋ฏ๋ก ์ ์ ํ ๋จ์๋ก ๋ถ๋ฆฌํด์ผํ๋ค.
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๊ฐ ์ปดํฌ๋ํธ ํ์ผ์ ์ด๋ํ์ ๋, ์ด๋ ํ ์ปดํฌ๋ํธ๋ค์ด ์์ผ๋ฉฐ, ์ด๋ ํ ์ญํ ์ ํ๋์ง ์ฝ๊ธฐ ์ฝ๋ค.
โ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌ๋ฅผ ์ ํ ๊ฒฝ์ฐ
import { ComponentProps } from "react";
import LoginForm from "./LoginForm";
import Button from "./Button";
type AuthProps = ComponentProps<"div">;
export default function Auth({ children, ...props }: AuthProps) {
return (
<main
className="flex flex-col justify-center items-center px-5 py-3 gap-5 w-[40rem] h-[25rem] border-1 border-gray-400 rounded-lg bg-black-400 shadow-lg shadow-black-600 "
{...props}
>
<form className="flex flex-col justify-center items-center gap-5 w-full">
<h1 className="text-2xl font-bold">Login</h1>
<input className="w-full h-10 bg-[#222] px-2 py-1 rounded-xs border-2 border-black-100 focus:border-cyan-600 outline-none" />
<input className="w-full h-10 bg-[#222] px-2 py-1 rounded-xs border-2 border-black-100 focus:border-cyan-600 outline-none" />
<button className="w-full h-[3rem] bg-cyan-600 px-4 py-1 rounded-sm border-1 border-gray-400 shadow-lg cursor-pointer hover:brightness-110">
Sign In
</button>
</form>
<button className="w-full h-[3rem] bg-cyan-600 px-4 py-1 rounded-sm border-1 border-gray-400 shadow-lg cursor-pointer hover:brightness-110">
Sign Up
</button>
</main>
);
}
๋ฌผ๋ก ์ธํ, ์ธํ, ๋ฒํผ, ๋ฒํผ์ผ๋ก ๋๋ต ์์ ๋ณผ ์ ์์ง๋ง ๋๋ฌด ๋์กํ์ฌ ์ง์ค์ ํํธ๋ฌ๋จ๋ฆฐ๋ค.
์ํ ๊ธฐ๊ฐ์ ๋ค๋ค ๋ฐฉ ์ฒญ์ํ์์๐
โญ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ ๊ฒฝ์ฐ
import Button from "./Button";
import Input from "./Input";
export default function LoginForm() {
return (
<form className="flex flex-col justify-center items-center gap-5 w-full">
<h1 className="text-2xl font-bold">Login</h1>
<Input placeholder="ID" />
<Input placeholder="Password" type="password" />
<Button type="submit" className="mt-10">
Sign In
</Button>
</form>
);
}
๋ ๊น๋ํด์ง๊ณ , ์ด๋ ํ ๊ธฐ๋ฅ์ ํ๋์ง์ ๋ํด ์ง์ ํด๋น ์ปดํฌ๋ํธ๋ก ๋ค์ด๊ฐ์ ํ์ธํ๋ฉด ํ๋์ฉ ์ฝ์ด๋๊ฐ๊ธฐ ํธ๋ฆฌํ๋ค.
๋, ํน์ ์ปดํฌ๋ํธ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ ํด๋น ์ปดํฌ๋ํธ์ ์ฝ๋๋ง์ ๋ถ์ํ๋ฉด ๋๋ค.
4. ๐จ UI ์์
๐๐ป ํ๋์ UI ์์๊ฐ ๋ณต์กํ๊ฑฐ๋ ๊ธธ์ด์ง๋ฉด ๋ณ๋์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ๋ค.
๐๐ป ex. Form, Table, Card, Dropdown, Sheet์ ๊ฐ์ UI ์์๋ ๋ ๋ฆฝ์ ์ธ ์ปดํฌ๋ํธ๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
import Button from "./Button";
type CardProps = {
imageSrc: string;
onhandlePurchase: () => void;
};
export default function Card({ imageSrc, onhandlePurchase }: CardProps) {
return (
<main className="w-50 h-70 bg-bg-primary flex flex-col items-center justify-between bg-black-100 rounded-lg py-3 px-3 gap-2">
<img
className="w-full h-full flex flex-1 rounded-lg items-center justify-center bg-white/30 text-black"
src={imageSrc}
alt="product"
/>
<Button onClick={onhandlePurchase}>Purchase</Button>
</main>
);
}
5. ๐ค ์ํ์ ๋ผ์ดํ์ฌ์ดํด (State and LifeCycle)
์ํ๋ ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๊ด๋ฆฌํ๋ ๋ฐ์ดํฐ -> ๋ณ๊ฒฝ๋๋ฉด ๋ฆฌ๋ ๋๋ง ๋ฐ์
๋ผ์ดํ์ฌ์ดํด์ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๊ฐ ์์ฑ, ์ ๋ฐ์ดํธ, ์ ๊ฑฐ ๋ ๋๋ฅผ ์๋ฏธ
๋ฐ๋ผ์, ๊ฐ๋ฐ ์, ํด๋น ์ปดํฌ๋ํธ๋ค์ด ์ฑ๋ฅ๊ณผ ๋์์ ์ด๋ป๊ฒ ๋ฏธ์น๋ฉฐ, React Developer Tool ์ ๊ฐ์ ํ๋ก๊ทธ๋จ์ ์ฌ์ฉํ์ฌ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๊ณ ์์ง๋ ์์์ง ํ์ธํ๋ค.
React Developer Tools - Chrome ์น ์คํ ์ด
Adds React debugging tools to the Chrome Developer Tools. Created from revision 44c3d3d665 on 2/7/2025.
chromewebstore.google.com
๐ง ๋ค์ ๋ด์ฉ ์์ฑ ์์ .. (์์ง๋์ ๊ฒฐํฉ๋)