๐ Tailwind 2์ฅ: ๋ ์ด์์ Flexbox & Grid
๐ ๊ฐ์
Tailwind ๋ก Flexbox ์ Grid ๋ฅผ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ โ CSS ๋ ์ด์์์ ๋ ์ถ์ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ก ์๋ฒฝํ๊ฒ ํต์ ํ๊ธฐ
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐ฆ Flexbox ์์ ์ ๋ณต
- ๐๏ธ Grid ์์ ์ ๋ณต
- โ๏ธ Flex vs Grid: ์ธ์ ๋ฌด์์ ์ธ๊น?
- ๐ป ์ค์ : ์์๋ค ์ปค๋ฎค๋ํฐ ๋ ์ด์์ ๋ฏ์ด๋ณด๊ธฐ
- ๐จ ํํ ์ค์์ ํธ๋ฆญ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 20๋ถ (์ ์ฒด) / ํต์ฌ ํํธ๋ง: 10๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
Flex ๊ธฐ์ด ํด๋์ค โ Grid ๊ธฐ์ด ํด๋์ค โ ์ธ์ ๋ฌด์์ ์ธ์ง ํ๋จ ๊ธฐ์ค โ ์ค์ ์ปค๋ฎค๋ํฐ ๋ ์ด์์
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
-
flex,grid๊ด๋ จ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ฅผ ๋ณด๊ณ ์ฆ์ ์ด๋ค CSS ๊ฐ ๋์ค๋์ง ๋จธ๋ฆฟ์์ ๊ทธ๋ฆด ์ ์๋ค - ๋ด๋น๊ฒ์ด์ ๋ฐ, ์นด๋ ๊ทธ๋ฆฌ๋, ์ฌ์ด๋๋ฐ ๋ ์ด์์์ Tailwind ๋ง์ผ๋ก ๊ตฌํํ ์ ์๋ค
- "์ด๊ฑด Flex ๋ก, ์ ๊ฑด Grid ๋ก" ํ๋จ์ ์๋์ด์ฒ๋ผ ๋ด๋ฆด ์ ์๋ค
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ๐ฃ ์์ฒ (์ ์
): "๋ฆฌ๋ ๋, ์คํฐ๋ ๋ชฉ๋ก ํ์ด์ง ๋ ์ด์์ ๋ง๋ค๋ค๊ฐ ๋งํ์ด์.
display: grid๋ก 3์ด ์นด๋ ๋ง๋ค๋ฉด ๋๋ ๊ฑด ์๋๋ฐ... Tailwind ์์ ์ด๋ป๊ฒ ์จ์?" - ๐ฆ ์ํธ (๋ฆฌ๋): "์์ฒ ๋,
grid-cols-3์ด๋ผ๊ณ ์น๋ฉด ๋ผ์. ๊ทผ๋ฐ ๊ทธ ์ ์ Flex ๋ Grid ๋ฅผ ์ธ์ ์ฐ๋์ง ํ๋จ ๊ธฐ์ค๋ถํฐ ์ก์๋ด์." - ๐จ ์์ (๋์์ด๋): "๊ทธ๋ฆฌ๊ณ ๋ชจ๋ฐ์ผ์์๋ ์นด๋๊ฐ 1์ด, ํ๋ธ๋ฆฟ์ 2์ด, ๋ฐ์คํฌํ์ 3์ด์ด๋ฉด ์ข๊ฒ ์ด์!"
- ๐ฃ ์์ฒ : "...๊ทธ๋ฌ๋ฉด ๋ฏธ๋์ด ์ฟผ๋ฆฌ 3๊ฐ ์จ์ผ ํ๋์?"
- ๐ฆ ์ํธ: "Tailwind ์์
grid-cols-1 md:grid-cols-2 lg:grid-cols-3ํ ์ค์ด์์."
๐ค ์ ์์์ผ ํ๋๊ฐ
๋ ์ด์์์ ๋ชจ๋ UI ์ ๋ผ๋์ผ. ๋ฒํผ ์๊น, ํฐํธ ํฌ๊ธฐ๋ ๋์ค์ ๋ฐ๊ฟ ์ ์์ง๋ง, ๋ ์ด์์์ด ๋ฌด๋์ง๋ฉด ํ์ด์ง ์์ฒด๊ฐ ๋ง๊ฐ์ ธ.
์์ฒ ์ด๊ฐ ์คํฐ๋ ๋ชฉ๋ก ํ์ด์ง๋ฅผ ๋ง๋ค ๋ ๋ฌธ์ ๊ฐ ์๊ฒผ์ด. ์นด๋๋ค์ ๋๋ํ ์ ๋ ฌํ๊ณ ์ถ์๋ฐ:
- CSS Grid ๋ก ๋ง๋ค๋ฉด HTML ๋ณ๊ฒฝ ์์ด ์ด ์๋ฅผ ์กฐ์ ํ ์ ์์ง๋ง, ๋ฏธ๋์ด ์ฟผ๋ฆฌ ์์ฑ์ด ๋ฒ๊ฑฐ๋ก์
- Flexbox ๋ก ๋ง๋ค๋ฉด
flex-wrap์ด ์์ง๋ง, ์นด๋ ๋๋น ๊ด๋ฆฌ๊ฐ ๋ณต์กํด
๊ฒฐ๊ตญ ์์ฒ ์ด๋ CSS ํ์ผ์ ๋ฏธ๋์ด ์ฟผ๋ฆฌ๋ฅผ 3๊ฐ ์์ฑํ๊ณ , ํด๋์ค๋ฅผ 4๊ฐ ๋ง๋ค๊ณ , ๊ทธ๋ฌ๋ค ์์์ด "๋ชจ๋ฐ์ผ ๋ ์ด์์ ๊นจ์ ธ์" ๋ผ๊ณ ํผ๋๋ฐฑ์ ๋ ๋ ธ์ด.
Tailwind ์ Flex/Grid ์ ํธ๋ฆฌํฐ๋ฅผ ์ ๋๋ก ์ตํ๋ฉด, ์ด๋ฐ ๋ ์ด์์ ์ฝ๋๊ฐ HTML ํ ์ค๋ก ํด๊ฒฐ๋ผ.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ค Flex ์ Grid ์ ์ฐจ์ด๋ฅผ ํ ๋ฌธ์ฅ์ผ๋ก ์ค๋ช ํ๋ค๋ฉด?
Flexbox ๋ ์ค๋๊ธฐ ์ค ์์ ์์ด๋ค ์ธ์ฐ๊ธฐ. ์ค ์์ ์์ด๋ค์ ์์๋๋ก ์ธ์ฐ๊ณ , ๊ฐ๊ฒฉ ์กฐ์ , ์ค์ ์ ๋ ฌ์ ํด. ๊ทผ๋ณธ์ ์ผ๋ก 1์ฐจ์ (๊ฐ๋ก ๋๋ ์ธ๋ก ํ ๋ฐฉํฅ) ๋ ์ด์์์ด์ผ.
Grid ๋ ๋ฐ๋ํ ์์ ์์ด๋ค ์ํ๊ธฐ. ๊ฐ๋ก์ธ๋ก ๊ฒฉ์(grid)๋ฅผ ๋จผ์ ๋ง๋ค๊ณ , ๊ฐ ์นธ์ ์์ด๋ค์ ๋ฐฐ์นํด. 2์ฐจ์ (๊ฐ๋ก + ์ธ๋ก ๋์) ๋ ์ด์์์ด์ผ.
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
Flex = ์ค ์ธ์ฐ๊ธฐ (1D). Grid = ๊ฒฉ์ํ ๋ฐฐ์น (2D). ์ค ์ธ์ธ ๋ Flex, ๊ฒฉ์ ๋ฐฐ์น์ Grid.
๐ฆ Flexbox ์์ ์ ๋ณต
๐ ํต์ฌ Flex ํด๋์ค ํ๋์ ๋ณด๊ธฐ
<!-- Flexbox ๊ธฐ๋ณธ ํ์ฑํ -->
<div class="flex">
<!-- ๋ฐฉํฅ ์ค์ -->
<div class="flex flex-row"> <!-- โ ๊ฐ๋ก ๋ฐฉํฅ (๊ธฐ๋ณธ๊ฐ) -->
<div class="flex flex-col"> <!-- โ ์ธ๋ก ๋ฐฉํฅ -->
<div class="flex flex-row-reverse"> <!-- โ ๊ฐ๋ก ์ญ๋ฐฉํฅ -->
<!-- ๋ฉ์ธ ์ถ ์ ๋ ฌ (justify-content) -->
<div class="flex justify-start"> <!-- |โผโผโผ | -->
<div class="flex justify-center"> <!-- | โผโผโผ | -->
<div class="flex justify-end"> <!-- | โผโผโผ| -->
<div class="flex justify-between"> <!-- |โผ โผ โผ| -->
<div class="flex justify-around"> <!-- | โผ โผ โผ | -->
<div class="flex justify-evenly"> <!-- | โผ โผ โผ | -->
<!-- ๊ต์ฐจ ์ถ ์ ๋ ฌ (align-items) -->
<div class="flex items-start"> <!-- ์๋จ ์ ๋ ฌ -->
<div class="flex items-center"> <!-- ์ค์ ์ ๋ ฌ โ ์์ฃผ ์! -->
<div class="flex items-end"> <!-- ํ๋จ ์ ๋ ฌ -->
<div class="flex items-stretch"> <!-- ๋๋ฆฌ๊ธฐ (๊ธฐ๋ณธ๊ฐ) -->
<div class="flex items-baseline"> <!-- ํ
์คํธ ๊ธฐ์ค์ ์ ๋ ฌ -->
<!-- ์ค ๋ฐ๊ฟ -->
<div class="flex flex-wrap"> <!-- ๋์น๋ฉด ๋ค์ ์ค๋ก -->
<div class="flex flex-nowrap"> <!-- ์ ๋ ์ ๋๊น (๊ธฐ๋ณธ๊ฐ) -->
<!-- ์์ ์์ ํฌ๊ธฐ ์ ์ด -->
<div class="flex-1"> <!-- flex: 1 1 0% โ ๊ฐ๋ฅํ ๊ณต๊ฐ ๊ท ๋ฑ ๋ถ๋ฐฐ -->
<div class="flex-auto"><!-- flex: 1 1 auto โ ์์ ์ ํฌ๊ธฐ ๊ธฐ์ค์ผ๋ก ๋ถ๋ฐฐ -->
<div class="flex-none"><!-- flex: none โ ํฌ๊ธฐ ๊ณ ์ -->
<!-- ๊ฐ๊ฒฉ -->
<div class="flex gap-4"> <!-- gap: 1rem (๊ฐ๋ก์ธ๋ก ๋ชจ๋) -->
<div class="flex gap-x-4"> <!-- column-gap: 1rem (๊ฐ๋ก๋ง) -->
<div class="flex gap-y-4"> <!-- row-gap: 1rem (์ธ๋ก๋ง) -->๐งฉ ์ค์ ํจํด: ๋ด๋น๊ฒ์ด์ ๋ฐ
// ์์๋ค ์ปค๋ฎค๋ํฐ ์๋จ ๋ค๋น๊ฒ์ด์
// ๐ฆ ์ํธ: "๋ด๋น๊ฒ์ด์
๋ฐ๋ ๊ต๊ณผ์์ ์ธ Flex ์ฌ์ฉ ์ฌ๋ก์์."
function Navbar() {
return (
// justify-between: ๋ก๊ณ ์ ๋ฉ๋ด๋ฅผ ์์ชฝ ๋์ผ๋ก
// items-center: ์ธ๋ก ์ค์ ์ ๋ ฌ
// h-16: ๋์ด 64px ๊ณ ์
<nav className="flex items-center justify-between px-6 h-16 bg-white border-b border-gray-200">
{/* ๋ก๊ณ ์์ญ */}
<a href="/" className="flex items-center gap-2 font-bold text-xl text-blue-600">
<span>๐</span>
<span>์์๋ค ์ปค๋ฎค๋ํฐ</span>
</a>
{/* ๋ฉ๋ด ์์ดํ
๋ค: ๊ฐ๋ก๋ก ๋๋ํ */}
<div className="flex items-center gap-6">
<a href="/studies" className="text-sm text-gray-600 hover:text-blue-600">์คํฐ๋ ์ฐพ๊ธฐ</a>
<a href="/create" className="text-sm text-gray-600 hover:text-blue-600">์คํฐ๋ ๋ง๋ค๊ธฐ</a>
{/* ๋ก๊ทธ์ธ ๋ฒํผ๋ง flex-none์ผ๋ก ๊ณ ์ ํฌ๊ธฐ */}
<button className="flex-none rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700">
๋ก๊ทธ์ธ
</button>
</div>
</nav>
);
}๐งฉ ์ค์ ํจํด: ์นด๋ ํค๋ (์์ด์ฝ + ํ ์คํธ)
// ์คํฐ๋ ์นด๋ ๋ด ์์ด์ฝ๊ณผ ํ
์คํธ ๊ฐ๋ก ์ ๋ ฌ
function StudyCardHeader({ icon, title, subtitle }: Props) {
return (
// flex items-center gap-3: ์์ด์ฝ๊ณผ ํ
์คํธ ์ํ ์ค์ ์ ๋ ฌ
<div className="flex items-center gap-3">
{/* ์์ด์ฝ: flex-none ์ผ๋ก ํฌ๊ธฐ ๊ณ ์ โ shrink ๋ฐฉ์ง! */}
<div className="flex-none flex items-center justify-center w-10 h-10 rounded-full bg-blue-100 text-blue-600 text-lg">
{icon}
</div>
{/* ํ
์คํธ: min-w-0 ํ์! flex ์์์์ ํ
์คํธ ๋ง์ค์ ์ ์ฉ ์ */}
<div className="min-w-0 flex-1">
<p className="font-semibold text-gray-900 truncate">{title}</p>
<p className="text-sm text-gray-500 truncate">{subtitle}</p>
</div>
</div>
);
}โ ๏ธ ์์ฒ ์ด ์ฃผ์๋ณด: Flex ์์์์
truncate(๋ง์ค์ํ) ๊ฐ ์ ๋จนํ ๋๋min-w-0์ ์ถ๊ฐํด. Flex ์์ดํ ์ ๊ธฐ๋ณธ์ ์ผ๋กmin-width: auto๋ผ์ ๋ด์ฉ๋ฌผ ํฌ๊ธฐ๋งํผ ๋์ด๋๊ฑฐ๋ .
๐๏ธ Grid ์์ ์ ๋ณต
๐ ํต์ฌ Grid ํด๋์ค ํ๋์ ๋ณด๊ธฐ
<!-- Grid ๊ธฐ๋ณธ ํ์ฑํ -->
<div class="grid">
<!-- ์ด ์ ๊ณ ์ -->
<div class="grid grid-cols-1"> <!-- 1์ด -->
<div class="grid grid-cols-2"> <!-- 2์ด -->
<div class="grid grid-cols-3"> <!-- 3์ด -->
<div class="grid grid-cols-4"> <!-- 4์ด -->
<div class="grid grid-cols-12"> <!-- 12์ด (๋์์ธ ์์คํ
๊ทธ๋ฆฌ๋) -->
<!-- ์ด ๋๋น ์๋ ์กฐ์ (auto-fit / auto-fill) -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))]">
<!-- ๊ฐ ์นด๋๊ฐ ์ต์ 280px, ๋จ์ ๊ณต๊ฐ ๊ท ๋ฑ ๋ถ๋ฐฐ -->
</div>
<!-- ํ ์ ์ง์ -->
<div class="grid grid-rows-3"> <!-- 3ํ -->
<!-- ๊ฐ๊ฒฉ -->
<div class="grid gap-4"> <!-- gap: 1rem -->
<div class="grid gap-x-6 gap-y-4"> <!-- ์ด ๊ฐ๊ฒฉ 1.5rem, ํ ๊ฐ๊ฒฉ 1rem -->
<!-- ์์ ์์๊ฐ ์ฐจ์งํ๋ ์ด ์ (span) -->
<div class="col-span-1"> <!-- 1์ด ์ฐจ์ง -->
<div class="col-span-2"> <!-- 2์ด ์ฐจ์ง -->
<div class="col-span-full"> <!-- ์ ์ฒด ์ด ์ฐจ์ง (= ๊ฐ๋ก๋ก ๊ฝ ์ฑ์) -->
<!-- ์์ ์์ ์์/๋ ์์น -->
<div class="col-start-2 col-end-4"> <!-- 2๋ฒ ์ ์์ 4๋ฒ ์ ๊น์ง -->๐งฉ ์ค์ ํจํด: ๋ฐ์ํ ์คํฐ๋ ์นด๋ ๊ทธ๋ฆฌ๋
// ๐จ ์์: "๋ชจ๋ฐ์ผ 1์ด, ํ๋ธ๋ฆฟ 2์ด, ๋ฐ์คํฌํ 3์ด๋ก ํด์ฃผ์ธ์!"
// ๐ฆ ์ํธ: "Tailwind ๋ฐ์ํ ์ ๋์ฌ ์ฐ๋ฉด ํ ์ค์ด์์."
function StudyGrid({ studies }: { studies: Study[] }) {
return (
// grid-cols-1: ๋ชจ๋ฐ์ผ ๊ธฐ๋ณธ 1์ด
// md:grid-cols-2: 768px ์ด์์์ 2์ด
// lg:grid-cols-3: 1024px ์ด์์์ 3์ด
// gap-6: ์นด๋ ๊ฐ 24px ๊ฐ๊ฒฉ
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{studies.map((study) => (
<StudyCard key={study.id} {...study} />
))}
</div>
);
}๐งฉ ์ค์ ํจํด: ๋์๋ณด๋ ๋ ์ด์์ (์ฌ์ด๋๋ฐ + ๋ฉ์ธ)
// ์ฌ์ด๋๋ฐ๊ฐ ๊ณ ์ ๋๋น, ๋ฉ์ธ ์ฝํ
์ธ ๊ฐ ๋๋จธ์ง ๊ณต๊ฐ ์ฐจ์ง
function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
// grid-cols-[240px_1fr]: ์ฌ์ด๋๋ฐ 240px ๊ณ ์ , ๋๋จธ์ง๋ 1fr
// min-h-screen: ์ต์ ๋์ด ํ๋ฉด ์ ์ฒด
<div className="grid grid-cols-[240px_1fr] min-h-screen">
{/* ์ฌ์ด๋๋ฐ: ์ธ๋ก ์ ์ฒด */}
<aside className="bg-gray-900 text-white p-6">
<nav className="flex flex-col gap-2">
<SidebarItem href="/dashboard" icon="๐">๋์๋ณด๋</SidebarItem>
<SidebarItem href="/studies" icon="๐">๋ด ์คํฐ๋</SidebarItem>
<SidebarItem href="/profile" icon="๐ค">ํ๋กํ</SidebarItem>
</nav>
</aside>
{/* ๋ฉ์ธ ์ฝํ
์ธ */}
<main className="p-8 bg-gray-50">
{children}
</main>
</div>
);
}๐งฉ ์ค์ ํจํด: ํ๋กํ ์นด๋ (๋ถ๊ท์น Grid)
// ํต๊ณ ๋ฐ์ค: 3๊ฐ ํญ๋ชฉ ๊ฐ๋ก ๋ฐฐ์น, ๋ง์ง๋ง ํญ๋ชฉ์ 2์นธ ์ฐจ์ง
function ProfileStats() {
return (
<div className="grid grid-cols-3 gap-4">
<StatBox label="์ฐธ์ฌ ์คํฐ๋" value={12} />
<StatBox label="์์ฑ ๊ฒ์๊ธ" value={48} />
{/* col-span-full ๋ก ๋ง์ง๋ง ํต๊ณ๋ ๊ฐ๋ก ์ ์ฒด */}
<div className="col-span-full rounded-xl bg-blue-50 p-4 text-center">
<p className="text-2xl font-bold text-blue-600">๐ ์์ 15%</p>
<p className="text-sm text-gray-600">์ด๋ฒ ๋ฌ ํ๋ ์์</p>
</div>
</div>
);
}โ๏ธ Flex vs Grid: ์ธ์ ๋ฌด์์ ์ธ๊น?
์ด ํ๋จ์ ๋งค๋ฒ ๊ณ ๋ฏผํ๋ ์์ฒ ์ด๋ฅผ ์ํ ์ํธ์ ๊ฒฐ์ ํธ๋ฆฌ:
UI ์์๊ฐ ์๋ค
โ
โโ 1์ฐจ์ ๋ฐฉํฅ(๊ฐ๋ก OR ์ธ๋ก)์ผ๋ก๋ง ๋์ด?
โ โโ โ
FLEX ์ฌ์ฉ
โ ์) ๋ด๋น๊ฒ์ด์
์์ดํ
, ๋ฒํผ ๊ทธ๋ฃน, ์นด๋ ํค๋ (์์ด์ฝ+ํ
์คํธ)
โ
โโ ๊ฐ๋ก + ์ธ๋ก ๋ชจ๋ ์ ์ด๊ฐ ํ์?
โโ โ
GRID ์ฌ์ฉ
์) ์นด๋ ๋ชฉ๋ก(ํx์ด), ๋์๋ณด๋ ๋ ์ด์์, ์ฌ์ง ๊ฐค๋ฌ๋ฆฌ
์ค์ ํ๋จ ๊ธฐ์ค ํ:
| ์ฌ์ฉ ์ฌ๋ก | Flex | Grid | ์ด์ |
|---|---|---|---|
| ๋ด๋น๊ฒ์ด์ ๋ฐ | โ | ๊ฐ๋ก 1์ฐจ์ ๋์ด | |
| ๋ฒํผ ๊ทธ๋ฃน | โ | ๊ฐ๋ก 1์ฐจ์ ๋์ด | |
| ์นด๋ + ์์ด์ฝ ํ | โ | ๊ฐ๋ก 1์ฐจ์ ๋์ด | |
| ์นด๋ ๋ชฉ๋ก (n์ด) | โ | 2์ฐจ์ ๊ฒฉ์ | |
| ์ฌ์ด๋๋ฐ ๋ ์ด์์ | โ | ๊ฐ๋ก 2๊ฐ ์์ญ ๋์ ์ ์ด | |
| ํผ ํ๋ ๋ ์ด์์ | โ | 2์ด ๋ ์ด๋ธ+์ ๋ ฅ ์ ๋ ฌ | |
| ๋ชจ๋ฌ ๊ฐ์ด๋ฐ ๋ฐฐ์น | โ | justify-center items-center |
๐ก ํท๊ฐ๋ฆฌ๋ฉด ์ด๋ ๊ฒ ๊ธฐ์ตํด: "๋์ด์ Flex, ๊ฒฉ์๋ Grid."
๐ป ์ค์ : ์์๋ค ์ปค๋ฎค๋ํฐ ๋ ์ด์์ ๋ฏ์ด๋ณด๊ธฐ
์ ์ฒด ํ์ด์ง ๊ตฌ์กฐ
// ๐ฆ ์ํธ: "ํ์ด์ง ์ ์ฒด ๋ ์ด์์์ Grid ๋ก, ๋ด๋ถ ์์ ๋์ด์ Flex ๋ก โ ์ด๊ฒ ๊ธฐ๋ณธ์ด์์."
function StudyListPage() {
return (
// ์ต์ธ๊ณฝ: Flex ์ธ๋ก โ ํค๋, ๋ฉ์ธ, ํธํฐ ์๊ธฐ
<div className="flex min-h-screen flex-col bg-gray-50">
<Navbar /> {/* flex items-center justify-between */}
{/* ๋ฉ์ธ ์ฝํ
์ธ ์์ญ */}
<main className="flex-1 px-4 py-8 mx-auto w-full max-w-6xl">
{/* ํ์ด์ง ์ ๋ชฉ + ํํฐ ๋ฒํผ: Flex */}
<div className="mb-8 flex items-center justify-between">
<h1 className="text-2xl font-bold text-gray-900">์คํฐ๋ ์ฐพ๊ธฐ</h1>
<div className="flex gap-2">
<FilterButton label="์ ์ฒด" active />
<FilterButton label="React" />
<FilterButton label="Next.js" />
<FilterButton label="TypeScript" />
</div>
</div>
{/* ์นด๋ ๊ทธ๋ฆฌ๋: Grid */}
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{studies.map((study) => (
<StudyCard key={study.id} {...study} />
))}
</div>
</main>
<Footer />
</div>
);
}๐จ ํํ ์ค์์ ํธ๋ฆญ
์ค์ 1: items-center ๋ก ์์ง ์ค์ ์ ๋ ฌ์ด ์ ๋ ๋
{/* โ ์ ๋๋ ๊ฒฝ์ฐ: ๋ถ๋ชจ ๋์ด๊ฐ ์ฝํ
์ธ ๋์ด์ ๊ฐ์์ */}
<div className="flex items-center">
<p>ํ
์คํธ</p>
</div>
{/* โ
๋ถ๋ชจ์ ๊ณ ์ ๋์ด๋ min-h ๋ฅผ ์ง์ ํด์ผ ํจ๊ณผ๊ฐ ๋ํ๋จ */}
<div className="flex items-center min-h-screen"> {/* ํ๋ฉด ์ ์ฒด ๋์ด */}
<p>ํ
์คํธ</p>
</div>์ค์ 2: Grid ์์์์ overflow ๋ฌธ์
{/* โ Grid ์์ ์์ ํ
์คํธ๊ฐ ๋ถ๋ชจ๋ฅผ ๋ซ๊ณ ๋์ฌ ๋ */}
<div className="grid grid-cols-3 gap-4">
<div>
<p className="truncate">์์ฃผ ๊ธด ํ
์คํธ๊ฐ ๊ทธ๋ฆฌ๋ ๊ฒฝ๊ณ๋ฅผ ๋ฌด์ํจ</p>
</div>
</div>
{/* โ
Grid ์์์ min-w-0 ์ถ๊ฐ */}
<div className="grid grid-cols-3 gap-4">
<div className="min-w-0"> {/* ์ด๊ฑฐ ์ถ๊ฐ! */}
<p className="truncate">์์ฃผ ๊ธด ํ
์คํธ๊ฐ ์ด์ ์๋ฆผ</p>
</div>
</div>๊ฟํ: ๋ชจ๋ฌ / ์ค๋ฒ๋ ์ด ์ ์ค์ ๋ฐฐ์น
{/* ํ๋ฉด ์ ์ค์์ ๋ชจ๋ฌ ๋์ฐ๊ธฐ */}
<div className="fixed inset-0 flex items-center justify-center bg-black/50">
<div className="w-full max-w-md rounded-2xl bg-white p-8 shadow-2xl">
{/* ๋ชจ๋ฌ ๋ด์ฉ */}
</div>
</div>๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
| ๊ฐ๋ | ํด๋์ค ์์ | ์ฉ๋ |
|---|---|---|
| Flex ๊ธฐ๋ณธ | flex flex-row gap-4 | ๊ฐ๋ก ๋์ด + ๊ฐ๊ฒฉ |
| Flex ์ ๋ ฌ | justify-between items-center | ์ ๋ + ์์ง ์ค์ |
| Flex ์ค๋ฐ๊ฟ | flex flex-wrap | ๋์น๋ฉด ์ค ๋ฐ๊ฟ |
| Grid ๊ธฐ๋ณธ | grid grid-cols-3 gap-6 | 3์ด ๊ฒฉ์ |
| Grid ๋ฐ์ํ | grid-cols-1 md:grid-cols-2 lg:grid-cols-3 | ๋ฐ์ํ ์ด ์ |
| Grid span | col-span-2, col-span-full | ์ฌ๋ฌ ์นธ ์ฐจ์ง |
| ํ๋จ ๊ธฐ์ค | 1D โ Flex, 2D โ Grid | ๋ ์ด์์ ์ ํ |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ์์์ด "์คํฐ๋ ์นด๋ ๋ชฉ๋ก์ ๋ชจ๋ฐ์ผ 1์ด, ๋ฐ์คํฌํ 3์ด๋ก ๋ณด์ฌ์ฃผ์ธ์" ๋ผ๊ณ ํ๋ค. ์ฌ๋ฐ๋ฅธ Tailwind ์ฝ๋๋?
โ <div class="grid-cols-3 md:grid-cols-1">
โก <div class="grid grid-cols-1 lg:grid-cols-3">
โข <div class="flex flex-wrap cols-3">
โฃ <div class="grid cols-1 desktop:cols-3">
โ
์ ๋ต: โก grid grid-cols-1 lg:grid-cols-3
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: Tailwind ๋ ๋ชจ๋ฐ์ผ ํผ์คํธ ์ผ.
grid-cols-1์ด ๊ธฐ๋ณธ(๊ฐ์ฅ ์์ ํ๋ฉด)์ด๊ณ ,lg:grid-cols-3์ 1024px ์ด์์์๋ง ์ ์ฉ๋ผ. ๊ทธ๋ฆฌ๊ณgridํด๋์ค๋ก Grid ๋ฅผ ๋จผ์ ํ์ฑํํด์ผgrid-cols-*์ด ์๋ํด. - ์ค๋ต ํผ๋๋ฐฑ: โ ์ ๋ฐ์ํ ๋ฐฉํฅ์ด ๊ฑฐ๊พธ๋ก์ผ. ํฐ ํ๋ฉด์์ ์์์ง๋ ๊ฑด Tailwind ์ฒ ํ์ด ์๋์ผ. โข ์
cols-3๊ฐ์ ํด๋์ค๊ฐ ์กด์ฌํ์ง ์์. โฃ ๋desktop:์ด๋ผ๋ prefix ๊ฐ ์์ด. Tailwind ๊ธฐ๋ณธ breakpoint ๋sm, md, lg, xl, 2xl์ด์ผ. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "๋ชจ๋ฐ์ผ ํผ์คํธ โ ์์ ๊ฒ๋ถํฐ ๊ธฐ๋ณธ, ํฐ ๊ฒ๋ถํฐ ์ ๋์ฌ."
Q2. ์์ฒ ์ด๊ฐ ๋ด๋น๊ฒ์ด์ ๋ฐ์์ ๋ก๊ณ ์ ๋ฉ๋ด๋ฅผ ์ ๋์ ๋ฐฐ์นํ๊ณ ์ถ๋ค. ์ด๋ค ํด๋์ค ์กฐํฉ์ด ์ ๋ต?
โ
์ ๋ต: flex justify-between items-center
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
:
justify-between์ ๋ฉ์ธ ์ถ(๊ฐ๋ก) ์์ ์ฒซ ๋ฒ์งธ์ ๋ง์ง๋ง ์์ ์ฌ์ด ๊ณต๊ฐ์ ์ต๋ํ ๋ฒ๋ ค.items-center๋ ๊ต์ฐจ ์ถ(์ธ๋ก) ์์ ์ค์ ์ ๋ ฌ. ์ด ๋ ํด๋์ค ์กฐํฉ์ด ๋ด๋น๊ฒ์ด์ ๋ฐ์ "๋ก๊ณ ์ผ์ชฝ, ๋ฉ๋ด ์ค๋ฅธ์ชฝ, ์์ง ์ค์" ๋ ์ด์์์ ์ ์์ด์ผ. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "๊ฐ๋ก ์ ๋ =
justify-between, ์ธ๋ก ์ค์ =items-center"
Q3. Flex ์์ ์์ truncate (ํ
์คํธ ๋ง์ค์) ๊ฐ ์๋ํ์ง ์์ ๋, ์ํธ๊ฐ ์ ์ํ ํด๊ฒฐ์ฑ
์?
โ
์ ๋ต: ํด๋น Flex ์์์ min-w-0 ์ ์ถ๊ฐํ๋ค.
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: Flex ์์ดํ
์ ๊ธฐ๋ณธ
min-width๋auto์ผ. ์ฆ, ๋ด์ฉ๋ฌผ๋งํผ ๊ณ์ ๋์ด๋ ์ ์์ด์overflow: hidden์ ์ค๋ ํ ์คํธ๊ฐ ์๋ฆฌ์ง ์์.min-w-0์ ์ถ๊ฐํ๋ฉดmin-width: 0์ด ๋์ด ์์ดํ ์ด ์ปจํ ์ด๋๋ฅผ ๋์ง ์๊ณ ,truncate๊ฐ ์ ์ ์๋ํด. Grid ์์๋ ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ณ , ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐ๋ผ. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "Flex/Grid ์์์ ํ
์คํธ ์๋ฆผ์ด ์ ๋๋ฉด
min-w-0์ ์์ฌํด."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋ ๋๋์ด ๋ ์ด์์ ์ก๋ ๊ฒ ๋๋ ต์ง ์์์ก๋ค. ์์ ์ ๋ฏธ๋์ด ์ฟผ๋ฆฌ ์ธ ๋๋ง๋ค CSS ํ์ผ์์ ํค๋งค๊ณ "์ด๊ฒ ์ธ์ ์ ์ฉ๋์ง?" ํ๋๋ฐ, grid-cols-1 md:grid-cols-2 lg:grid-cols-3 ํ ์ค๋ก ๋ฐ์ํ ๊ทธ๋ฆฌ๋๊ฐ ๋๋ฑ ๋๋ ๊ฑธ ๋ณด๊ณ ์ข ์๋ฆ ๋์๋ค.
์ํธ ๋์ด "1D ๋ Flex, 2D ๋ Grid" ๋ผ๊ณ ๋จ์ํ๊ฒ ์ ๋ฆฌํด์ฃผ์ จ๋๋ฐ, ์ด๊ฒ ์๊ฐ๋ณด๋ค ๊ฐ๋ ฅํ ํ๋จ ๊ธฐ์ค์ด๋๋ผ. ์ด์ ๋ ์ด์์ ์ค๊ณํ ๋ ์ ๊ธฐ์ค๋ถํฐ ์๊ฐํ๊ฒ ๋ ๊ฒ ๊ฐ๋ค.
๐ก ์ค๋์ ๊ตํ: "๋ ์ด์์์ Flex ์ Grid ๋ ๋๊ตฌ๋ง ์ ๋๋ก ์๋ฉด 90% ๊ฐ ํด๊ฒฐ๋๋ค. ๋๋จธ์ง 10% ๋
min-w-0๊ฐ์ ํธ๋ฆญ์ด๊ณ ."
ํด๊ทผ ํ์ ์ค์ฟผํธ 3์ธํธ ํ๊ณ , ์์๋ค ์ปค๋ฎค๋ํฐ ์คํฐ๋ ํ์ด์ง ๋ ์ด์์ ์ง์ ๋ง๋ค์ด๋ด์ผ์ง. ๋ฐฐ์ด ๊ฑธ ๊ทธ๋ ์จ๋ด์ผ ๊ธฐ์ต์ ๋จ์ผ๋๊น.