3강. 컴포넌트와 레이아웃으로 반복 없애기
헤더·푸터처럼 모든 페이지에 반복되는 코드를 Astro 컴포넌트와 레이아웃으로 분리하고, props와 slot으로 재사용하는 방법을 배웁니다.

2강에서 만든 소개 페이지에는 <html>, <head>, <body>가 전부 들어 있었습니다. 페이지가 열 개가 되면 같은 코드를 열 번 복사해야 할까요? 이번 강의에서는 컴포넌트와 레이아웃으로 반복을 없애는 방법을 배웁니다.
컴포넌트: 재사용 가능한 UI 조각
src/components/ 폴더에 .astro 파일을 만들면 그것이 컴포넌트입니다. 다른 파일에서 HTML 태그처럼 불러 쓸 수 있습니다.
src/components/Header.astro를 만들어 봅니다.
---
const links = [
{ label: '홈', href: '/' },
{ label: '소개', href: '/about' },
{ label: '블로그', href: '/blog' },
];
---
<header>
<a href="/" class="logo">My Site</a>
<nav>
{links.map((link) => <a href={link.href}>{link.label}</a>)}
</nav>
</header>
<style>
header { display: flex; justify-content: space-between; padding: 1rem; }
nav a { margin-left: 1rem; }
</style>
<style> 태그 안의 CSS는 이 컴포넌트에만 적용됩니다. Astro가 자동으로 고유한 클래스를 붙여 주기 때문에 다른 곳의 header 스타일과 충돌하지 않습니다. 이것을 스코프드 CSS(scoped CSS)라고 합니다.
컴포넌트 사용하기
페이지에서 import 한 뒤 태그로 씁니다. 컴포넌트 이름은 반드시 대문자로 시작해야 합니다.
---
import Header from '../components/Header.astro';
---
<Header />
<h1>홈</h1>
Props: 컴포넌트에 값 전달하기
같은 컴포넌트를 조금씩 다르게 쓰고 싶을 때 props를 사용합니다. Astro.props로 전달받은 값을 꺼냅니다.
src/components/Card.astro:
---
interface Props {
title: string;
description?: string; // ? 는 선택 사항이라는 뜻
}
const { title, description = '설명 없음' } = Astro.props;
---
<div class="card">
<h3>{title}</h3>
<p>{description}</p>
</div>
사용하는 쪽:
<Card title="빠른 속도" description="필요한 JS만 전송합니다." />
<Card title="쉬운 문법" />
interface Props를 적어 두면 잘못된 이름의 값을 넘겼을 때 편집기가 바로 알려줍니다. 필수는 아니지만 습관을 들이면 실수가 크게 줄어듭니다.
레이아웃: 페이지의 공통 뼈대
레이아웃은 “내용만 바뀌는 컴포넌트”입니다. <html>, <head>, 헤더, 푸터를 레이아웃에 넣어 두고, 페이지마다 바뀌는 본문 자리는 <slot />으로 비워 둡니다.
src/layouts/BaseLayout.astro:
---
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
interface Props {
title: string;
}
const { title } = Astro.props;
---
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{title} | My Site</title>
</head>
<body>
<Header />
<main>
<slot /> <!-- 페이지의 내용이 이 자리에 들어갑니다 -->
</main>
<Footer />
</body>
</html>
이제 2강의 소개 페이지는 이렇게 짧아집니다.
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="소개">
<h1>소개 페이지</h1>
<p>Astro를 배우고 있습니다.</p>
</BaseLayout>
<BaseLayout> 태그 사이에 넣은 내용이 레이아웃의 <slot /> 자리에 그대로 들어갑니다. 헤더나 푸터를 고치고 싶으면 레이아웃 파일 하나만 수정하면 모든 페이지에 반영됩니다.
이름 있는 slot
한 레이아웃에 여러 빈자리가 필요하면 slot에 이름을 붙입니다.
<!-- 레이아웃 -->
<aside><slot name="sidebar" /></aside>
<main><slot /></main>
<!-- 페이지 -->
<BaseLayout title="문서">
<nav slot="sidebar">목차</nav>
<p>본문</p>
</BaseLayout>
전역 CSS 적용하기
컴포넌트의 <style>은 해당 파일에만 적용되므로, 글꼴이나 기본 색상처럼 사이트 전체에 쓸 스타일은 src/styles/global.css에 작성하고 레이아웃에서 불러옵니다.
---
import '../styles/global.css';
---
레이아웃의 스크립트 영역에 이 한 줄만 넣으면 그 레이아웃을 쓰는 모든 페이지에 적용됩니다.
정리
- 컴포넌트는
src/components/에 두고 태그처럼 사용합니다.<style>은 해당 컴포넌트에만 적용됩니다. - props로 값을 전달하고,
interface Props로 타입을 적어 두면 실수를 줄일 수 있습니다. - 레이아웃은
<slot />으로 본문 자리를 비워 둔 공통 뼈대입니다. 헤더·푸터 수정은 레이아웃 한 곳에서 끝납니다.
다음 강의에서는 마크다운 파일로 글을 쓰고 자동으로 블로그 페이지를 만들어 주는 콘텐츠 컬렉션을 배웁니다.