babipanote·
#1인 빌더#buildlog#sprint 회고#AdSense#W22#fix-everything

Sprint 4주차 회고 — AdSense 신청 직전 1주, 13 PR을 머지하며

자가점검에서 발견한 5 FAIL을 시작점으로 1주 동안 13 PR을 머지하고 AdSense 신청 직전 상태로 사이트를 정리한 W22 회고. P0 보안·인프라 5건, P1 콘텐츠 구조 2건, UX·E-E-A-T 3건. 자동으로 가능한 것의 한계와 사용자 수동 4건의 무게.

읽는 시간 9

지난 일요일 AdSense 가이드 글을 쓰고 자가점검했더니 5 FAIL을 발견한 회고를 적었다. 그 글의 마지막 줄에서 "다음 한 주는 fix-everything 1주"라고 약속했다. 오늘이 그 약속을 닫는 날이다. 1주 동안 13개 PR을 머지했고, 사이트는 이제 AdSense 신청 직전 상태에 있다. 이 글은 그 13 PR에서 무엇이 가능했고 무엇이 끝까지 사용자 수동으로 남았는지에 대한 짧은 회고다.

시작점 — 자가점검 5 FAIL + 11 WARN + 광고 인프라 0건

지난주 회고를 다시 펼쳐 보면 발견된 문제는 크게 셋이었다. broken link·image 5건 (apple-shortcuts·claude-4-sonnet·perplexity·obsidian 2편), AdSense 인프라 0건 (ads.txt 404 + 광고 스크립트 미노출), 사이트 구조 약점 다수 (Topic Cluster Pillar 백링크 누락·중복 URL·고아 글·FAQ 형식 오류). 거절 사유 7가지 중 4~5가지가 동시에 걸리는 상태였다.

해야 할 일을 우선순위 3단계로 묶었다. P0 자동 가능한 항목 11개, P1 콘텐츠 구조 4건, P1 UX·E-E-A-T 5건. 총 20+ 항목. 이걸 1주에 끝내려면 가장 큰 함정은 "PR 하나에 너무 많이 묶는 것"이라는 걸 알고 있었다. 그래서 단일 책임 PR 원칙을 처음부터 끝까지 지키기로 했다.

13 PR 1주 타임라인 — P0 5 + P1 콘텐츠 2 + P1 UX 3 + hot fix

W22 1주 13 PR — 각 PR이 단일 책임을 지키도록 분리한 결과

P0 자동 처리 — 보안·인프라·SEO schema (5 PR)

처음 사흘은 사람 결정 없이 끝낼 수 있는 항목부터 정리했다. Next.js를 16.2.3에서 16.2.6으로 올리면서 HIGH CVE 7건과 MODERATE 4건이 한 번에 해소됐다. pnpm-lock.yaml 재생성 과정에서 packages/blog-core의 peer 의존성이 16.2.3 스냅샷에 묶여 있어 한 번 막혔는데, devDependency로 명시 추가해서 풀었다. favicon.ico·manifest.ts 양쪽 앱에 추가, 영문 카테고리 매핑 누락 해소, FAQ + BreadcrumbList JSON-LD 보강까지 5 PR.

이 중 가장 미세한 사고는 FAQ JSON-LD가 처음에 라이브에 안 떴던 것이다. 정규식 \b 워드 경계가 한국어 글자 뒤에서 매칭에 실패했고, JS에 없는 \Z anchor를 그대로 썼다. Python 정규식이면 동작했을 패턴이었다. JavaScript 환경에서는 (?:^|\n)(?=\n## |$) 같은 명시적 lookahead만 안전하다는 걸 다시 확인했다.

P1 콘텐츠 구조 — AI 코딩 Pillar 신규 + 카테고리 통합 (2 PR)

가장 큰 변화는 새 Pillar 한 편을 작성한 것이다. AI 코딩 cluster에 보조 글 4편(claude-code-flutter·claude-mcp-guide·obsidian-claude-code-mcp·claude-code-vs-cursor)이 쌓여 있는데 Pillar가 없는 상태였다. 신규 Pillar ai-coding-complete-guide-2026을 9,326자로 작성하고 matplotlib 차트 5장을 자동 생성해 발행했다. 이제 4 cluster + 1 Pillar 구조가 완성됐다.

같은 PR 묶음에서 카테고리 통합도 끝냈다. 글 1편짜리 카테고리 5개(LLM·AI 검색·코딩 도구·생산성·LLM)를 3개 상위 카테고리로 흡수하고, next.config.ts에 301 redirect 4건을 박았다. categories.ts는 한글 10개 카테고리에서 6개로 정리됐고, 영문 alias 2개는 EN 글 매칭을 위해 유지했다. 한 달 전 디버깅 회고에서 다룬 매핑 문제가 이번엔 사전 차단 모드로 들어왔다.

P1 UX·E-E-A-T — 404·About·image lazy (3 PR + 1 hot fix)

가장 시간이 걸린 건 마지막 단계였다. 404 페이지를 양쪽 앱에 신규 작성하고, GA4 광고 시그널 옵션을 명시하고, About 페이지에 운영자 경력·리뷰 신뢰도 단락 2개를 추가했다. MDX 본문 이미지에 loading="lazy" + decoding="async" 자동 주입을 blog-core 컴포넌트 매핑 한 줄로 끝냈다. 5월 신작 3편의 백링크 0건도 같이 정리해 고아 글 0편 상태에 도달했다.

처리 중 한 번 사고가 났다. PR #41이 ESLint react/no-unescaped-entities 룰로 verify CI에서 차단됐다. About 페이지의 인용부호 "..." 4건이 JSX 안에서 escape되지 않은 것. 한국어 코드 작업 중 "를 그대로 쓰던 습관이 lint에 잡힌 순간이다. “...”로 escape하는 lint fix commit을 추가 push해서 머지를 풀었다. 같은 commit이 머지될 때 GitHub squash 과정에서 conflict markers가 main에 그대로 들어갈 뻔한 더 큰 사고도 있었는데, git checkout --theirs + rebase로 정상 fix 적용 상태를 확인했다.

자동으로 가능한 것의 한계 — 사용자 수동 4건만 남았다

1주 동안 자동 가능한 항목은 거의 다 처리됐지만, 끝까지 Claude Code 단독으로는 불가능한 항목이 4건 남았다.

  1. Vercel Settings에서 NEXT_PUBLIC_ADSENSE_ID 환경변수 설정 — AdSense 콘솔에서 게시자 ID 발급받은 후 직접 추가
  2. AdSense 콘솔에서 사이트 추가 + 신청 클릭
  3. publisher ID 수령 후 ads.txt placeholder 한 줄 교체 + push
  4. contact@aigrit.dev 메일 inbox 라우팅 (Cloudflare Email Routing 등 — 선택)

이 4건의 공통점은 모두 외부 서비스 콘솔과 시크릿 입력이 필요하다는 점이다. AdSense·Vercel 둘 다 사용자 인증이 필요한 영역이고, CLI나 코드에서 직접 건드릴 수 있는 부분이 아니다. 자동화의 경계는 결국 "외부 서비스의 사용자 계정 안쪽"에서 멈춘다.

이번 sprint를 통해 자동화 가능한 것과 그렇지 않은 것의 경계가 더 분명해졌다. 사이트 구조·콘텐츠·코드·SEO 메타·라이브 검증까지는 코드 작업으로 끝낼 수 있다. 그러나 게시자 ID·결제 정보·계정 인증이 끼는 순간 사람의 시간이 필요해진다. 이 4건이 5분짜리 작업이지만, 그 5분이 1주 자동 작업과 동등한 무게를 갖는다.

마무리 — 신청 버튼 직전

오늘 저녁 또는 내일 아침에 AdSense 신청 버튼을 누른다. 거절되면 사유에 맞게 추가 보강을 한다. 1차 통과되면 W23은 신규 글 발행 + SNS 자산 생성 + 영문 번역 우선순위 글로 채운다. 3주차 회고에서 "발행 속도가 빨라진 만큼 점검 빈도가 따라잡지 못했다"고 적었던 게 지금 풀린 셈이다. 점검 1주를 통째로 비우는 것이 발행 속도를 다시 정직하게 만든다.

이 글이 끝나면 13 PR이 라이브에 모두 안착하고 GSC URL Inspection으로 정책 페이지 색인 상태만 한 번 확인한 뒤 AdSense 콘솔로 들어간다. 다음 회고는 신청 결과를 받은 뒤에 적는다.

공식 출처는 Google AdSense 정책·게시자 정책.

관련 글