2026년 5월 2일 · 개발
Astro로 애드센스 블로그 만들기
Astro, Tailwind CSS, MDX, Cloudflare Workers를 이용해 애드센스 블로그를 만들고 chailmon.com 도메인에 배포한 전체 과정을 정리합니다.
들어가며
이번 글은 chailmon.com 블로그를 새로 만들면서 실제로 진행한 전체 과정을 정리한 기록이다.
목표는 단순했다.
- Google AdSense와 호환이 좋은 블로그를 만들기
- 개발자인 내가 디자인을 쉽고 예쁘게 적용할 수 있게 만들기
- 기존에 사용하던
chailmon.com도메인을 그대로 사용하기 - Oracle Cloud 서버에 직접 호스팅하던 방식에서 벗어나 더 단순하게 운영하기
- 글을 계속 쌓기 쉬운 구조로 만들기
처음에는 여러 선택지를 비교했다.
대표적으로 고민한 조합은 두 가지였다.
Astro + Tailwind CSS + MDX + Cloudflare
Next.js + Supabase + Vercel
결론적으로는 Astro + Tailwind CSS + MDX + Cloudflare Workers Static Assets 조합을 선택했다.
최종 구조는 다음과 같다.
Astro
+ Tailwind CSS
+ MDX
+ Cloudflare Workers Static Assets
+ Google AdSense
+ chailmon.com
왜 Astro를 선택했나
Next.js + Supabase + Vercel 조합은 좋은 선택지다.
하지만 이 조합은 단순 블로그보다는 로그인, 데이터베이스, 사용자별 저장 기능, 결제, 대시보드 같은 기능이 들어가는 웹서비스에 더 적합하다.
반면 내가 만들고 싶은 것은 먼저 검색 유입과 애드센스를 목표로 하는 정보성 블로그였다.
그래서 처음부터 데이터베이스와 로그인 기능을 붙이는 것보다, 정적 사이트로 빠르고 단순하게 운영할 수 있는 Astro가 더 잘 맞았다.
정리하면 이렇게 볼 수 있다.
Astro 조합
→ 콘텐츠 중심 블로그에 적합
Next.js + Supabase 조합
→ 서비스형 웹앱에 적합
현재 목표가 애드센스 블로그라면 Astro가 더 현실적인 선택이었다.
전체 진행 순서
이번 블로그 구축은 아래 순서로 진행했다.
1. 개발환경 확인
2. Astro 프로젝트 생성
3. 로컬 개발 서버 실행
4. Tailwind CSS 적용
5. 기본 폴더 구조 정리
6. MDX 적용
7. 블로그 글 구조 만들기
8. 메인 페이지 만들기
9. 글 목록 페이지 만들기
10. 글 상세 페이지 만들기
11. 공통 레이아웃, Header, Footer 분리
12. SEO 설정
13. sitemap.xml, robots.txt 설정
14. About, Contact, Privacy Policy 페이지 만들기
15. AdSense 적용
16. GitHub 저장소 생성 및 푸시
17. Cloudflare Workers Static Assets 배포
18. chailmon.com 도메인 연결
19. www → non-www 리다이렉트 설정
20. 배포 후 점검
21. AdSense 운영 체크리스트 확인
22. 글 작성 템플릿 만들기
단계별로 체크하면서 진행하니 중간에 누락되는 작업을 줄일 수 있었다.
1. 개발환경 확인
먼저 Node.js, npm, Git 버전을 확인했다.
node -v
npm -v
git --version
내 환경은 다음과 같았다.
Node.js v25.9.0
npm 11.12.1
git 2.54.0
Astro를 사용하기에 충분한 버전이었다.
2. Astro 프로젝트 생성
Astro 프로젝트는 아래 명령어로 생성했다.
npm create astro@latest
프로젝트 이름은 chailmon-blog로 정했다.
처음에는 복잡한 템플릿보다 기본 minimal starter로 시작했다.
블로그는 처음부터 너무 많은 기능을 넣기보다, 작게 시작해서 필요한 기능을 하나씩 붙이는 편이 관리하기 쉽다.
3. 로컬 개발 서버 실행
프로젝트 생성 후 로컬 서버를 실행했다.
npm run dev
Astro의 기본 개발 서버 주소는 보통 다음과 같다.
http://localhost:4321
브라우저에서 기본 Astro 화면이 뜨는 것을 확인한 뒤 다음 단계로 넘어갔다.
4. Tailwind CSS 적용
디자인을 빠르게 적용하기 위해 Tailwind CSS를 붙였다.
npx astro add tailwind
Tailwind를 적용하면 HTML에 클래스를 직접 붙여 빠르게 디자인을 잡을 수 있다.
블로그처럼 카드, 버튼, 레이아웃, 간격 조정이 많은 사이트에서는 Tailwind가 편하다.
5. 기본 폴더 구조 정리
블로그를 운영하기 편하도록 폴더 구조를 정리했다.
src/
components/
content/
blog/
layouts/
pages/
styles/
public/
images/
각 폴더의 역할은 다음과 같다.
src/components
→ Header, Footer 같은 재사용 컴포넌트
src/layouts
→ 전체 페이지 공통 레이아웃
src/content/blog
→ MDX 블로그 글
src/pages
→ 실제 URL 페이지
src/styles
→ 전역 CSS
public
→ 이미지, robots.txt, ads.txt 같은 정적 파일
6. MDX 적용
블로그 글은 MDX로 작성하기로 했다.
npx astro add mdx
MDX를 사용하면 Markdown처럼 글을 쓰면서도 HTML이나 컴포넌트를 함께 사용할 수 있다.
예를 들어 글 안에서 이런 식으로 Tailwind 클래스가 들어간 HTML도 사용할 수 있다.
<div class="rounded-2xl bg-gray-100 p-6">
Tailwind CSS 클래스도 사용할 수 있습니다.
</div>
7. Content Collection 설정
Astro에서 블로그 글을 안정적으로 관리하기 위해 Content Collection을 설정했다.
Astro 6.2.1 기준으로는 loader: glob(...) 방식을 사용했다.
import { defineCollection } from "astro:content";
import { z } from "astro/zod";
import { glob } from "astro/loaders";
const blog = defineCollection({
loader: glob({
pattern: "**/*.mdx",
base: "./src/content/blog",
}),
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
category: z.string(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
cover: z.string().optional(),
}),
});
export const collections = {
blog,
};
처음에는 예전 방식인 type: "content"로 설정했다가 컬렉션이 인식되지 않는 문제가 있었다.
에러 메시지는 다음과 같았다.
The collection "blog" does not exist or is empty.
Astro 6.2.1에서는 glob loader 방식으로 수정하니 문제가 해결됐다.
8. 메인 페이지 만들기
메인 페이지에는 다음 요소를 넣었다.
상단 메뉴
히어로 영역
최근 글 3개
카테고리 카드
푸터
메인 페이지는 블로그의 첫인상을 결정하기 때문에 너무 복잡하게 만들지 않고, 어떤 블로그인지 바로 알 수 있게 구성했다.
핵심 문구는 다음과 같이 잡았다.
개발자가 직접 키우는 수익형 블로그
9. 글 목록 페이지 만들기
글 목록 페이지는 /blog 경로로 만들었다.
https://chailmon.com/blog
getCollection("blog")를 사용해 src/content/blog 안의 MDX 글을 가져오고, 날짜 기준으로 최신 글이 먼저 보이게 정렬했다.
글 목록에서는 다음 정보를 보여주도록 했다.
작성일
카테고리
제목
설명
태그
글 읽기 링크
10. 글 상세 페이지 만들기
개별 글 상세 페이지는 동적 라우트로 만들었다.
파일 구조는 다음과 같다.
src/pages/blog/[id].astro
Astro 정적 사이트에서 동적 페이지를 만들기 위해 getStaticPaths()를 사용했다.
export async function getStaticPaths() {
const posts = await getCollection("blog", ({ data }) => {
return data.draft !== true;
});
return posts.map((post) => ({
params: {
id: post.id,
},
props: {
post,
},
}));
}
이렇게 하면 MDX 파일명이 URL로 사용된다.
예를 들어 파일명이 다음과 같다면:
astro-adsense-blog-build-guide.mdx
URL은 다음처럼 된다.
/blog/astro-adsense-blog-build-guide
11. 공통 레이아웃 분리
처음에는 각 페이지마다 Header와 Footer 코드가 반복되어 있었다.
중복을 줄이기 위해 아래 파일로 분리했다.
src/components/Header.astro
src/components/Footer.astro
src/layouts/BaseLayout.astro
BaseLayout.astro에서는 공통 HTML 구조, SEO 태그, Header, Footer를 처리했다.
이렇게 해두면 새로운 페이지를 만들 때마다 같은 코드를 반복해서 복사하지 않아도 된다.
12. SEO 설정
astro.config.mjs에는 최종 도메인을 설정했다.
export default defineConfig({
site: "https://chailmon.com",
});
그리고 BaseLayout.astro에는 다음 SEO 태그를 넣었다.
title
description
canonical
Open Graph
Twitter card
robots
generator
대표 도메인은 www 없는 주소로 정했다.
https://chailmon.com
그래서 canonical도 항상 https://chailmon.com 기준으로 생성되도록 했다.
13. sitemap.xml과 robots.txt 설정
검색엔진이 사이트를 잘 크롤링할 수 있도록 sitemap과 robots.txt를 설정했다.
npx astro add sitemap
public/robots.txt는 다음처럼 만들었다.
User-agent: *
Allow: /
Sitemap: https://chailmon.com/sitemap-index.xml
배포 후 확인 주소는 다음과 같다.
https://chailmon.com/robots.txt
https://chailmon.com/sitemap-index.xml
14. 기본 페이지 만들기
AdSense와 사이트 신뢰도를 위해 기본 페이지도 만들었다.
/about
/contact
/privacy-policy
특히 privacy-policy는 중요하다.
애드센스 블로그라면 Google AdSense, 쿠키, 제3자 광고 사업자, 광고 식별자 관련 내용을 개인정보처리방침에 포함하는 것이 좋다.
15. AdSense 적용
이미 승인된 AdSense 계정이 있어서 실제 Publisher ID를 기준으로 적용했다.
.env에는 아래와 같은 값을 넣었다.
PUBLIC_ADSENSE_CLIENT=ca-pub-xxxxxxxxxxxxxxxx
그리고 AdSenseHead.astro를 만들어 모든 페이지의 <head>에 AdSense 스크립트가 들어가도록 했다.
---
const adsenseClient = import.meta.env.PUBLIC_ADSENSE_CLIENT;
const adsenseSrc = adsenseClient
? `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adsenseClient}`
: null;
---
{adsenseSrc && (
<>
<meta name="google-adsense-account" content={adsenseClient} />
<script async src={adsenseSrc} crossorigin="anonymous"></script>
</>
)}
초기에는 수동 광고 단위도 고려했지만, 지금은 자동 광고만 사용하는 것으로 정했다.
초기 블로그는 글 수가 적기 때문에 수동 광고 위치를 직접 잡기보다, 먼저 자동 광고로 운영하면서 콘텐츠를 쌓는 편이 낫다고 판단했다.
16. ads.txt 설정
public/ads.txt 파일을 만들었다.
형식은 다음과 같다.
google.com, pub-xxxxxxxxxxxxxxxx, DIRECT, f08c47fec0942fa0
중요한 점은 AdSense 코드와 ads.txt의 ID 표기가 다르다는 것이다.
AdSense script:
ca-pub-xxxxxxxxxxxxxxxx
ads.txt:
pub-xxxxxxxxxxxxxxxx
ads.txt에서는 ca-를 빼고 pub-... 형식으로 넣는다.
배포 후에는 아래 주소에서 확인했다.
https://chailmon.com/ads.txt
17. GitHub에 푸시
로컬 프로젝트를 GitHub 저장소에 올렸다.
git add .
git commit -m "Initial Astro blog setup"
git push -u origin main
AdSense ID가 들어간 .env 파일은 GitHub에 올라가지 않도록 .gitignore에 추가했다.
.env
.env.local
18. Cloudflare Workers Static Assets 배포
처음에는 Cloudflare Pages 방식으로 배포하려고 했지만, 실제 Cloudflare UI에서는 Workers 기반 배포 흐름으로 진행되었다.
그래서 최종적으로는 Cloudflare Workers Static Assets 방식으로 배포했다.
이를 위해 wrangler.jsonc를 만들었다.
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "chailmon-blog",
"compatibility_date": "2026-05-02",
"assets": {
"directory": "./dist",
"not_found_handling": "404-page"
}
}
Cloudflare 설정은 다음과 같았다.
Build command:
npm run build
Deploy command:
npx wrangler deploy
배포 후 처음 생성된 주소는 다음과 같았다.
https://chailmon-blog.chailmon.workers.dev
19. chailmon.com 도메인 연결
기존에는 chailmon.com이 Oracle Cloud 서버로 연결되어 있었다.
Cloudflare에 chailmon.com 도메인을 추가한 뒤, Namecheap에서 네임서버를 Cloudflare 네임서버로 변경했다.
julissa.ns.cloudflare.com
tim.ns.cloudflare.com
Cloudflare에서 도메인이 Active 상태가 된 후, Worker의 Custom Domain으로 chailmon.com을 연결했다.
기존 Oracle Cloud IP로 향하던 A 레코드는 삭제했다.
A chailmon.com 129.153.175.22
A www 129.153.175.22
20. www 리다이렉트 설정
대표 주소는 https://chailmon.com으로 정했다.
그래서 www.chailmon.com은 chailmon.com으로 301 리다이렉트되도록 Cloudflare Redirect Rule을 만들었다.
조건은 다음과 같다.
(http.host eq "www.chailmon.com")
리다이렉트 대상은 다음과 같다.
concat("https://chailmon.com", http.request.uri.path)
query string도 유지하도록 설정했다.
최종 결과는 다음과 같다.
https://www.chailmon.com
→ https://chailmon.com
https://www.chailmon.com/blog
→ https://chailmon.com/blog
21. 배포 후 점검
최종 배포 후 아래 주소들을 확인했다.
https://chailmon.com
https://chailmon.com/blog
https://chailmon.com/blog/astro-adsense-blog-build-guide
https://chailmon.com/about
https://chailmon.com/contact
https://chailmon.com/privacy-policy
https://chailmon.com/robots.txt
https://chailmon.com/sitemap-index.xml
https://chailmon.com/ads.txt
canonical도 확인했다.
<link rel="canonical" href="https://chailmon.com/blog/astro-adsense-blog-build-guide/" />
www가 들어가지 않고 chailmon.com 기준으로 잡혀 있어서 정상이었다.
22. 글 작성 템플릿 만들기
앞으로 새 글을 쉽게 만들기 위해 템플릿 파일도 만들었다.
templates/blog-post-template.mdx
새 글을 만들 때는 이 템플릿을 복사해서 사용하면 된다.
cp templates/blog-post-template.mdx src/content/blog/new-post-name.mdx
그리고 공개할 글은 반드시 draft: false로 바꿔야 한다.
draft: false
draft: true이면 블로그 목록에 표시되지 않는다.
이번 구축에서 배운 점
이번 작업에서 가장 중요했던 점은 세 가지였다.
첫째, Astro 버전에 맞는 Content Collection 설정을 사용해야 한다.
Astro 6.2.1에서는 loader: glob(...) 방식이 맞았다.
둘째, Cloudflare 배포 방식은 UI 변화에 따라 달라질 수 있다.
예전에는 Cloudflare Pages 중심으로 설명되는 경우가 많지만, 실제로는 Workers Static Assets 방식으로 진행했다.
셋째, AdSense는 처음부터 수동 광고 단위를 무리하게 넣기보다 자동 광고만 먼저 적용하는 것이 낫다.
초기 블로그에서는 광고 최적화보다 사이트 안정성, 콘텐츠 품질, 검색 등록이 더 중요하다.
마무리
이번 과정을 통해 chailmon.com은 다음 구조의 블로그가 되었다.
Astro
+ Tailwind CSS
+ MDX
+ Cloudflare Workers Static Assets
+ Google AdSense
+ sitemap.xml
+ robots.txt
+ ads.txt
+ www → non-www redirect
이제 기술적인 구축은 끝났다.
앞으로 중요한 것은 글을 꾸준히 쌓는 것이다.
초기 목표는 고유한 글 20개를 작성하는 것이다.
이후 검색 유입이 생기면 방문자가 많이 들어오는 글을 기준으로 수동 광고 단위나 제휴 링크를 조금씩 테스트해볼 수 있다.