Astro

백엔드 개발자가 블로그에 Astro를 선택한 이유

Angular 경험이 있는 백엔드 개발자가 개인 기술 블로그에 Astro를 선택한 기준과 정적 HTML, Content Collections, 아일랜드 구조를 설명한다.

이 글의 목차프레임워크보다 블로그가 해야 할 일을 먼저 적었다9
  1. 프레임워크보다 블로그가 해야 할 일을 먼저 적었다
  2. Angular로도 가능하지만 출발점이 달랐다
  3. NestJS로 만들면 익숙하지만 운영할 것도 늘어난다
  4. Content Collections가 백엔드 개발 방식과 잘 맞았다
  5. HTML을 기본으로 만들고 동작만 따로 붙였다
  6. 처음 확인해야 했던 경계도 있었다
  7. Astro가 SEO와 수익화를 자동으로 해결해 주지는 않는다
  8. 다른 프로젝트에서도 Astro를 고를 것인가
  9. 참고 자료

나는 백엔드 개발자이다.
직접 써 본 프론트엔드 프레임워크는 Angular가 전부였다.

그래서 개인 블로그를 만들기로 했을 때 선택지는 자연스럽게 세 갈래였다.
익숙한 Angular로 화면을 만들거나, NestJS에 템플릿 엔진을 붙이거나, 블로그에 맞는 도구를 새로 배우는 정도였다.

처음에는 잘 아는 기술로 시작하는 편이 빠를 것 같았다.
하지만 프레임워크 목록을 비교하기 전에 블로그에서 방문자가 실제로 할 일을 적어 보니 생각이 달라졌다.

이곳의 중심은 로그인이나 복잡한 입력 폼이 아니었다.
글을 읽고, 필요한 내용을 검색하고, 코드를 복사하고, 가끔 테마나 메뉴를 바꾸는 정도였다.

애플리케이션을 만드는 데 익숙한 도구와 글을 전달하는 데 알맞은 도구는 같지 않을 수 있었다. 그 차이 때문에 이 블로그는 Angular도 NestJS도 아닌 Astro로 시작했다.

프레임워크보다 블로그가 해야 할 일을 먼저 적었다

블로그에 필요하다고 생각한 조건은 다음과 같았다.

  • Markdown 파일로 글을 쓰고 Git으로 변경 이력을 남길 수 있어야 했다.
  • JavaScript가 실행되지 않아도 제목과 본문을 읽을 수 있어야 했다.
  • 제목, 설명, 날짜와 태그가 빠지면 배포 전에 발견할 수 있어야 했다.
  • 검색엔진이 읽을 HTML, 대표 주소, 사이트맵과 RSS를 관리하기 쉬워야 했다.
  • 검색·테마·모바일 메뉴처럼 필요한 동작만 브라우저에서 실행하고 싶었다.
  • 글을 공개할 때마다 별도의 애플리케이션 서버를 운영하고 싶지는 않았다.

여기서 정적 HTML은 방문자가 페이지를 열 때마다 서버가 본문을 새로 조립하는 대신, 배포 전에 완성해 둔 HTML 파일을 뜻한다.
책을 주문받을 때마다 인쇄하는 것이 아니라, 미리 인쇄해 둔 페이지를 바로 건네는 방식에 가깝다.

글이 바뀌면 다시 빌드하면 된다.
반대로 방문자마다 내용이 달라지거나 실시간 정보가 필요하다면 정적 HTML만으로는 부족할 수 있다.
이 블로그는 대부분의 방문자에게 같은 글을 보여 주기 때문에 이 제약이 문제가 되지 않았다.

웹 페이지를 만드는 방식은 크게 세 가지로 나눠 볼 수 있다.

방식HTML을 만드는 시점잘 맞는 예
SSG(정적 사이트 생성)배포 전에 한 번 만든다블로그 글, 문서, 제품 소개
SSR(서버 사이드 렌더링)방문 요청이 올 때 서버가 만든다사용자별 대시보드, 자주 바뀌는 목록
CSR(클라이언트 사이드 렌더링)브라우저의 JavaScript가 주요 화면 내용을 조립한다복잡한 관리자 화면, 편집 도구

이 구분은 서로 배타적인 제품 목록이 아니다.
한 사이트에서도 공개 글은 SSG로 만들고, 로그인 뒤 화면은 SSR로 응답하며, 편집기 안의 복잡한 상호작용은 CSR로 실행할 수 있다.
중요한 질문은 “어떤 프레임워크가 유명한가”가 아니라 “이 페이지의 HTML을 언제 만드는 편이 가장 단순한가”였다.

콘텐츠 전달이 중심이면 Astro, 복잡한 화면 상태는 Angular, 계정과 데이터 처리는 NestJS와 API로 이어지는 프로젝트 선택 기준
세 기술은 서로 대체 관계가 아니다. 도구의 익숙함보다 프로젝트에서 가장 많은 비중을 차지하는 일을 기준으로 중심 도구를 골랐다.

Angular로도 가능하지만 출발점이 달랐다

Angular로 정적 블로그를 만들 수 없는 것은 아니다.
Angular는 서버 렌더링과 빌드 시 미리 HTML을 만드는 프리렌더링을 지원하고, 완전한 정적 출력도 설정할 수 있다.
공식 문서에서도 CSR·SSR·SSG를 함께 다루는 렌더링 방식을 안내한다.

그런데 내가 사용해 온 Angular 애플리케이션의 출발점은 브라우저에서 실행되는 클라이언트 렌더링이었다.
블로그에 맞추려면 정적 출력을 선택하고, 어떤 경로를 미리 만들지 결정하며, 브라우저에서 실행할 코드의 범위를 다시 줄여야 한다.

Astro는 반대 방향에서 시작했다.
Astro 컴포넌트는 기본적으로 빌드 시점이나 요청 시점에 HTML로 렌더링되고, 별도로 지시하지 않은 컴포넌트의 JavaScript 런타임은 브라우저로 보내지 않는다.

둘 중 하나가 무조건 더 좋은 구조라는 뜻은 아니다.
관리자 화면처럼 입력과 상태가 많고 여러 컴포넌트가 계속 데이터를 주고받는다면 Angular의 라우터, 폼, 의존성 주입과 일관된 애플리케이션 구조가 도움이 된다.
이 블로그는 먼저 글을 전달하고 일부 동작만 나중에 붙이는 쪽이 자연스러웠다.

Angular를 제외한 이유는 무겁다는 한 문장으로 정리되지 않는다.
Angular도 정적 사이트를 만들 수 있지만, 이 프로젝트에서는 Astro의 기본값이 내가 만들려는 결과에 더 가까웠다.

NestJS로 만들면 익숙하지만 운영할 것도 늘어난다

백엔드 개발자라서 NestJS로 블로그 API와 관리자 화면부터 만드는 선택도 떠올릴 수 있었다.
글을 데이터베이스에 저장하고, 인증을 붙이고, 작성·수정 API를 만들면 익숙한 구조 안에서 모든 것을 통제할 수 있다.

하지만 지금 필요한 것은 여러 작성자가 사용하는 콘텐츠 관리 서비스가 아니다.
내가 Markdown 파일을 수정하고 Git에 푸시하는 흐름이면 충분했다.

쓰기 화면을 직접 만드는 순간 인증, 권한, 입력 검증, 데이터베이스 마이그레이션, 백업과 보안 업데이트도 함께 관리해야 한다.
요청마다 글을 읽어 주는 서버를 두면 프로세스 상태와 장애 대응 범위도 늘어난다.

NestJS를 사용하지 않은 것은 백엔드가 필요 없다는 뜻이 아니다.
사용자 계정, 비공개 글, 댓글, 결제나 실시간 데이터가 생기면 API와 데이터 저장소가 필요할 수 있다.
다만 현재 단계에서 NestJS를 선택하는 것은 블로그의 문제보다 내가 익숙한 도구의 문제를 먼저 해결하는 일이었다.

예를 들어 방문자가 공개 글을 읽는 데는 서버 세션이 필요하지 않다.
반면 유료 구독자가 비공개 글을 보고, 여러 작성자가 초안을 승인하고, 댓글 신고 상태를 관리한다면 사용자별 권한과 변경 이력이 생긴다.
그 시점에는 Astro를 버리는 대신 인증과 데이터가 필요한 경로에 API를 연결하거나, 관리 영역을 별도 애플리케이션으로 분리할 수 있다.

Content Collections가 백엔드 개발 방식과 잘 맞았다

Astro를 선택하고 가장 익숙하게 느껴진 부분은 화려한 화면 기능이 아니라 콘텐츠 검증이었다.

글은 src/content/blog에 Markdown으로 저장한다.
src/content.config.ts에서는 모든 글이 가져야 할 제목, 설명, 날짜, 태그와 공개 상태를 스키마로 정의한다.

src/content.config.tsTypeScript단순화 예시
const blog = defineCollection({
  loader: glob({
    base: './src/content/blog',
    pattern: '**/*.{md,mdx}',
  }),
  schema: z.object({
    title: z.string().trim().min(1),
    description: z.string().trim().min(1),
    publishedAt: z.coerce.date(),
    tags: z.array(z.string().trim().min(1)).min(1),
    draft: z.boolean().default(false),
  }),
});
Markdown 글의 제목과 날짜 및 태그를 스키마로 검사해 통과하면 HTML을 만들고 오류가 있으면 빌드를 중단하는 과정
잘못된 글을 운영 중에 발견하는 대신, 배포 전에 빌드를 멈춰 수정할 수 있게 한다.

제목이 없거나 날짜 형식이 잘못되면 운영 화면에서 뒤늦게 발견하는 대신 빌드가 실패한다.
NestJS의 DTO 검증과 같은 기능은 아니지만, 경계에서 데이터 형태를 먼저 확인하고 잘못된 입력을 조용히 통과시키지 않는 방향은 익숙했다.

Astro의 Content Collections는 로컬 Markdown뿐 아니라 다른 데이터 원본도 같은 콘텐츠 API로 다룰 수 있다.
현재는 데이터베이스나 CMS를 연결하지 않고 빌드 시 컬렉션만 사용한다.
글처럼 변경 빈도가 낮은 콘텐츠를 미리 읽어 HTML로 만드는 현재 구조에 맞기 때문이다.

HTML을 기본으로 만들고 동작만 따로 붙였다

현재 설정은 글 목록과 본문을 배포 전에 정적 HTML로 만든다.

astro.config.mjsJavaScript단순화 예시
export default defineConfig({
  output: 'static',
  build: {
    format: 'directory',
  },
});

검색은 빌드 결과를 Pagefind가 색인한다.
테마 전환, 모바일 메뉴, 상단 검색과 코드 복사는 각 기능에 필요한 작은 브라우저 스크립트로 동작한다.
페이지 전체를 Angular 애플리케이션으로 실행하지 않아도 되는 기능들이었다.

Markdown 글을 정적 HTML로 만들고 검색·테마·메뉴에만 필요한 JavaScript를 붙이는 Astro 블로그 구조
본문은 미리 만든 HTML로 전달하고, 검색이나 메뉴처럼 상호작용이 필요한 부분만 사용 시점에 실행한다.

하이드레이션은 서버나 빌드가 만든 HTML에 브라우저 JavaScript를 연결해 버튼과 입력 같은 동작을 살리는 과정이다.
Astro에서는 하이드레이션할 작은 UI 영역을 아일랜드라고 부른다.
대부분은 HTML로 두고, 상호작용이 필요한 작은 영역만 독립적으로 실행하는 방식이다.
공식 아일랜드 아키텍처 설명처럼 UI 프레임워크가 필요한 기능이 생기면 그 조각에만 클라이언트 실행을 지정할 수 있다.

현재 블로그의 package.json에는 React가 없고 Angular 런타임도 없다.
프레임워크를 싫어해서 제외한 것이 아니라, 검색·테마·메뉴 사이에 복잡한 공유 상태가 없어서 설치할 이유가 아직 없었다.

나중에 여러 입력값과 비동기 결과가 얽힌 실습 도구를 추가한다면 판단은 달라질 수 있다.
그때는 해당 도구만 별도 UI 컴포넌트로 만들거나, 규모가 더 커지면 애플리케이션 영역을 분리하는 편이 낫다.

처음 확인해야 했던 경계도 있었다

Astro가 백엔드 개발자에게 무조건 쉬운 도구였던 것은 아니다.
.astro 파일 위쪽의 컴포넌트 스크립트는 주로 빌드 또는 서버에서 실행되고, <script> 안의 코드는 브라우저에서 실행된다.
같은 TypeScript와 JavaScript처럼 보여도 사용할 수 있는 객체와 실행 시점이 다르다.

Angular에서는 화면 상태를 컴포넌트와 서비스 안에서 관리하는 흐름에 익숙했다.
Astro에서는 먼저 이 값이 정말 브라우저 상태인지, 아니면 빌드할 때 HTML로 끝낼 수 있는 값인지 구분해야 했다.

기존 Angular 컴포넌트를 그대로 가져오는 상황에도 주의가 필요하다.
Astro는 React, Preact, Vue, Svelte, Solid와 Alpine을 공식 통합으로 제공하지만 Angular 연결은 커뮤니티 통합 영역이다.
지원하는 프론트엔드 프레임워크를 보면 기존 Angular 디자인 시스템을 대량으로 재사용해야 하는 프로젝트에서는 다른 판단이 나올 수 있다.

나에게는 옮겨야 할 Angular 컴포넌트 자산이 없었다.
필요한 것은 Angular 코드를 재사용하는 일이 아니라, TypeScript로 설정을 다루고 관심사를 나누며 빌드 실패를 통해 오류를 앞당기는 작업 방식이었다.

Astro가 SEO와 수익화를 자동으로 해결해 주지는 않는다

정적 HTML이 기본이라는 점은 검색엔진이 본문을 읽기 쉬운 출발점을 만든다.
그렇다고 Astro를 선택하는 것만으로 검색 순위가 오르거나 AdSense 승인이 보장되는 것은 아니다.

이 블로그에서는 별도로 대표 주소, 제목과 설명, Open Graph 정보와 구조화 데이터를 레이아웃에 넣었다.
공개 글은 사이트맵과 RSS에 포함하고, 내부 검색 페이지처럼 검색 결과에 노출할 필요가 없는 경로는 제외했다.
이미지 크기를 미리 지정하고 내부 링크를 빌드 후 검사하는 과정도 Astro라는 이름보다 실제 구현이 만든 결과다.

배포는 Git에 푸시한 소스를 Cloudflare가 빌드해 정적 결과물을 제공하는 방식으로 구성했다.
계속 실행되는 NestJS 프로세스를 관리하지 않아도 되고, 문제가 생기면 이전 커밋과 빌드 기록을 기준으로 되돌아갈 수 있다.

Astro는 이런 구성을 강제로 완성해 주는 도구가 아니다.
다만 콘텐츠, 정적 출력과 선택적 JavaScript가 기본 방향이라 빠뜨린 항목을 하나씩 확인하기 좋은 바탕이 됐다.

다른 프로젝트에서도 Astro를 고를 것인가

콘텐츠가 중심이고 방문자마다 달라지는 화면이 적다면 다시 Astro를 먼저 검토할 것 같다.
개인 블로그, 문서 사이트, 제품 소개처럼 미리 만들 수 있는 페이지가 대부분인 경우다.

반대로 로그인 뒤의 업무 화면, 복잡한 폼, 실시간으로 변하는 상태와 여러 화면의 공통 데이터가 중심이라면 Angular 같은 애플리케이션 프레임워크가 더 자연스러울 수 있다.
API와 권한, 데이터베이스가 제품의 핵심이라면 NestJS도 다시 중심에 놓인다.

지금 다시 선택한다면 다음 질문에 먼저 답한다.

  1. 페이지 대부분을 배포 전에 완성할 수 있는가.
  2. 방문자마다 다른 데이터가 필요한 경로는 몇 개인가.
  3. 브라우저에서 계속 유지해야 할 공유 상태가 있는가.
  4. 기존 Angular 컴포넌트를 대량으로 재사용해야 하는가.
  5. 작성자가 Git과 Markdown을 편하게 사용할 수 있는가, 아니면 CMS가 필요한가.

첫째와 다섯째가 맞고 나머지가 작다면 Astro의 장점이 크다.
사용자별 상태와 기존 애플리케이션 자산이 중심이라면 익숙한 Angular를 억지로 걷어낼 이유가 없다.
프레임워크 선택은 브랜드 선호가 아니라 앞으로 직접 운영할 복잡성의 위치를 정하는 일이다.

이번 선택은 Angular를 버리고 새로운 프레임워크로 갈아탄 이야기가 아니다.
애플리케이션을 만들 때 사용하던 기준으로 블로그가 실제로 필요한 범위를 다시 계산한 결과다.

글은 빌드할 때 HTML로 만들고, 필요한 동작만 브라우저에서 실행하며, 글의 형식은 배포 전에 검증한다.
그 범위에서는 페이지 전체를 실행하는 프레임워크보다 Astro가 더 작은 구조로 목적을 충족했다.

참고 자료

소신을 상징하는 고래 일러스트
작성자소신 · Soshin

직접 부딪힌 문제와 해결 과정을 다음에 다시 꺼내 볼 수 있도록 기록합니다.