Astro 5.0으로 개발자 블로그 마이그레이션 경험기

들어가며

이번 글을 통해 기존 Next.js 기반 블로그를 Astro 5.0으로 마이그레이션한 경험을 공유하고자 합니다. 마이그레이션 과정에서 느낀 Astro의 장점과 핵심 기능들, 그리고 실제 구현 경험을 담았습니다. 블로그 플랫폼 선택을 고민하는 분들이나 Astro에 관심 있는 개발자들에게 도움이 되기를 바랍니다.

기술 선택의 기준: 과하지 않은 적절함

개발자로서 새로운 프로젝트를 시작할 때 가장 중요한 것은 목적에 맞는 기술을 선택하는 것입니다. 특히 개인 블로그처럼 명확한 요구사항이 있는 프로젝트에서는 더욱 그렇습니다.

이전 블로그: Next.js ISR의 문제점

ISR로 시작된 여정

블로그를 처음으로 직접 구축할 때는 실시간성이 중요하다고 생각했습니다. 글을 발행하면 즉시 반영되고, 수정사항도 바로 적용되는 것이 좋은 사용자 경험을 제공할 것이라 판단했죠. 그래서 Next.js의 ISR(Incremental Static Regeneration) 기능을 활용하기로 결정했습니다.

javascript
// 초기 Next.js ISR 구조
export async function getStaticProps() {
  const posts = await notion.databases.query({
    database_id: process.env.NOTION_DATABASE_ID,
  });

  return {
    props: { posts },
    revalidate: 60, // 1분마다 재생성
  };
}

ISR을 통해 정적 사이트의 성능과 동적 업데이트의 장점을 모두 가져갈 수 있을 것이라 생각했습니다. Notion을 CMS로 활용하면서 글 작성과 동시에 블로그에 반영되는 워크플로우를 구상했죠.

현실과 이상의 괴리

하지만 실제로 블로그를 운영해보니 예상과 달랐습니다. 글은 생각보다 자주 쓰지 않았고, 하루에 여러 개의 글이 올라오는 경우는 거의 없었습니다. 주간 또는 월간 단위로 콘텐츠가 업데이트되는 패턴이었죠.

이런 상황에서 실시간 업데이트를 위한 복잡한 인프라는 오히려 오버엔지니어링이었습니다. ISR의 revalidation 로직, 캐시 무효화 처리, 그리고 예상치 못한 빌드 실패 상황들을 관리하는 것이 단순한 블로그에는 과한 복잡성이었습니다.

더 나은 방향: SSG + 스케줄 기반 배포

경험을 통해 깨달은 것은 블로그에는 정적 생성이 더 적합하다는 점이었습니다. 콘텐츠의 업데이트 빈도를 고려했을 때, 빌드 타임에 모든 페이지를 미리 생성하는 SSG 방식이 더 안정적이고 성능도 우수했습니다.

대신 GitHub Actions를 활용해 스케줄 기반 자동 배포 파이프라인을 구축했습니다:

yaml
# .github/workflows/deploy.yml
name: Deploy Blog
on:
  schedule:
    - cron: '0 9 * * *'  # 매일 오전 9시
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Build and Deploy
        run: |
          npm run build
          npm run deploy

이 방식의 장점은 명확했습니다:

  • 안정성: 빌드 실패 시 기존 사이트 유지
  • 성능: 모든 페이지가 완전히 정적으로 생성
  • 예측 가능성: 정해진 시간에 업데이트 확인 가능
  • 단순함: 복잡한 캐시 로직 불필요

과한 기술 스택의 문제점

블로그라는 프로젝트의 본질을 다시 생각해보면, 핵심 요구사항은 다음과 같습니다:

  • 정적 콘텐츠 제공
  • 빠른 로딩 속도
  • SEO 최적화
  • 간단한 인터랙션 (검색, 댓글 등)

하지만 Next.js를 사용하면서 다음과 같은 오버엔지니어링을 하고 있었습니다:

  1. 불필요한 서버사이드 렌더링 — 대부분의 페이지가 정적 콘텐츠인데 굳이 SSR이 필요한가?
  2. 무거운 JavaScript 번들 — React 런타임과 Next.js 프레임워크 코드가 모든 페이지에 포함
  3. 복잡한 설정 — API 라우트, 미들웨어, 다양한 렌더링 전략 등 블로그에는 과한 기능들

특히 Lighthouse 성능 측정을 해보면, JavaScript 번들 크기로 인한 First Contentful Paint 지연이 눈에 띄었습니다.

Astro: 목적에 맞는 선택

이런 상황에서 Astro를 알게 되었습니다. Astro의 핵심 철학은 제가 느끼던 문제를 정확히 해결해주었습니다.

기본적으로 정적 HTML을 생성하고, 필요한 부분에만 JavaScript를 추가한다

Astro의 핵심 기능과 마이그레이션 경험

Islands Architecture: 필요한 곳에만 JavaScript

Astro의 가장 혁신적인 부분은 Islands Architecture입니다. 페이지 전체가 JavaScript로 동작하는 기존 SPA와 달리, Astro는 기본적으로 정적 HTML을 생성하고 필요한 부분에만 “섬(Island)” 형태로 JavaScript를 활성화합니다.

astro
---
// 서버에서만 실행되는 코드
const posts = await getCollection('blog');
---

<html>
  <body>
    <!-- 정적 HTML로 렌더링 -->
    <Header />
    <main>
      {posts.map(post => (
        <article>
          <h2>{post.data.title}</h2>
          <p>{post.data.description}</p>
        </article>
      ))}
    </main>

    <!-- 필요한 곳에만 JavaScript 활성화 -->
    <SearchBox client:load />
    <CommentSection client:visible />
  </body>
</html>

실제로 제 블로그에서는 검색 기능과 댓글 시스템에만 JavaScript를 사용하고, 나머지는 모두 정적 HTML로 처리합니다. 이를 통해 초기 로딩 속도가 40% 이상 개선되었고, Lighthouse 성능 점수도 크게 향상되었습니다.

Content Collections: 타입 안전한 콘텐츠 관리

기존 Next.js에서는 Notion API를 통해 콘텐츠를 가져왔지만, Astro에서는 Content Collections를 사용해 마크다운 기반으로 콘텐츠를 관리합니다.

typescript
// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string(),
    publishDate: z.date(),
    tags: z.array(z.string()),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };

이를 통해 컴파일 타임에 콘텐츠 검증이 가능해졌고, IDE에서 자동완성도 지원받을 수 있게 되었습니다. 마크다운 파일에 잘못된 메타데이터가 있으면 빌드 시점에 에러를 잡을 수 있어 운영 안정성도 크게 향상되었습니다.

성능 개선과 개발 경험

빌드 성능의 극적인 향상

마이그레이션 후 가장 체감되는 변화는 빌드 속도였습니다. Next.js에서 약 45초 걸리던 빌드가 Astro에서는 15초 내외로 단축되었습니다. 이는 Vite 기반의 빠른 번들링과 불필요한 JavaScript 처리 과정이 생략되었기 때문입니다.

특히 개발 서버 시작 시간도 5초에서 1초 내외로 줄어들어, 글을 쓰고 확인하는 워크플로우가 훨씬 쾌적해졌습니다.

런타임 성능 최적화

javascript
// Lighthouse 성능 측정 결과
// Before (Next.js): Performance 78, FCP 2.1s
// After (Astro):    Performance 96, FCP 0.8s

JavaScript 번들 크기가 90% 감소하면서 First Contentful Paint가 대폭 개선되었고, 모바일 환경에서의 사용자 경험도 크게 향상되었습니다.

추가 기능들의 활용

Server Islands와 동적 콘텐츠

Astro 5.0의 Server Islands 기능을 활용해 조회수나 최신 댓글 같은 동적 콘텐츠도 효율적으로 처리할 수 있게 되었습니다.

astro
---
// components/ViewCounter.astro
---
<div server:defer>
  <span>조회수: {await getViewCount(postId)}</span>
</div>

다양한 프레임워크 통합

필요에 따라 React, Vue, Svelte 컴포넌트를 함께 사용할 수 있어, 기존 컴포넌트 라이브러리를 그대로 활용할 수 있었습니다.

astro
---
import ReactSearch from './ReactSearch.jsx';
import VueComments from './VueComments.vue';
import SvelteChart from './SvelteChart.svelte';
---

<ReactSearch client:load />
<VueComments client:visible />
<SvelteChart client:idle />

선택의 근거

Astro를 선택한 결정적인 이유들:

  1. 적절한 복잡도 — 블로그에 필요한 기능만 제공
  2. 성능 우선 — 기본적으로 빠른 성능 보장
  3. 개발 생산성 — 마크다운 기반 콘텐츠 관리
  4. 확장성 — 필요시 React, Vue 등 추가 가능

무엇보다 “과하지 않다”는 점이 가장 매력적이었습니다. 블로그라는 목적에 딱 맞는 도구를 찾은 느낌이었죠.

마이그레이션 과정에서의 고려사항

물론 마이그레이션이 모든 면에서 완벽하지는 않았습니다. Astro 특유의 문법을 학습해야 했고, 일부 Next.js 전용 기능들은 다른 방식으로 구현해야 했습니다. 하지만 콘텐츠 중심의 블로그라는 목적에는 Astro가 훨씬 적합했습니다.

마치며

Astro는 복잡한 웹 애플리케이션보다는 콘텐츠가 중심이 되는 웹사이트에 최적화되어 있습니다. 블로그, 마케팅 사이트, 포트폴리오 등을 고민하고 있다면 Astro를 강력히 추천드립니다. 무엇보다 빠른 성능이 기본이라는 점이 가장 큰 매력인 것 같습니다.

개발자 입장에서도 Vite 기반의 빠른 개발 경험과 타입 안전한 콘텐츠 관리는 정말 만족스럽고, 방문자 입장에서도 빠른 로딩과 부드러운 전환 효과로 좋은 사용자 경험을 제공할 수 있었습니다.

앞으로도 Astro의 발전과 함께 더 나은 블로그 경험을 만들어 나가겠습니다.