핵심요약은 무엇인가?

  • 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가지였다:

  1. Answer-First 구조 — 첫 100자 안에 결론을 넣는다
  2. 질문형 H2 — "## MCP 서버는 어떻게 만들까?" 같은 형태
  3. E-E-A-T 1인칭 경험 — "내가 직접 써보니" 같은 경험 기반 서술

계획 수립 후 바로 구현에 들어갔다. 코드 변경부터 99개 포스트 마이그레이션까지 전부 이 세션에서 처리했다.

인프라 구축 (Step 1~7):

  • blog.tstldr, 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.comsdoublesouls@gmail.com 자동 전달을 설정하고, 사이드바 이메일도 새 주소로 변경했다.

여기서 배운 점: 한 세션에서 너무 많은 걸 하면 맥락 관리가 어렵다. GEO 최적화, 도메인, 검색엔진 등록, 이메일까지 한 세션에 몰아넣었는데, 세션을 나눴으면 더 깔끔했을 것이다.

세션 11: Google AdSense + GA4 설정

이 세션은 분석 도구를 붙이는 작업이었다.

AdSense 확인:

Google AdSense에 learning-stack.com을 등록했더니 "준비 중" 상태였고, Ads.txt가 "찾을 수 없음"으로 나왔다. Claude Code에게 물었더니 public/ads.txt를 확인해봤고 — 이미 올바른 내용으로 존재했다. AdSense가 크롤링을 아직 안 한 것뿐이었다.

GA4 설정 — 전 과정:

  1. Google Analytics에서 속성 생성 → 데이터 스트림(웹) → 측정 ID 발급
  2. Claude Code에게 측정 ID를 주자 바로 layout.tsx<head>에 gtag.js 코드를 삽입
  3. 빌드 확인 → 성공

여기서 중요한 논의가 있었다. "내 방문을 제외하려면 어떻게 하지?"라는 질문이었다.

처음에는 IP 기반 필터링을 생각했다. 그런데 Claude Code가 짚어줬다:

IP 주소는 개인정보보호법상 개인정보에 해당한다. 수집·저장하면 고지 의무가 생긴다.

대안으로 4가지를 제시받았고, GA4 내부 트래픽 필터를 선택했다. IP를 직접 저장하지 않고도 내 트래픽을 제외할 수 있다.

GA4 → 관리 → 데이터 스트림 → 태그 설정 구성 → 내부 트래픽 정의에서 내 IP를 등록했다. 검색 유형은 CIDR 범위가 아니라 "IP 주소가 다음과 같음"으로 설정하는 게 정확하다.

Firestore vs GA4 역할 분담:

이미 Firestore로 방문자 수를 실시간 표시하고 있었다. GA4를 추가하면서 3가지 선택지가 나왔다:

  1. Firestore 추적 제거 → GA4로 완전 대체
  2. Firestore 리셋 → 새로 수집 시작
  3. 둘 다 유지 → 실시간은 Firestore, 분석은 GA4

3번을 선택했다. 역할이 다르기 때문이다:

Firestore GA4
용도 사이드바 실시간 방문자 수 유입 경로, 체류시간, 기기 분석
식별 방식 localStorage UUID (개인정보 이슈 없음) 쿠키 기반
비용 Firestore 읽기/쓰기 무료

Firestore의 site_visitorssite_stats/counters를 리셋해서 0부터 새로 시작할지도 논의했다. 리셋하려면 두 컬렉션을 동시에 삭제해야 숫자가 안 맞는 일이 없다.

세션 12: 블로그 카드 조회수 위치 수정 + 커밋

마지막 세션은 CSS 버그 수정이었다.

블로그 목록에서 "조회 0" 뱃지가 카드마다 다른 높이에 표시되고 있었다. 스크린샷을 Claude Code에게 보여줬더니 바로 원인을 찾았다.

원인: .blog-card__viewsmargin-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 #바이브코딩

불러오는 중...