Astro 가이드
블로그를 만들 때마다 “이 작은 사이트에 React가 정말 필요할까?” 하는 생각이 든 적 있나요? 글 몇 개 보여주는데 수백 KB의 JavaScript를 다운로드해야 한다는 게 비효율적으로 느껴지더라고요.
Astro는 이런 고민에서 출발한 웹 프레임워크입니다. 블로그, 마케팅 페이지, 포트폴리오처럼 콘텐츠를 보여주는 것이 주목적인 사이트를 만들 때, 불필요한 JavaScript를 배제하고 빠른 로딩 속도와 뛰어난 SEO에 집중합니다.
왜 Astro를 배워야 할까요?
콘텐츠 중심 웹사이트를 만든다면 Astro가 탁월한 선택지입니다. 특히 성능과 SEO가 중요한 프로젝트에서 빛을 발합니다.
주요 사용 사례
전통적인 방식 Astro
─────────────────────────────────────────────────────────────
블로그 (React + Next.js) → 기본적으로 정적 HTML 생성
마케팅 페이지 (SPA) → 0 JS 기본, 필요할 때만 추가
문서 사이트 (Gatsby) → 빠른 빌드, 작은 번들 크기
포트폴리오 (Vue + Nuxt) → 다양한 프레임워크 혼용 가능
이커머스 (무거운 JS) → 상품 페이지는 정적, 장바구니만 동적
흔한 오해와 실제
- ❌ “정적 사이트 생성기는 기능이 제한적이지 않나?” → Astro는 필요한 곳에만 JavaScript를 추가할 수 있습니다
- ❌ “React를 포기해야 하나?” → React, Vue, Svelte 등 원하는 프레임워크를 함께 사용 가능
- ❌ “동적 기능은 못 만들지?” → Islands Architecture로 인터랙티브 컴포넌트를 독립적으로 추가
Astro란?
콘텐츠 중심 웹사이트를 위한 올인원 웹 프레임워크입니다. JavaScript 오버헤드를 줄이기 위해 “Islands Architecture“라는 새로운 프런트엔드 아키텍처를 도입했습니다.
핵심 특징
1. 제로 JavaScript 기본값
Astro는 기본적으로 JavaScript를 클라이언트로 보내지 않습니다. 모든 컴포넌트를 빌드 타임에 HTML로 렌더링합니다.
---
// 이 코드는 빌드 타임에만 실행됩니다
const posts = await fetchPosts();
---
<ul>
{posts.map(post => (
<li>{post.title}</li>
))}
</ul>
결과:
- 브라우저로 전송되는 것: 순수 HTML
- JavaScript 번들 크기: 0 KB
- 렌더링 속도: 즉시 표시
2. Islands Architecture
페이지의 대부분은 정적 HTML이고, 필요한 부분만 인터랙티브하게 만드는 아키텍처입니다.
전통적인 SPA Islands Architecture
┌─────────────────┐ ┌─────────────────┐
│ │ │ 정적 HTML │
│ 전체가 │ │ ┌───────┐ │
│ JavaScript │ → │ │Island1│ │
│ │ │ └───────┘ │
│ │ │ 정적 HTML │
│ │ │ ┌────────┐ │
└─────────────────┘ │ │Island2 │ │
전체 무거움 └──────┴────────┘─┘
필요한 곳만 무거움
실제 예시:
<!-- 정적 헤더 -->
<header>
<h1>My Blog</h1>
</header>
<!-- 인터랙티브한 검색 바 -->
<SearchBar client:load />
<!-- 정적 컨텐츠 -->
<article>
<p>블로그 글 내용...</p>
</article>
<!-- 인터랙티브한 댓글 섹션 -->
<Comments client:visible />
이 페이지에서 JavaScript가 필요한 부분은 SearchBar와 Comments뿐입니다. 나머지는 순수 HTML로 제공됩니다.
3. UI 프레임워크 무관
React, Vue, Svelte, Preact 등 원하는 프레임워크를 자유롭게 선택할 수 있습니다. 심지어 하나의 페이지에서 여러 프레임워크를 섞어 쓸 수도 있습니다.
---
import ReactCounter from './ReactCounter.jsx';
import VueCalendar from './VueCalendar.vue';
import SvelteChart from './SvelteChart.svelte';
---
<ReactCounter client:load />
<VueCalendar client:visible />
<SvelteChart client:idle />
왜 이렇게 할까요?
- 기존 컴포넌트를 재사용하고 싶을 때
- 팀마다 선호하는 프레임워크가 다를 때
- 점진적으로 프레임워크를 전환하고 싶을 때
먼저, 기초부터 이해하기
Astro를 이해하려면 먼저 왜 기존 프레임워크가 무거워졌는지 알아야 합니다.
전통적인 SPA의 문제점
React나 Vue로 만든 Single Page Application은 모든 페이지를 JavaScript로 렌더링합니다.
// 브라우저가 해야 할 일
1. HTML 다운로드 (거의 비어있음)
2. JavaScript 번들 다운로드 (수백 KB~수 MB)
3. JavaScript 파싱 및 실행
4. React/Vue가 DOM을 생성
5. 화면에 콘텐츠 표시 👈 여기서야 사용자가 뭔가 볼 수 있음
문제:
- 느린 네트워크에서는 흰 화면이 오래 지속
- 검색 엔진 봇은 JavaScript 실행이 제한적
- 단순히 글을 읽는데도 수백 KB 다운로드
Astro의 접근 방식
Astro는 빌드 타임에 모든 것을 HTML로 만들어 둡니다.
// 브라우저가 해야 할 일
1. HTML 다운로드 (완전한 콘텐츠 포함)
2. 화면에 콘텐츠 표시 👈 즉시!
3. (선택적) 인터랙티브 컴포넌트만 JavaScript 로드
장점:
- 즉시 First Contentful Paint
- 검색 엔진이 완전한 HTML을 바로 읽음
- 필요한 경우에만 JavaScript 로드
개념 비교
| 측면 | 전통적인 SPA | Astro |
|---|---|---|
| 렌더링 시점 | 런타임 (브라우저) | 빌드 타임 (서버) |
| JavaScript 번들 | 항상 필요 | 선택적 |
| 초기 로딩 | 느림 (JS 파싱 필요) | 빠름 (HTML만) |
| SEO | 추가 설정 필요 | 기본적으로 최적 |
| 사용 사례 | 인터랙티브 앱 | 콘텐츠 중심 사이트 |
시작하기
설치 및 프로젝트 생성
# 새 프로젝트 생성
npm create astro@latest
# 프로젝트 디렉토리로 이동
cd my-astro-site
# 개발 서버 실행
npm run dev
대화형 설치 과정에서 다음을 선택할 수 있습니다:
- 템플릿 (빈 프로젝트, 블로그, 포트폴리오 등)
- TypeScript 사용 여부
- Git 초기화 여부
- 의존성 자동 설치 여부
프로젝트 구조
my-astro-site/
├── src/
│ ├── components/ # 재사용 가능한 컴포넌트
│ ├── layouts/ # 페이지 레이아웃
│ ├── pages/ # 페이지 (파일 기반 라우팅)
│ └── styles/ # CSS 파일
├── public/ # 정적 파일 (이미지, 폰트 등)
├── astro.config.mjs # Astro 설정
└── package.json
핵심 개념:
src/pages/: 이 폴더의 파일이 자동으로 라우트가 됩니다pages/index.astro→/pages/about.astro→/aboutpages/blog/post.astro→/blog/post
Astro 컴포넌트 작성하기
Astro 컴포넌트는 .astro 확장자를 사용하며, 세 부분으로 구성됩니다.
기본 구조
---
// 1. 컴포넌트 스크립트 (빌드 타임에만 실행)
const title = "Hello, Astro!";
const items = ['Apple', 'Banana', 'Cherry'];
---
<!-- 2. 컴포넌트 템플릿 (HTML + 표현식) -->
<div>
<h1>{title}</h1>
<ul>
{items.map(item => <li>{item}</li>)}
</ul>
</div>
<style>
/* 3. 스코프 스타일 (이 컴포넌트에만 적용) */
h1 {
color: blue;
}
</style>
중요한 점:
---사이의 코드는 빌드 타임에만 실행됩니다- 클라이언트 JavaScript가 아니므로
window,document같은 브라우저 API 사용 불가 - 데이터 페칭, 파일 읽기 등 서버 작업에 적합
데이터 페칭
---
// fetch는 빌드 타임에 실행됩니다
const response = await fetch('https://api.github.com/repos/withastro/astro');
const data = await response.json();
---
<div>
<h1>{data.name}</h1>
<p>⭐ {data.stargazers_count}</p>
<p>{data.description}</p>
</div>
어떻게 동작할까요?
- 빌드 시 API 호출
- 응답 데이터를 HTML에 삽입
- 완성된 HTML을 정적 파일로 저장
- 사용자가 페이지 방문 → 이미 완성된 HTML 즉시 표시
Props 전달하기
---
// src/components/Card.astro
interface Props {
title: string;
description: string;
url?: string;
}
const { title, description, url } = Astro.props;
---
<div class="card">
<h2>{title}</h2>
<p>{description}</p>
{url && <a href={url}>Learn more →</a>}
</div>
<style>
.card {
border: 1px solid #ddd;
padding: 1rem;
border-radius: 8px;
}
</style>
사용:
---
import Card from '../components/Card.astro';
---
<Card
title="Astro"
description="콘텐츠 중심 웹사이트를 위한 프레임워크"
url="https://astro.build"
/>
Client Directives - 인터랙티브 컴포넌트 만들기
Astro에서 JavaScript를 클라이언트로 보내려면 client directive를 사용합니다.
사용 가능한 Directives
1. client:load - 페이지 로드 즉시
<Counter client:load />
언제 사용?
- 즉시 보여야 하는 중요한 인터랙션 (메인 네비게이션 등)
2. client:visible - 화면에 보일 때
<Comments client:visible />
언제 사용?
- 페이지 하단의 댓글, 무한 스크롤, 게으른 로딩이 적절한 곳
어떻게 동작?
- Intersection Observer API 사용
- 컴포넌트가 뷰포트에 들어오면 JavaScript 로드
3. client:idle - 브라우저가 한가할 때
<Analytics client:idle />
언제 사용?
- 중요하지 않은 기능 (분석, 채팅 위젯 등)
어떻게 동작?
requestIdleCallbackAPI 사용- 메인 스레드가 한가할 때 로드
4. client:only - 서버에서 렌더링 안 함
<Chart client:only="react" />
언제 사용?
- 브라우저 전용 API를 사용하는 컴포넌트
- 서버 렌더링이 불가능한 라이브러리
❌ 흔한 실수
<!-- ❌ directive 없이 React 컴포넌트 사용 -->
<Counter />
<!-- 결과: HTML만 렌더링, 클릭해도 동작 안 함 -->
<!-- ✅ client directive와 함께 사용 -->
<Counter client:load />
<!-- 결과: JavaScript가 로드되어 정상 동작 -->
✅ 최적화 팁
<!-- 페이지 상단의 중요한 네비게이션 -->
<Navigation client:load />
<!-- 페이지 중간의 이미지 갤러리 -->
<ImageGallery client:visible />
<!-- 페이지 하단의 분석 스크립트 -->
<Analytics client:idle />
레이아웃 사용하기
레이아웃은 여러 페이지에서 공통으로 사용하는 구조를 정의합니다.
---
// src/layouts/BaseLayout.astro
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>{title}</title>
</head>
<body>
<header>
<nav>
<a href="/">Home</a>
<a href="/blog">Blog</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<slot /> <!-- 페이지 콘텐츠가 여기에 삽입됩니다 -->
</main>
<footer>
<p>© 2025 My Site</p>
</footer>
</body>
</html>
사용:
---
// src/pages/about.astro
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="About">
<h1>About Me</h1>
<p>안녕하세요! Astro로 웹사이트를 만들고 있습니다.</p>
</BaseLayout>
함정과 주의사항
콘텐츠 중심 사이트를 만들 때 Astro가 탁월하지만, 몇 가지 주의할 점이 있습니다.
1. 클라이언트 상태 관리의 복잡성
문제:
<!-- ❌ Islands끼리 상태를 공유하기 어려움 -->
<Counter client:load />
<Display client:load />
<!-- 두 컴포넌트가 같은 카운트를 공유하려면? -->
해결책:
// src/store/counter.ts
import { atom } from 'nanostores';
export const count = atom(0);
// React 컴포넌트에서
import { useStore } from '@nanostores/react';
import { count } from '../store/counter';
export default function Counter() {
const $count = useStore(count);
return (
<button onClick={() => count.set($count + 1)}>
Count: {$count}
</button>
);
}
언제 문제가 될까요?
- 여러 컴포넌트가 복잡한 상태를 공유해야 할 때
- 실시간 동기화가 필요한 인터랙티브 앱
대안:
- 복잡한 인터랙티브 앱이라면 Next.js나 Remix 고려
- Astro는 콘텐츠가 주, 인터랙션이 부가적인 경우에 적합
2. 빌드 타임 vs 런타임 혼동
문제:
---
// ❌ 이 코드는 브라우저에서 실행되지 않습니다
const now = new Date();
---
<p>현재 시간: {now.toLocaleString()}</p>
<!-- 결과: 빌드 시점의 시간이 표시됨, 페이지 새로고침해도 변하지 않음 -->
해결책:
<p id="current-time"></p>
<script>
// ✅ 이 코드는 브라우저에서 실행됩니다
function updateTime() {
const now = new Date();
document.getElementById('current-time').textContent =
`현재 시간: ${now.toLocaleString()}`;
}
updateTime();
setInterval(updateTime, 1000);
</script>
3. 대용량 사이트 빌드 시간
문제:
- 수천 개의 페이지를 가진 사이트는 빌드가 오래 걸릴 수 있음
- 예: 10,000개 페이지 → 10분 이상 빌드 시간
해결책:
// astro.config.mjs
export default defineConfig({
// 증분 빌드 활성화
experimental: {
contentCollections: true,
},
// 병렬 빌드 최적화
vite: {
build: {
rollupOptions: {
output: {
manualChunks: (id) => {
if (id.includes('node_modules')) {
return 'vendor';
}
}
}
}
}
}
});
언제 문제가 될까요?
- 뉴스 사이트, 대규모 전자상거래
- 매일 수백 개 콘텐츠가 추가되는 경우
대안:
- SSR 모드 활용
- Astro의 하이브리드 렌더링 (일부 페이지만 정적 생성)
Astro vs 다른 프레임워크
Astro vs Next.js
| 측면 | Astro | Next.js |
|---|---|---|
| 렌더링 기본값 | 정적 (SSG) | 하이브리드 (SSG + SSR) |
| JavaScript 기본값 | 0 KB | React 번들 포함 |
| 사용 사례 | 콘텐츠 중심 | 풀스택 앱 |
| 러닝 커브 | 낮음 | 중간 |
| 서버 기능 | 제한적 | 풍부함 (API Routes, Middleware) |
언제 Astro?
- 블로그, 문서 사이트, 마케팅 페이지
- 성능이 최우선
- 인터랙션이 제한적
언제 Next.js?
- 인증, 데이터베이스 연동 등 서버 기능 필요
- 복잡한 클라이언트 상태 관리
- 실시간 데이터 업데이트
Astro vs Gatsby
| 측면 | Astro | Gatsby |
|---|---|---|
| 빌드 속도 | 매우 빠름 | 느림 (대규모 사이트) |
| GraphQL | 선택사항 | 필수 |
| 플러그인 생태계 | 성장 중 | 성숙함 |
| JavaScript 번들 | 선택적 | React 포함 |
Gatsby에서 Astro로 마이그레이션 이유:
- 빌드 시간 단축 (10분 → 1분)
- 복잡한 GraphQL 쿼리 제거
- 번들 크기 감소
마이그레이션 고려사항
기존 사이트를 Astro로 전환하려면 다음을 확인하세요.
Next.js에서 Astro로
적합한 경우:
- 대부분의 페이지가 정적 콘텐츠
- API Routes를 사용하지 않음
getStaticProps위주의 데이터 페칭
마이그레이션 체크리스트:
✓ 페이지를 .astro 파일로 변환
✓ getStaticProps 로직을 컴포넌트 스크립트로 이동
✓ 동적 라우팅 구조 유지 (파일 기반 라우팅 동일)
✓ 인터랙티브 컴포넌트에 client:* directive 추가
✓ API Routes는 별도 백엔드나 Netlify Functions로 대체
React/Vue SPA에서 Astro로
적합한 경우:
- SEO가 중요하지만 SSR 설정이 복잡
- 대부분의 콘텐츠가 정적
- 클라이언트 상태가 복잡하지 않음
마이그레이션 전략:
1. 정적 페이지부터 시작
- About, Contact 등 단순 페이지를 .astro로 변환
2. 동적 컴포넌트는 기존 프레임워크 유지
- React 컴포넌트를 그대로 가져와서 client:load 추가
3. 점진적으로 .astro 컴포넌트로 전환
- 불필요한 JavaScript 제거
실무 활용 사례
1. 기술 블로그 (Astro 공식 블로그)
문제:
- Gatsby로 구축했지만 빌드가 5분 이상 소요
- 단순히 글을 보여주는데 React 번들이 과도
해결:
- Astro로 마이그레이션
- 빌드 시간: 5분 → 30초
- JavaScript 번들: 200KB → 0KB (인터랙티브 검색만 10KB)
2. 마케팅 랜딩 페이지
요구사항:
- Lighthouse 점수 95+ 필수
- 인터랙티브 요소: 네비게이션, 폼 검증, 애니메이션
구현:
<!-- 대부분 정적 HTML -->
<Hero />
<Features />
<Pricing />
<!-- 인터랙티브 폼만 JavaScript -->
<ContactForm client:visible />
결과:
- Lighthouse Performance: 100
- FCP: 0.5초
- TTI: 1.2초
3. 전자상거래 상품 페이지
전략:
- 상품 설명, 이미지 갤러리: 정적 HTML
- 장바구니 추가, 리뷰: 인터랙티브 컴포넌트
---
const product = await fetchProduct(Astro.params.id);
---
<!-- 정적 SEO 콘텐츠 -->
<ProductInfo {...product} />
<ImageGallery images={product.images} />
<!-- 인터랙티브 기능 -->
<AddToCart productId={product.id} client:visible />
<Reviews productId={product.id} client:idle />
다음 단계
Astro의 기초를 배웠다면, 다음을 탐색해보세요:
- Content Collections - 타입 안전한 콘텐츠 관리
- SSR 모드 - 서버 사이드 렌더링으로 동적 데이터 처리
- View Transitions API - 페이지 전환 애니메이션
- Integrations - Tailwind, MDX, Partytown 등
참고 자료
핵심 정리:
- Astro는 콘텐츠 중심 웹사이트에 최적화된 프레임워크입니다
- 기본적으로 0 JS, 필요한 곳에만 선택적으로 추가
- Islands Architecture로 성능과 인터랙티브함을 모두 확보
- 블로그, 문서, 마케팅 페이지, 포트폴리오에 탁월한 선택
댓글