핵심요약은 무엇인가?
- 99개 블로그 포스트에 GEO(Generative Engine Optimization) 최적화를 일괄 적용했다.
- Cloudflare에서 learning-stack.com 도메인을 구매하고, Vercel에 연결했다.
- Google Analytics 4, AdSense, 네이버 서치 어드바이저까지 하루, 3개 세션으로 전부 세팅했다.
사이트를 만드는 데 2일이 걸렸다면, 운영 기반을 다지는 데도 하루가 필요하다. 이 글은 포트폴리오 2일 완성기의 후속편이다.
배포 후에 뭘 더 해야 하나?
1탄에서 9개 세션으로 사이트를 완성하고 Vercel에 배포했다. 끝인 줄 알았는데 아니었다. 배포는 시작일 뿐이었다.
해야 할 게 쌓여 있었다:
- 커스텀 도메인이 없으면
*.vercel.app주소로 돌아다님 - 검색엔진에 등록 안 하면 아무도 못 찾음
- 방문자 분석이 없으면 누가 오는지 모름
- 기존 블로그 99개 글이 AI 검색 시대에 맞지 않는 구조
이걸 Claude Code와의 3개 세션으로 하루 만에 끝냈다. 아래는 그 과정이다.
3개 세션에서 실제로 무엇을 했나?
Day 3 (3/13)
세션 10: GEO 최적화 전략 수립 + 전체 구현
이 세션이 가장 규모가 컸다. 2026년 AI 검색 시대에 맞춰 블로그 전체를 리뉴얼하는 작업이었다.
왜 GEO인가? Google AI Overview, 네이버 AI 브리핑 같은 생성형 검색이 기본이 되면서, 기존 SEO만으로는 부족해졌다. AI가 내 콘텐츠를 "인용"하게 만드는 것 — 그게 GEO다.
먼저 Claude Code에게 전략 문서를 주고 계획을 세웠다. 9단계 구현 계획이 나왔고, 핵심은 3가지였다:
- Answer-First 구조 — 첫 100자 안에 결론을 넣는다
- 질문형 H2 — "## MCP 서버는 어떻게 만들까?" 같은 형태
- E-E-A-T 1인칭 경험 — "내가 직접 써보니" 같은 경험 기반 서술
계획 수립 후 바로 구현에 들어갔다. 코드 변경부터 99개 포스트 마이그레이션까지 전부 이 세션에서 처리했다.
인프라 구축 (Step 1~7):
blog.ts에tldr,contentType,relatedPosts,lastUpdated필드 추가- 검증 스크립트에 GEO 규칙 추가
- 마이그레이션 스크립트 작성 (
--dry-run지원) /write-blog스킬을 v2.0으로 전면 리라이팅- 블로그 상세 페이지에 JSON-LD 구조화 데이터, TL;DR 박스, 관련 글 섹션 추가
99개 포스트 마이그레이션 (Step 8~9):
수작업은 불가능했다. 3단계로 나눠서 자동화했다:
| 단계 | 방법 | 대상 |
|---|---|---|
| Stage 1 | 마이그레이션 스크립트 | lastUpdated, contentType, relatedPosts 자동 적용 |
| Stage 2 | 에이전트 5개 병렬 실행 | tldr 생성 + contentType 검증 |
| Stage 3 | 에이전트 6개 병렬 실행 | 본문 H2 질문형 변환, Answer-first 도입부, 내부 링크 삽입 |
결과: 104개 파일 변경, +1,814줄 추가, -463줄 삭제. 내가 한 건 5개 글을 골라 톤이 변하지 않았는지 확인하는 것뿐이었다.
TIP: 대규모 마이그레이션은 반드시
--dry-run부터 돌려보자. 99개 파일을 한 번에 바꾸고 롤백하는 건 악몽이다.
커스텀 도메인 구매 + 연결:
같은 세션에서 도메인도 처리했다. Claude Code에게 "도메인 어디서 사는 게 좋아?"라고 물었더니 Cloudflare와 Vercel을 비교해줬다.
| 비교 항목 | Cloudflare | Vercel |
|---|---|---|
| 가격 | 원가 판매 (마진 없음) | 약간 비쌈 |
| DDoS 방어 | 무료 포함 | 기본만 |
| 이메일 라우팅 | 무료 제공 | 없음 |
Cloudflare를 선택한 결정적 이유는 이메일 라우팅이었다. contact@learning-stack.com으로 오는 메일을 Gmail로 자동 전달하는 기능이 무료다.
Vercel 연결은 간단했다. Cloudflare DNS에 A 레코드를 추가하되, **Proxy OFF(DNS only)**로 설정해야 한다. Proxy를 켜면 Vercel SSL 인증서 발급이 실패한다. Claude Code가 이걸 바로 짚어줬다.
AI 봇 차단 — 일부러 안 함:
GEO 최적화를 하면서 AI 크롤러를 차단하면 모순이다. Cloudflare의 AI Labyrinth, Block AI bots, Bot fight mode를 전부 OFF로 설정했다. 이 판단은 내가 했다.
검색엔진 등록:
- Google Search Console — 도메인 속성 추가 + sitemap.xml 제출
- 네이버 서치 어드바이저 — HTML 메타 태그로 소유 확인 (
layout.tsx에 삽입) - Google AdSense — 사이트 등록 +
ads.txt확인
sitemap.xml에서 버그도 하나 잡았다. 블로그 slug에 & 같은 특수문자가 포함되면 XML 파싱 에러가 난다. URL 인코딩 처리로 해결.
이메일 라우팅 + 사이드바 수정:
Cloudflare Email Routing으로 contact@learning-stack.com → sdoublesouls@gmail.com 자동 전달을 설정하고, 사이드바 이메일도 새 주소로 변경했다.
여기서 배운 점: 한 세션에서 너무 많은 걸 하면 맥락 관리가 어렵다. GEO 최적화, 도메인, 검색엔진 등록, 이메일까지 한 세션에 몰아넣었는데, 세션을 나눴으면 더 깔끔했을 것이다.
세션 11: Google AdSense + GA4 설정
이 세션은 분석 도구를 붙이는 작업이었다.
AdSense 확인:
Google AdSense에 learning-stack.com을 등록했더니 "준비 중" 상태였고, Ads.txt가 "찾을 수 없음"으로 나왔다. Claude Code에게 물었더니 public/ads.txt를 확인해봤고 — 이미 올바른 내용으로 존재했다. AdSense가 크롤링을 아직 안 한 것뿐이었다.
GA4 설정 — 전 과정:
- Google Analytics에서 속성 생성 → 데이터 스트림(웹) → 측정 ID 발급
- Claude Code에게 측정 ID를 주자 바로
layout.tsx의<head>에 gtag.js 코드를 삽입 - 빌드 확인 → 성공
여기서 중요한 논의가 있었다. "내 방문을 제외하려면 어떻게 하지?"라는 질문이었다.
처음에는 IP 기반 필터링을 생각했다. 그런데 Claude Code가 짚어줬다:
IP 주소는 개인정보보호법상 개인정보에 해당한다. 수집·저장하면 고지 의무가 생긴다.
대안으로 4가지를 제시받았고, GA4 내부 트래픽 필터를 선택했다. IP를 직접 저장하지 않고도 내 트래픽을 제외할 수 있다.
GA4 → 관리 → 데이터 스트림 → 태그 설정 구성 → 내부 트래픽 정의에서 내 IP를 등록했다. 검색 유형은 CIDR 범위가 아니라 "IP 주소가 다음과 같음"으로 설정하는 게 정확하다.
Firestore vs GA4 역할 분담:
이미 Firestore로 방문자 수를 실시간 표시하고 있었다. GA4를 추가하면서 3가지 선택지가 나왔다:
- Firestore 추적 제거 → GA4로 완전 대체
- Firestore 리셋 → 새로 수집 시작
- 둘 다 유지 → 실시간은 Firestore, 분석은 GA4
3번을 선택했다. 역할이 다르기 때문이다:
| Firestore | GA4 | |
|---|---|---|
| 용도 | 사이드바 실시간 방문자 수 | 유입 경로, 체류시간, 기기 분석 |
| 식별 방식 | localStorage UUID (개인정보 이슈 없음) | 쿠키 기반 |
| 비용 | Firestore 읽기/쓰기 | 무료 |
Firestore의 site_visitors와 site_stats/counters를 리셋해서 0부터 새로 시작할지도 논의했다. 리셋하려면 두 컬렉션을 동시에 삭제해야 숫자가 안 맞는 일이 없다.
세션 12: 블로그 카드 조회수 위치 수정 + 커밋
마지막 세션은 CSS 버그 수정이었다.
블로그 목록에서 "조회 0" 뱃지가 카드마다 다른 높이에 표시되고 있었다. 스크린샷을 Claude Code에게 보여줬더니 바로 원인을 찾았다.
원인: .blog-card__views에 margin-left: auto가 있어서 메타 행 안에서 오른쪽으로 밀리는데, 카드 본문(blog-card__body) 너비가 콘텐츠(제목 길이, 태그 수)에 따라 달라지기 때문이었다.
수정: 3줄의 CSS 변경으로 해결했다:
.blog-card__link { position: relative; }
.blog-card__views { position: absolute; top: 20px; right: 0; }
.blog-card__link:hover .blog-card__views { right: 16px; }
hover 시 패딩이 바뀌는 것까지 보정. 빌드 확인 후 GA4 코드와 함께 커밋, 푸시했다.
TIP: CSS에서 "같은 위치에 있어야 하는데 제각각"인 문제는 대부분
position: absolute로 해결된다. 부모에relative, 자식에absolute— 이 패턴을 기억하자.
AI에게 무엇을 맡기고, 무엇을 직접 판단했나?
1탄에서도 같은 패턴이 반복됐다.
| 구분 | AI에게 맡긴 것 | 직접 판단한 것 |
|---|---|---|
| GEO 마이그레이션 | 99개 파일 자동 변환, 스크립트 작성 | 톤 변화 여부 검수 (5개 샘플링) |
| 도메인 | DNS 설정 가이드, 코드 변경 | Cloudflare vs Vercel 선택 |
| 분석 도구 | GA4 코드 삽입, 설정 가이드 | Firestore와 GA4 역할 분담, IP 수집 안 하기 |
| 봇 설정 | - | AI 봇 차단 전부 OFF |
| CSS 버그 | 원인 분석 + 수정 코드 | 조회수를 absolute로 고정할지 |
AI는 "어떻게(How)"에 강하고, 사람은 "무엇을(What)"과 "왜(Why)"에 강하다. Cloudflare를 선택한 이유, AI 봇을 일부러 안 막은 이유, IP를 수집하지 않기로 한 이유 — 이런 판단은 내가 했고, 실행은 Claude Code가 했다.
특히 이번에 느낀 건, AI에게 스크린샷을 보여주는 게 생각보다 강력하다는 것이다. 블로그 카드 CSS 버그는 말로 설명하면 복잡하지만, 스크린샷 한 장 보여주니까 3초 만에 원인을 짚었다.
배포 후 운영 체크리스트
포트폴리오 사이트 만들기를 참고해서 사이트를 만들었다면, 배포 후 이 순서로 진행하면 된다:
- 커스텀 도메인 구매 + DNS 연결
- Google Search Console 등록 + sitemap 제출
- 네이버 서치 어드바이저 등록
- Google Analytics 4 설치 + 내 IP 제외
- Google AdSense 등록 + ads.txt 배치
- GEO 최적화 (tldr, Answer-First, 질문형 H2)
- 이메일 라우팅 설정 (선택)
사이트를 만드는 건 시작이고, 검색엔진과 분석 도구를 붙이는 게 진짜 런칭이다.
이 글은 Claude Code와의 실제 대화 기록 3개 세션을 기반으로 작성되었습니다.
#포트폴리오 #ClaudeCode #GEO최적화 #GoogleAnalytics #도메인 #Cloudflare #SEO #바이브코딩
불러오는 중...