컴포넌트
Astro 컴포넌트는 모든 Astro 프로젝트의 기본 구성 요소입니다. 이는 클라이언트 측 런타임이 없는 HTML 전용 템플릿 컴포넌트이며, .astro
파일 확장자를 사용합니다.
이미 HTML을 알고 있다면, 충분히 첫 번째 Astro 컴포넌트를 작성할 수 있습니다.
Astro 컴포넌트는 매우 유연합니다. Astro 컴포넌트는 SEO 작업을 쉽게 해주는 일반적인 <meta>
태그 모음과 같이 HTML 스니펫만큼 작을 수도 있습니다. 컴포넌트는 헤더나 프로필 카드와 같이 재사용 가능한 UI 요소가 될 수 있습니다. Astro 컴포넌트는 전체 페이지 레이아웃을 포함할 수도 있고, 특별한 src/pages/
폴더에 있다면 전체 페이지 자체가 될 수도 있습니다.
Astro 컴포넌트에 대해 알아야 할 가장 중요한 점은 클라이언트에서 렌더링되지 않는다는 것입니다. 빌드 타임 또는 요청 시 HTML로 렌더링됩니다. 컴포넌트 프런트매터에 JavaScript 코드를 포함할 수 있으며, 이는 사용자의 브라우저로 전송되는 최종 페이지에서 모두 제거됩니다. 그 결과, 기본적으로 추가되는 JavaScript가 없는 더 빠른 사이트를 얻을 수 있습니다.
Astro 컴포넌트에 클라이언트 측 상호작용이 필요한 경우, 표준 HTML <script>
태그를 추가하거나 UI 프레임워크 컴포넌트를 “클라이언트 아일랜드”로 추가할 수 있습니다.
개인화된 콘텐츠나 동적 콘텐츠를 렌더링하는 컴포넌트의 경우 서버 지시어를 추가하여 서버 렌더링을 연기할 수 있습니다. 이러한 “서버 아일랜드”는 전체 페이지 로드를 지연시키지 않고 콘텐츠를 사용할 수 있을 때 해당 콘텐츠를 렌더링합니다.
컴포넌트 구조
섹션 제목: 컴포넌트 구조Astro 컴포넌트는 컴포넌트 스크립트와 컴포넌트 템플릿이라는 두 가지 주요 부분으로 구성됩니다. 각 부분은 서로 다른 역할을 수행하지만, 함께 작동하여 사용하기 쉽고 원하는 모든 것을 구현할 수 있을 만큼 표현력이 풍부한 프레임워크를 제공합니다.
---// 컴포넌트 스크립트 (JavaScript)---
<!-- 컴포넌트 템플릿 (HTML + JS 표현식) -->
컴포넌트 스크립트
섹션 제목: 컴포넌트 스크립트Astro는 코드 펜스(---
)를 사용하여 Astro 컴포넌트의 컴포넌트 스크립트를 구분합니다. Markdown을 사용해본 적이 있다면 프런트매터라고 하는 비슷한 개념을 이미 알고 계실 것입니다. Astro의 컴포넌트 스크립트는 이 개념에서 직접적으로 영감을 받았습니다.
컴포넌트 스크립트에서는 템플릿을 렌더링하는 데 필요한 모든 JavaScript 코드를 작성할 수 있습니다. 여기에는 다음과 같은 것들이 포함됩니다:
- 다른 Astro 컴포넌트 가져오기
- React와 같은 다른 프레임워크 컴포넌트 가져오기
- JSON 파일과 같은 데이터 가져오기
- API 또는 데이터베이스에서 콘텐츠 가져오기
- 템플릿에서 참조할 변수 생성
---import SomeAstroComponent from '../components/SomeAstroComponent.astro';import SomeReactComponent from '../components/SomeReactComponent.jsx';import someData from '../data/pokemon.json';
// `<X title="안녕하세요" />`와 같이 컴포넌트에 전달된 props에 액세스const { title } = Astro.props;// 비공개 API나 데이터베이스에서 외부 데이터 가져오기const data = await fetch('SOME_SECRET_API_URL/users').then((r) => r.json());---<!-- 템플릿 -->
코드 펜스는 그 안에 작성한 JavaScript가 “제한된 영역 안에 있도록” 보장하기 위해 설계되었습니다. 이 코드는 프런트엔드 애플리케이션으로 유출되거나 사용자의 손에 들어가지 않습니다. 따라서 비용이 많이 들거나 민감한 코드(비공개 데이터베이스 호출 등)를 사용자의 브라우저에 노출될 걱정 없이 안전하게 작성할 수 있습니다.
Astro 컴포넌트 스크립트는 TypeScript로 작성되며, 이를 통해 JavaScript에 에디터 도구와 오류 검사를 위한 추가 구문을 사용할 수 있습니다.
컴포넌트 템플릿
섹션 제목: 컴포넌트 템플릿컴포넌트 템플릿은 코드 펜스 아래에 있으며 컴포넌트의 HTML 출력을 결정합니다.
여기에 일반 HTML을 작성하면, 해당 컴포넌트를 가져와 사용하는 모든 Astro 페이지에서 그 HTML이 렌더링됩니다.
하지만 Astro의 컴포넌트 템플릿 구문은 JavaScript 표현식, Astro의 <style>
및 <script>
태그, 가져온 컴포넌트, 특별한 Astro 지시어도 지원합니다. 컴포넌트 스크립트에서 정의된 데이터와 값을 컴포넌트 템플릿에서 사용하여 동적으로 HTML을 생성할 수 있습니다.
---// 컴포넌트 스크립트import Banner from '../components/Banner.astro';import Avatar from '../components/Avatar.astro';import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';const myFavoritePokemon = [/* ... */];const { title } = Astro.props;---<!-- HTML 주석이 지원됩니다! -->{/* JS 주석 구문도 유효합니다! */}
<Banner /><h1>Hello, world!</h1>
<!-- 컴포넌트 스크립트의 props와 다른 변수들을 사용: --><p>{title}</p>
<!-- 컴포넌트 렌더링을 지연하고 대체 로딩 콘텐츠 제공: --><Avatar server:defer> <svg slot="fallback" class="generic-avatar" transition:name="avatar">...</svg></Avatar>
<!-- 하이드레이션을 위해 `client:` 지시어를 사용하여 다른 UI 프레임워크 컴포넌트 포함: --><ReactPokemonComponent client:visible />
<!-- JSX와 유사하게 HTML과 JavaScript 표현식을 함께 사용: --><ul> {myFavoritePokemon.map((data) => <li>{data.name}</li>)}</ul>
<!-- 여러 문자열이나 객체로부터 클래스 이름을 만들기 위해 템플릿 지시어 사용! --><p class:list={["add", "dynamic", {classNames: true}]} />
컴포넌트 기반 설계
섹션 제목: 컴포넌트 기반 설계컴포넌트는 재사용이 가능하고 조합이 가능하도록 설계되어 있습니다. 더 발전된 UI를 만들기 위해 다른 컴포넌트 안에서 컴포넌트를 사용할 수 있습니다. 예를 들어, Button
컴포넌트를 사용하여 ButtonGroup
컴포넌트를 만들 수 있습니다.
---import Button from './Button.astro';---<div> <Button title="Button 1" /> <Button title="Button 2" /> <Button title="Button 3" /></div>
컴포넌트 Props
섹션 제목: 컴포넌트 PropsAstro 컴포넌트는 props를 정의하고 받을 수 있습니다. 이러한 props는 HTML을 렌더링하기 위해 컴포넌트 템플릿에서 사용할 수 있게 됩니다. Props는 프런트매터 스크립트에서 Astro.props
전역 객체를 통해 사용할 수 있습니다.
다음은 greeting
prop과 name
prop을 받는 컴포넌트의 예시입니다. 받을 props들이 전역 Astro.props
객체에서 구조 분해되는 것에 주목하세요.
---// 사용: <GreetingHeadline greeting="Howdy" name="Partner" />const { greeting, name } = Astro.props;---<h2>{greeting}, {name}!</h2>
이 컴포넌트는 다른 Astro 컴포넌트, 레이아웃 또는 페이지에서 가져와 렌더링될 때, 이러한 props들을 속성으로 전달할 수 있습니다.
---import GreetingHeadline from './GreetingHeadline.astro';const name = 'Astro';---<h1>Greeting Card</h1><GreetingHeadline greeting="Hi" name={name} /><p>I hope you have a wonderful day!</p>
TypeScript를 사용하여 Props
타입 인터페이스로 props를 정의할 수도 있습니다. Astro는 프론트매터에서 Props
인터페이스를 자동으로 인식하여 타입 경고/에러를 표시해줍니다. Astro.props
를 구조 분해할 때 이러한 props에 기본값을 지정할 수 있습니다.
---interface Props { name: string; greeting?: string;}
const { greeting = "Hello", name } = Astro.props;---<h2>{greeting}, {name}!</h2>
컴포넌트 props에 값이 전달되지 않았을 때 사용할 기본값을 지정할 수 있습니다.
---const { greeting = "Hello", name = "Astronaut" } = Astro.props;---<h2>{greeting}, {name}!</h2>
<slot />
요소는 외부 HTML 콘텐츠를 위한 플레이스홀더로, 다른 파일의 자식 요소들을 컴포넌트 템플릿에 주입(또는 “슬롯”)할 수 있게 해줍니다.
기본적으로 컴포넌트에 전달된 모든 자식 요소들은 해당 컴포넌트의 <slot />
에 렌더링됩니다.
Astro 컴포넌트로 전달되어 Astro.props
를 통해 컴포넌트 전반에서 사용할 수 있는 속성인 props 와는 달리, 슬롯 은 작성된 위치에 자식 HTML 요소를 렌더링합니다.
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <Logo /> <h1>{title}</h1> <slot /> <!-- 자식 요소 렌더링 --> <Footer /></div>
---import Wrapper from '../components/Wrapper.astro';---<Wrapper title="Fred's Page"> <h2>All about Fred</h2> <p>Here is some stuff about Fred.</p></Wrapper>
이 패턴은 Astro 레이아웃 컴포넌트의 기본이 됩니다: HTML 콘텐츠의 전체 페이지를 <SomeLayoutComponent></SomeLayoutComponent>
태그로 “감싸서” 컴포넌트에 전달하면, 해당 컴포넌트에 정의된 공통 페이지 요소에서 렌더링됩니다.
명명된 슬롯
섹션 제목: 명명된 슬롯Astro 컴포넌트는 명명된 슬롯도 가질 수 있습니다. 이를 통해 해당 슬롯의 이름과 일치하는 HTML 요소만을 특정 슬롯의 위치에 전달할 수 있습니다.
슬롯의 이름은 name
속성을 사용하여 지정됩니다:
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <slot name="after-header" /> <!-- `slot="after-header"` 속성을 가진 자식 요소들이 이 위치에 배치됩니다. --> <Logo /> <h1>{title}</h1> <slot /> <!-- `slot` 속성이 없거나 `slot="default"` 속성을 가진 자식 요소들이 이 위치에 배치됩니다 --> <Footer /> <slot name="after-footer" /> <!-- `slot="after-footer"` 속성을 가진 자식 요소들이 이 위치에 배치됩니다. --></div>
특정 슬롯에 HTML 콘텐츠를 주입하기 위해서는, 자식 요소에 slot
속성을 사용하여 대상 슬롯의 이름을 지정합니다. 컴포넌트의 다른 모든 자식 요소들은 기본(이름 없는) <slot />
에 주입됩니다.
---import Wrapper from '../components/Wrapper.astro';---<Wrapper title="Fred's Page"> <img src="https://my.photo/fred.jpg" slot="after-header" /> <h2>All about Fred</h2> <p>Here is some stuff about Fred.</p> <p slot="after-footer">Copyright 2022</p></Wrapper>
컴포넌트의 <slot name="my-slot" />
플레이스홀더와 매칭시키고 싶은 자식 요소에 slot="my-slot"
속성을 사용하세요.
컴포넌트의 <slot/>
플레이스홀더에 여러 HTML 요소를 <div>
래퍼 없이 전달하려면 Astro의 <Fragment/>
컴포넌트에 slot=""
속성을 사용하세요:
---// 헤드와 바디 콘텐츠를 위한 명명된 슬롯 플레이스홀더가 있는 커스텀 테이블 생성---<table class="bg-white"> <thead class="sticky top-0 bg-white"><slot name="header" /></thead> <tbody class="[&_tr:nth-child(odd)]:bg-gray-100"><slot name="body" /></tbody></table>
slot=""
속성을 사용하여 "header"
와 "body"
콘텐츠를 지정함으로써 여러 행과 열의 HTML 콘텐츠를 주입할 수 있습니다. 개별 HTML 요소에 스타일을 적용할 수 있습니다:
---import CustomTable from './CustomTable.astro';---<CustomTable> <Fragment slot="header"> <!-- 테이블 헤더 전달 --> <tr><th>Product name</th><th>Stock units</th></tr> </Fragment>
<Fragment slot="body"> <!-- 테이블 바디 전달 --> <tr><td>Flip-flops</td><td>64</td></tr> <tr><td>Boots</td><td>32</td></tr> <tr><td>Sneakers</td><td class="text-red-500">0</td></tr> </Fragment></CustomTable>
명명된 슬롯은 반드시 컴포넌트의 직계 자식이어야 합니다. 중첩된 요소를 통해 명명된 슬롯을 전달할 수 없습니다.
명명된 슬롯은 UI 프레임워크 컴포넌트에도 전달할 수 있습니다!
Astro의 슬롯 이름을 map 함수와 같이 동적으로 생성하는 것은 불가능합니다. UI 프레임워크 컴포넌트에서 이러한 기능이 필요한 경우, 프레임워크 자체에서 동적 슬롯을 생성하는 것이 좋은 방법일 수 있습니다.
슬롯 대체 콘텐츠
섹션 제목: 슬롯 대체 콘텐츠슬롯은 대체 콘텐츠도 렌더링할 수 있습니다. 슬롯에 전달된 일치하는 자식 요소가 없는 경우, <slot />
요소는 자체적으로 가지고 있는 플레이스홀더 자식을 렌더링합니다.
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <Logo /> <h1>{title}</h1> <slot> <p>This is my fallback content, if there is no child passed into slot</p> </slot> <Footer /></div>
대체 콘텐츠는 명명된 슬롯에 전달되는 slot=“name” 속성을 가진 일치하는 요소가 없는 경우에만 표시됩니다.
슬롯 요소가 존재하지만 전달할 콘텐츠가 없는 경우 Astro는 빈 슬롯을 전달합니다. 대체 콘텐츠는 빈 슬롯이 전달될 때 기본값으로 사용될 수 없습니다. 대체 콘텐츠는 슬롯 요소를 찾을 수 없을 때만 표시됩니다.
슬롯 전송
섹션 제목: 슬롯 전송슬롯은 다른 컴포넌트로 전달될 수 있습니다. 예를 들어, 중첩된 레이아웃을 만들 때:
------<html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <slot name="head" /> </head> <body> <slot /> </body></html>
---import BaseLayout from './BaseLayout.astro';---<BaseLayout> <slot name="head" slot="head" /> <slot /></BaseLayout>
<slot />
태그에 name
과 slot
속성을 모두 사용하여 명명된 슬롯을 다른 컴포넌트로 전달할 수 있습니다
HomeLayout
으로 전달된 기본 슬롯과 head
슬롯이 BaseLayout
(부모)으로 전달됩니다
---import HomeLayout from '../layouts/HomeLayout.astro';---<HomeLayout> <title slot="head">Astro</title> <h1>Astro</h1></HomeLayout>
HTML 컴포넌트
섹션 제목: HTML 컴포넌트Astro는 .html
파일을 컴포넌트로 가져와서 사용하거나 src/pages/
하위 디렉터리에 페이지로 이 파일들을 배치하는 것을 지원합니다. 프레임워크 없이 구축된 기존 사이트의 코드를 재사용하거나 컴포넌트에 동적 기능이 없도록 하려는 경우 HTML 컴포넌트를 사용할 수 있습니다.
HTML 컴포넌트에는 유효한 HTML만 포함되어야 하므로 Astro 컴포넌트의 주요 기능이 부족합니다.
- 프런트매터, 서버 측 가져오기, 동적 표현식을 지원하지 않습니다.
<script>
태그는 번들링되지 않고is:inline
이 적용된 것처럼 처리됩니다.public/
폴더의 자산만 참조할 수 있습니다.
HTML 컴포넌트의 <slot />
요소는 Astro 컴포넌트에서와 동일하게 작동합니다. 대신 HTML 웹 컴포넌트 슬롯 요소를 사용하려면 <slot>
요소에 is:inline
을 추가하세요.