Astro Islands 아키텍처 완전 가이드 | 부분 Hydration으로 성능 극대화
이 글의 핵심
Astro의 혁신적인 Islands 아키텍처. 기본적으로 0 JavaScript를 전송하고, 필요한 컴포넌트만 선택적으로 Hydration합니다. React·Vue·Svelte를 한 페이지에서 동시에 사용할 수 있으며, Core Web Vitals를 크게 개선합니다.
Next.js로 블로그랑 랜딩만 돌릴 땐 괜찮았는데, 글이 늘어날수록 “왜 이 정적 본문까지 하이드레이트하지?” 싶은 순간이 왔어. page.tsx는 커지고, 번들은 무거워지고, Lighthouse는 애매하게 먹먹해지는 그 느낌. 그래서 한번은 정말 포기하듯 Next.js에서 Astro로 마이그레이션해봤다. 뼈는 그대로 두고(마크다운, 레이아웃 감각), 인터랙션이 필요한 곳만 남기는 식이었지. 옮기고 나서 첫인상은 딱 이랬다: 끝났다, Islands가 미래다. 이건 과장이 아니고, “문서는 문서답게, 앱은 앱답게”를 코드 레벨에서 강제하는 쪽이 이쪽이 훨씬 잘 맞는다.
Islands가 뭐냐고? 말 그대로 바닷가에 섬 몇 개 박혀 있는 그림이면 돼. 2019년쯤 Katie Sylor-Miller가 말하던 “부분 하이드레이션”이 그거고, Astro는 그걸 기본 제품으로 가져갔다. 옛날에 익숙한 SSR(Next, Nuxt 계열)은 서버가 HTML 뽑아주고, 클라이언트는 앱 전체 JS 받아서 앱 전체를 붙이잖아. 본문이 긴 블로그, 소개 글, 문서 사이트에서 이건 너무 아깝다. 반대로 Astro 쪽 머릿속 모델은 이래: 정적 콘텐츠는 그냥 HTML로 보내고 끝, 클릭·차트·댓글만 그 조각에만 JS를 얹는다. 그 “조각”이 섬이야. 나는 이게 프론트엔드 토론에서 제일 잘못 퍼진 착각—“SPA가 곧 최신”이라는 것—을 끊어버리는 쪽이라고 본다. 문서·마케팅·블로그의 기본은 정적이 맞다.
Astro 써보려면 npm create astro@latest로 띄우고, React 쓰고 싶으면 npx astro add react 정도면 된다. astro.config.mjs에 @astrojs/react 붙이는 건 익숙한 사람한테는 지루한 수준이라 생략해도 되고, 중요한 건 페이지는 .astro가 주인이고, 리액트는 필요한 섬으로만 온다는 점이야.
npm create astro@latest my-astro-project
cd my-astro-project
npx astro add react
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
integrations: [react()],
});
Islands 쓰는 감은 이래. 본문은 그냥 HTML이고, 카운터 하나만 client:load로 붙이는 식. 아래는 그 느낌 그대로.
---
import Layout from '../layouts/Layout.astro';
import Counter from '../components/Counter.tsx';
---
<Layout title="Islands Demo">
<h1>Welcome to Astro Islands</h1>
<p>This is static content with no JavaScript.</p>
<Counter client:load />
<p>More static content...</p>
</Layout>
// src/components/Counter.tsx
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
client:load는 “보이든 말든 일단 이 섬은 물을 올려라”, client:idle은 “한가할 때”, client:visible은 “뷰포트에 보일 때”, client:media는 “이 미디어 쿼리일 때만”, client:only는 “서버는 건너뛰고 클라이언트만” 같은 식이야. 나는 무조건 load만 박는 사람 제일 싫어한다. “전부 인터랙티브”가 목표가 아니라면, 위에서부터 visible / idle 써가면서 번들 죽이는 연습이 재밌어진다. 이건 미니멀리즘 욕이 아니라, 사용자한테 불필요한 JS를 안 싣는다는 최소한의 예의에 가깝다고 본다.
<Counter client:load />
<ChatWidget client:idle />
<LazyChart client:visible />
<MobileMenu client:media="(max-width: 768px)" />
<ThirdPartyWidget client:only="react" />
Next에서 넘어올 때 제일 맛있었던 건 React, Vue, Svelte를 한 페이지에 얹을 수 있다는 쪽이야. “한 스택에 맞춰라”는 압박이 사라지거든. npx astro add react vue svelte 해놓고, 섬마다 프레임워크만 갈아끼우면 됨. 실무에서는 그렇게까지 안 해도 되지만, 가능하다는 사실이 옛날 “번들러 하나, 프레임워크 하나” 강박을 깨준다.
---
import ReactCounter from '../components/ReactCounter.tsx';
import VueChart from '../components/VueChart.vue';
import SvelteCarousel from '../components/SvelteCarousel.svelte';
---
<Layout>
<h1>Multi-Framework Page</h1>
<ReactCounter client:load />
<VueChart client:visible />
<SvelteCarousel client:idle />
</Layout>
성능 이야기를 숫자로 박고 싶은 유혹이 있는데, 표로 정리하진 않을게(그건 AI가 잘 쓰잖아). 대신 느낌만 말하자. 비슷한 콘텐츠를 Next SSR로 돌릴 땐 JS가 한 덩이로 느껴지고, Astro로 쪼개면 “아 이 페이지는 8kb만 쐈다”는 체감이 온다. TTI·Lighthouse가 좋아지는 건 그 다음이고, 디버그할 때 “왜 이 스크립트가?”가 줄어드는 것이 더 크다. 마이그레이션한 블로그 템플릿으로 치면, 본문은 HTML로 펼치고, 목차·공유·댓글이 각각 visible / idle에 묶이게 만드는 식—그게 내가 쓰는 기본 골격이야.
---
import Layout from '../../layouts/Layout.astro';
import TableOfContents from '../../components/TableOfContents.tsx';
import CommentSection from '../../components/CommentSection.tsx';
import ShareButtons from '../../components/ShareButtons.tsx';
const { slug } = Astro.params;
const post = await getPost(slug);
---
<Layout title={post.title}>
<article>
<h1>{post.title}</h1>
<time>{post.date}</time>
<div set:html={post.content} />
</article>
<TableOfContents headings={post.headings} client:visible />
<ShareButtons title={post.title} url={post.url} client:idle />
<CommentSection postId={post.id} client:visible />
</Layout>
Next랑 Qwik이랑 한 줄씩 겹쳐 비교하자면 이렇게 느낀다. Next는 앱에 강하고 생태계는 말 안 해도 안다. Qwik은 resumable 쪽의 극단적인 답. Astro는 “대부분 정적 + 필요한 섬만”이 기본 뼈이고, 문서·콘텐츠·마케팅 사이트의 기본값으로는 내가 Next보다 이쪽에 표를 몰아준다. Qwik은 그 다음 관심사. 표로 안 적었지만, “초기 JS 덩어리” 감각만 놓고 보면 Islands 쪽이 덜 죄책감이 든다. 물론 대시보드, 실시간 협업, 앱 느낌이 본질이면 Next가 나을 수 있어. 나는 겹치는 영역이 있을 때 “우리가 진짜 SPA냐?”를 먼저 묻고, 아니라면 Islands 쪽이 이긴다고 본다.
끝으로 나는 이렇게 말하고 싶다. “전부 React로 통일”이 실패를 줄여주는 경우도 많지만, 콘텐츠가 중심인 제품에서는 그게 느리게 죽는 패턴이 될 수 있다. Astro Islands는 그걸 끊는 도구고, 내 돈·내 사이트를 걸면 나는 앞으로도 이 쪽이 더 끌린다. 공식 문서는 docs.astro.build에 있고, 막히면 그냥 Discord 가서 부딪혀. npm create astro@latest 한 번이면 5분 안에 끝난다—그다음은 섬을 어디에 박을지, 그게 재미다.