Astro + React + Tailwind로 나만의 기술 블로그 구축하기
첫 블로그의 뼈대를 잡고 핵심 개념을 학습한 과정을 복기해 본다.
1. 프로젝트 초기화 및 환경 세팅
- 빈 프로젝트 생성:
npm create astro@latest명령어를 사용하여 템플릿 없는 빈 폴더(Empty) 상태에서 시작했다. - 스택 추가:
npx astro add tailwind react명령어로 평소 다루기 편한 React와 Tailwind CSS 스택을 추가했다. - 🚨 트러블슈팅 (버전 충돌): 최신 Tailwind v4와 Astro 플러그인(v3) 간의 피어 의존성(peer dependency) 충돌 에러가 발생했다.
npm install @astrojs/tailwind tailwindcss@3명령어로 호환되는 버전을 명시하고 강제 설치하는 방식으로 해결했다.
💡 Astro 파일의 3단 구조 이해하기
Astro 파일(.astro)은 크게 3가지 영역으로 이루어져 있다.
---
// 1. 컴포넌트 스크립트 (코드 펜스)
import Header from '../components/Header.tsx'; // 다른 컴포넌트 가져오기
const { title } = Astro.props; // Props 받기
const currentDate = new Date().getFullYear();
---
<html>
<head>
<title>{title}</title> </head>
<body>
<Header /> <h1>{title} 페이지에 오신 걸 환영합니다!</h1>
<slot /> <footer>
Copyright {currentDate} Um Noni.
</footer>
</body>
</html>
<style>
/* 3. 스타일 (이 컴포넌트의 h1에만 적용됨) */
h1 {
color: #3b82f6; /* Tailwind를 안 써도 여기서 직접 스타일링 가능 */
}
</style>
1. 컴포넌트 스크립트 (코드 펜스)
파일의 가장 윗부분에 위치하며, 대시 세 개(---)로 감싸여 있다. 리액트로 비유하면 JSX 코드 작성 전 import와 변수 선언을 수행하는 곳이다.
- 서버 전용: 이 구역의 코드는 빌드 시점(또는 SSR 시)에만 실행된다.
- 데이터 준비: API 호출, 데이터베이스 쿼리, 다른 컴포넌트 임포트, 변수 선언 등을 수행한다.
- 브라우저 차단: 여기서 작성한 자바스크립트는 브라우저로 전송되지 않으므로 보안이 필요한 로직을 작성하기 좋다.
2. 컴포넌트 템플릿 코드 펜스 아래에 위치하며, 페이지의 실제 HTML 구조를 결정한다.
- JSX 스타일: 리액트의 JSX와 매우 비슷하게 중괄호
{}를 사용하여 자바스크립트 변수를 HTML 안에 직접 렌더링할 수 있다. - Zero JS: 특별한 설정이 없다면 이 영역은 순수 HTML로 변환되어 사용자에게 전달된다.
3. 스타일 및 스크립트 (선택 사항)
템플릿 아래에 <style>이나 <script> 태그를 추가할 수 있다.
- 범위 지정 스타일:
<style>태그 안의 CSS는 기본적으로 해당 컴포넌트에만 고립되어 적용(Scoped)된다. - 클라이언트 스크립트:
<script>태그를 사용하면 브라우저에서 실행될 클라이언트 사이드 자바스크립트를 작성할 수 있다.
2. 공통 뼈대 만들기 (Layout.astro)
- 위치:
src/layouts/Layout.astro - 역할: 모든 페이지에 공통으로 적용되는 최상위 레이아웃이다.
<head>태그에 SEO 메타데이터를 넣고,<body>에 Tailwind를 활용해 전체 배경색과 기본 텍스트 색상을 설정했다. - 핵심 기능:
<slot />태그를 사용하여 React의children처럼 각 페이지의 개별 콘텐츠가 렌더링될 영역을 뚫어두었다.
블로그의 엔트리 포인트(진입점)는 index.astro이다. 초기에 index.astro에 있던 HTML 코드를 Layout.astro로 옮기고, index.astro에서는 이 Layout을 임포트하여 <slot /> 영역에 첫 인삿말을 띄우는 방식으로 구조를 개선했다.
3. React 컴포넌트 연동하기 (Header.tsx)
Astro 컴포넌트만 사용해도 재사용이 가능하고 Tailwind CSS도 원활하게 적용된다. 하지만 추후 useState, useEffect 등 리액트의 훅(Hook)을 활용한 정밀한 상태 제어(예: 다크모드 토글)가 필요할 때를 대비하여 Header는 React 컴포넌트로 만들었다.
- 위치:
src/components/Header.tsx - 역할: 상단 네비게이션 바를 익숙한 React와 TypeScript 문법(
import type { ReactElement } from 'react';)으로 작성했다. - 적용: 작성한 컴포넌트를
Layout.astro에서 가져와<Header />형태로 렌더링했다. - 💡 아일랜드 아키텍처(Islands Architecture): React로 컴포넌트를 작성했음에도 내부에 동적인 상태가 없다면, 빌드 시 브라우저로 JS를 전송하지 않고 순수 HTML/CSS로만 초고속 렌더링됨을 확인했다.
4. 콘텐츠 컬렉션(Content Collections) 세팅
콘텐츠 컬렉션은 Astro에서 마크다운(.md)이나 MDX(.mdx) 같은 로컬 콘텐츠 파일을 체계적으로 관리하기 위해 제공하는 핵심 기능이다. 쉽게 말해 흩어져 있는 텍스트 파일들을 하나의 ‘정돈된 데이터베이스 테이블’처럼 취급할 수 있게 해주는 도구다.
이 기능은 다음과 같은 흐름으로 작동한다.
- 폴더 구조:
src/content/[컬렉션이름]/구조로 마크다운 파일을 모아둔다. (src/content/blog/) - 설정:
src/content/config.ts에서zod를 활용하여 각 컬렉션의 데이터 스키마를 엄격하게 정의한다. 이를 통해 프론트매터(Frontmatter)의 제목 누락이나 날짜 오타 등의 휴먼 에러를 빌드 단계에서 미리 방지할 수 있다. - 조회: 페이지 파일에서
getCollection('컬렉션이름')함수를 사용해 데이터를 불러온다.
💡 정렬 팁:
pubDate: z.date()로 설정했을 때 하루에 여러 개의 포스팅을 작성한다면,2026-02-28T09:00:00형태로 시간을 명시하거나 파일명 앞에01-,02-처럼 숫자를 붙여 정렬 순서를 명확히 제어할 수 있다.
5. 블로그 페이지 및 동적 라우팅 구현
Astro는 파일 기반 라우팅(File-based Routing) 방식을 사용한다. src/pages 폴더 안에 파일을 만들거나 폴더를 구성하면 Astro가 이를 감지하여 자동으로 URL 주소를 생성한다.
src/pages/index.astro→domain.com/src/pages/blog/index.astro→domain.com/blog
1. 목록 페이지 (src/pages/blog/index.astro)
getCollection('blog') 함수를 이용해 작성된 모든 마크다운 글을 불러오고, 타임스탬프(valueOf())를 기준으로 최신순 정렬하여 카드 리스트 형태로 렌더링했다.
2. 상세 페이지 (src/pages/blog/[slug].astro)
- 동적 라우팅: 파일명에 대괄호
[]를 사용하여 동적 URL을 처리했다. 즉, “/blog/뒤에 오는 어떤 주소값이든 이 파일 하나가 다 처리하겠다”는 선언이다. 사용자가/blog/first-post로 접속하면, Astro는slug변수에first-post를 담아 화면을 보여준다. - 정적 경로 생성:
getStaticPaths()함수를 통해 빌드 시점에 생성할 페이지 경로들을 미리 지정했다. 이 함수는 Astro가 빌드 타임에 자동으로 호출하므로 코드상에 별도의 호출부는 없다. - 본문 렌더링:
render()함수를 호출하여 마크다운 본문을 HTML(<Content />)로 변환한다.
💡 동적 라우팅의 동작 원리 (‘붕어빵’ 비유)
동적 페이지가 각각의 HTML 파일로 만들어지는 과정은 붕어빵을 굽는 것과 같다.
[slug].astro는 “붕어빵 틀”이다: 실제 내용이 담긴 페이지가 아니라, 코드 펜스 안의 로직과 HTML 구조를 통해 “어떻게 보여줄 것인가”를 정의한 공통 템플릿이다.getStaticPaths()는 “주문서 목록”이다: Astro는 빌드 시점에 이 함수를 호출하여 “몇 개의 붕어빵을 구워야 하고, 각 붕어빵에 어떤 속재료(데이터)를 넣을지” 명단을 확인한다. (params: { slug }로 경로를 결정하고,props: { entry }로 마크다운 데이터를 전달한다.)- HTML 생성 과정: Astro는 빌드 시점에 이 배열을 순회한다.
- 배열 요소 추출: 예)
slug: 'first-post',entry: { 데이터 } - 데이터 주입:
[slug].astro틀에 해당 데이터를 주입한다. - 파일 생성: 완성된 결과물을
dist/blog/first-post/index.html이라는 독립된 정적 파일로 저장하여 렌더링 속도를 극대화한다.
- 배열 요소 추출: 예)
6. 마크다운 스타일링 (Typography 플러그인)
- 변환된 날것의 HTML 텍스트를 깔끔하게 스타일링하기 위해
@tailwindcss/typography플러그인을 설치했다. tailwind.config.mjs에 플러그인을 등록한 후, 상세 페이지의 본문 영역에prose prose-lg클래스를 적용하여 타이포그래피와 간격이 자동으로 정돈되도록 구성했다.
기본 제공되는 스타일 외에도, prose 클래스를 덮어씌워 인라인 코드(백틱) 나 코드 블록의 스타일을 취향에 맞게 커스터마이징하는 것도 가능하다.
1) 인라인 코드(백틱) 스타일 커스터마이징
tailwind.config.mjs 파일에서 theme.extend.typography 부분을 수정하면 인라인 코드의 디자인을 전역적으로 변경할 수 있다. 특히 기본적으로 붙는 양옆의 백틱 기호를 제거하고 배경색을 주어 가독성을 높일 수 있다.
// tailwind.config.mjs 예시
export default {
theme: {
extend: {
typography: {
DEFAULT: {
css: {
// 1. 작은 백틱(인라인 코드) 스타일 변경
'code::before': { content: '""' }, // 기본적으로 붙는 앞쪽 백틱 제거
'code::after': { content: '""' }, // 뒤쪽 백틱 제거
'code': {
backgroundColor: '#f3f4f6', // gray-100
padding: '2px 4px',
borderRadius: '4px',
color: '#3b82f6', // blue-600
fontWeight: '300',
},
// 2. 코드 스니펫(블록) 스타일 변경
'pre': {
backgroundColor: '#1f2937', // gray-800
color: '#e5e7eb',
borderRadius: '8px',
border: '1px solid #374151',
},
},
},
},
},
},
plugins: [require('@tailwindcss/typography')],
};
2) 코드 블록 스타일 및 테마 변경 (Shiki)
단순한 테두리 색상뿐만 아니라, 코드 내부의 키워드 색상(문법 강조)까지 프로그래밍 언어별로 최적화하고 싶다면 Astro의 기본 문법 강조 도구인 Shiki를 활용하면 된다. astro.config.mjs 파일에서 Shiki 옵션을 직접 설정하여 원하는 테마를 입힐 수 있다.
// astro.config.mjs 예시
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
shikiConfig: {
theme: 'github-dark', // 원하는 테마로 변경 가능
langs: ['js', 'ts', 'jsx', 'tsx', 'css', 'html'], // 주로 사용하는 언어 지정
},
},
});