콘텐츠로 이동

36. 사이트 런칭 풀스택 셋업 (도메인 + 이메일 + 검색엔진 + 측정 + AEO)

2026-05-28 storywinner.co.kr 정식 런칭 경험 기반. 다음에 다른 사이트(앱 서브도메인, 신규 프로젝트 등) 런칭 시 그대로 재사용.

0. 전제 조건 체크리스트

  • [ ] 사이트가 임시 도메인(예: *.pages.dev, *.vercel.app)에 이미 배포돼 작동 중
  • [ ] astro.config.mjssite 값이 최종 도메인으로 박혀 있나 (BaseLayout canonical/OG가 그걸 참조)
  • [ ] robots.txt, sitemap 자동 생성 설정 OK
  • [ ] OG 이미지 (페이지별 + fallback) 준비

→ 도메인 연결 전에 이걸 갖춰두면 connection 직후 모든 메타가 자동으로 올바른 도메인을 가리킴.


1. 도메인 연결 (Cloudflare 기준)

1-1. 사전 DNS 조사 (read-only, 안전)

$d = "도메인.co.kr"
Resolve-DnsName $d -Type NS    # 네임서버
Resolve-DnsName $d -Type A     # 웹 IP
Resolve-DnsName $d -Type MX    # 이메일 (없으면 도메인 메일 안 씀 = 안전)
Resolve-DnsName "www.$d" -Type CNAME
Resolve-DnsName $d -Type TXT   # GSC verification 등
- MX 없음 = 이메일 끊길 걱정 0 → 도메인 이전 안전 - 기존 TXT(google-site-verification 등)는 보존

1-2. Cloudflare에 도메인 추가

  1. CF 대시보드 → Account home+ AddConnect a domain (Transfer 말고!)
  2. 도메인 입력 → Free 플랜 선택 (대부분 사이트는 Free 충분)
  3. CF가 기존 DNS 스캔 → 그대로 두고 Continue (전파 중 사이트 끊김 방지)
  4. CF가 알려주는 네임서버 2개 기억 (예: xxx.ns.cloudflare.com)

1-3. 등록업체에서 네임서버 교체

  • 등록업체(아이티이지, 후이즈, 가비아 등) 패널 → 도메인 관리 → 네임서버 변경
  • 기존 NS 전부 삭제 → CF NS 2개만 입력
  • IP 칸은 비움 (외부 NS는 IP 불필요)

1-4. 전파 확인 (자동 폴링)

# Google DNS에 CF NS가 박혔는지 1분 간격 폴링
for ($i=0; $i -lt 60; $i++) {
  $ns = (Resolve-DnsName 도메인 -Type NS -Server 8.8.8.8).NameHost
  if ($ns -match 'cloudflare') { Write-Output "DEPLOYED"; break }
  Start-Sleep 60
}
- 보통 10~30분, 빠르면 3분, 길면 수시간 - CF가 알아서 감지하면 "Domain is active" 이메일 옴

1-5. CF Pages 커스텀 도메인 연결

  1. CF 대시보드 → Workers & Pages → 프로젝트 → Custom domains
  2. "Set up a custom domain" → 도메인 입력 → Activate
  3. www도 별도로 추가 (root + www 둘 다 작동해야 기존 링크 안 깨짐)
  4. Verifying → Active로 자동 전환 (SSL 자동 발급)

1-6. 검증

Invoke-WebRequest "https://도메인" -UseBasicParsing  # 200 OK + 새 사이트 콘텐츠

2. 기존 링크 보존 (_redirects)

블로그·외부에 박힌 옛 경로를 새 경로로 매핑.

public/_redirects:

# 옛 → 새 (301 영구 이전)
/old-path     /new-path     301
/old-prefix/* /new-prefix   301

CF Pages는 빌드 시 public/_redirects를 그대로 dist로 복사 → 즉시 작동.

검증

$r = Invoke-WebRequest "https://도메인/old-path" -UseBasicParsing -MaximumRedirection 0 -ErrorAction SilentlyContinue
# 또는 catch 블록에서 Status + Location 헤더 확인

3. CF Pages trailing slash 함정 ⚠️

CF Pages는 모든 디렉토리 URL에 trailing slash를 강제 (308 redirect).

/apps  → 308 → /apps/  (매번 한 번씩 깜빡임)

해결: 사이트 내 모든 내부 href/ 붙이기. - href="/apps/", href="/about/" - 동적 라우트도 href={`/content/${id}/`} - 앵커는 슬래시 앞: href="/about/#contact" - 외부(https://), 파일(/og.jpg), 순수 앵커(#)는 예외

처음부터 일관되게 박는 게 best.


4. 이메일 셋업 (받기 + 보내기 + 스팸 예방)

4-1. CF Email Routing (무료)

  1. CF 대시보드 → 도메인 → EmailEmail Routing
  2. Enable → CF가 MX·SPF·DKIM TXT 자동 추가 ✅
  3. Destination address: 받을 Gmail 등록 + 인증 (메일의 verify 링크 클릭)
  4. Routes:
  5. Custom address: hello@도메인 → Gmail
  6. Catch-all: Enable + Action Send to email + Destination Gmail
  7. ⚠️ catch-all 토글만 켜고 Action이 Drop이면 모든 메일 버려짐 — 꼭 Send to email로 변경

4-2. DMARC TXT 수동 추가

CF DNS → Add record:

Type: TXT
Name: _dmarc
Content: v=DMARC1; p=none; rua=mailto:hello@도메인
- p=none = 모니터링 모드 (안전한 시작) - 운영 안정 후 p=quarantinep=reject로 강화

4-3. Gmail 화이트리스트 필터

Gmail → ⚙️ → 모든 설정 보기필터 및 차단된 주소새 필터 만들기 - 받는사람: @도메인 ( 없이) - "필터 만들기" → "스팸으로 표시하지 않음"* ✅ + (선택) 라벨 적용

4-4. 도메인 주소로 발송 — Gmail "다른 주소에서 메일 보내기" (2026-06-12 검증)

원리: CF Email Routing은 받기 전용 (회사 주소로 온 우편을 집으로 전달만, 그 이름으로 부치는 기능 없음). 답신/발신까지 도메인 주소로 하려면 Gmail에 발신 별칭을 등록한다. 무료, 10분.

셋업 순서: 1. 앱 비밀번호 발급: https://myaccount.google.com/apppasswords 직접 접속 (보안 메뉴엔 숨겨져 있어서 안 보임. 검색창에 "앱 비밀번호"도 가능). 2단계 인증 필수. 앱 이름 아무거나 → 16자리 비밀번호 복사 (창 닫으면 재확인 불가) 2. Gmail → ⚙️ → 모든 설정 보기 → 계정 및 가져오기 → "다른 주소에서 메일 보내기" → 다른 이메일 주소 추가 - 이메일: hello@도메인 / "별칭으로 처리" 체크 유지 - SMTP 서버: smtp.gmail.com ← ⚠️ Gmail이 도메인 MX(routeN.mx.cloudflare.net)를 자동으로 채워넣는데 그건 수신 전용이라 "서버에 연결할 수 없습니다" 에러. 반드시 교체 - 포트 587 / TLS / 사용자 이름: 본인 Gmail 주소 / 비밀번호: 앱 비밀번호 (계정 비번 아님) 3. 확인 메일이 hello@로 발송 → 포워딩 덕분에 같은 받은편지함 도착 → 인증 링크 클릭 4. 같은 설정 화면에서 "메일을 받은 주소에서 답장하기" 선택 → hello@로 온 메일에 답장하면 자동으로 hello@ 발신. 새 메일은 "보낸 사람" 드롭다운에서 선택

네이버 "발송 주소 다름" 경고 해결 (SPF에 Google 추가): - 증상: 네이버 수신함에 "이 메일은 [도메인]을 통해 발송된 메일이 아닙니다" 빨간 경고. 실제 발송 서버(Gmail)가 도메인의 SPF 허가 목록에 없어서 사칭 의심 - 해결: CF DNS → 루트 TXT의 SPF 레코드에 include:_spf.google.com 추가

v=spf1 include:_spf.mx.cloudflare.net include:_spf.google.com ~all
(기존 cloudflare include는 지우면 안 됨 - 수신 쪽 인증 깨짐) - 반영 몇 분 + 네이버 캐시 고려해 10분 후 "새 메일"로 재테스트

한계와 업그레이드 경로: - 발송 서버가 Gmail이라 받는 쪽에 작게 "via gmail.com" 표시될 수 있음. DMARC 정합(alignment)은 아니지만 p=none이면 실사용 무방 - Resend 도입 시 SMTP 서버만 smtp.resend.com으로 교체하면 도메인 DKIM 정합 발신으로 업그레이드 (via 표시 제거). 1:1 답신 용도면 Gmail SMTP로 충분 - 본격 운영(팀 메일함 등)은 Google Workspace 유료가 정석이나, 문의 답신 용도엔 과함

결과

SPF + DKIM + DMARC + Gmail 필터 = 4중 deliverability. 스팸함 떨어질 확률 최소화. 받기(Email Routing) + 보내기(Gmail 별칭)로 도메인 메일 양방향 완성.


5. 검색엔진 등록

5-1. Google Search Console

  1. search.google.com/search-console → "+ 속성 추가"
  2. "도메인" 옵션 선택 (URL 접두사 말고!) — root + www + http/https 다 커버
  3. DNS TXT 인증 시 "Authorize DNS records from Google" 옵션이 뜨면 클릭 → CF에 자동 추가 (수동 안 해도 됨)
  4. 인증 완료 후 → Sitemapshttps://도메인/sitemap-index.xml 입력 (도메인 속성은 절대 URL 필요)
  5. 첫 fetch는 "가져올 수 없음"으로 떴다 자동 재시도로 "성공" 전환 (시간 걸림, 정상)

5-2. 네이버 서치어드바이저 (한국 타겟 필수)

  1. searchadvisor.naver.com웹마스터 도구 (우측 상단, 메인 메뉴 아님)
  2. 사이트 등록 → 소유 확인 → HTML 태그 옵션
  3. <meta name="naver-site-verification" content="..."> 값 받아 BaseLayout의 <head>에 추가 → push
  4. 배포 폴링 후 "확인" 클릭 → 인증
  5. 요청 → 사이트맵 제출sitemap-0.xml 입력 (네이버는 sitemap-index보다 실제 sitemap을 직접 받는 게 더 잘 동작)

6. 방문자 측정

6-1. Cloudflare Web Analytics (가장 쉬움)

  • CF 대시보드 → Analytics & LogsWeb AnalyticsAdd a site
  • Hostname 입력 → Done → Manage site → RUM Enable → Update
  • Pages 프로젝트면 Automatic setup = 코드 삽입 0
  • 광고차단 우회·쿠키 없음·GDPR 친화

6-2. Google Analytics 4

  1. analytics.google.com → 속성 만들기 (시간대 대한민국, 통화 KRW)
  2. 데이터 스트림(웹) 추가 → 측정 ID G-XXXXXXXXXX 받기
  3. BaseLayout.astro</head> 직전에:
    <script is:inline async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
    <script is:inline>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', 'G-XXXXXXXXXX');
    </script>
    
  4. Astro에서 is:inline 필수 (안 붙이면 번들링되어 inline 실행 안 됨)
  5. 누적 데이터는 24~48시간 후. 실시간 보고서는 즉시 ↔ 광고차단기 켜져 있으면 안 잡힘
  6. CF Analytics와 GA4 이중화 권장 (CF는 광고차단 우회, GA4는 디테일 분석)

7. AEO (Answer Engine Optimization)

AI 답변 엔진(ChatGPT·Claude·Perplexity)이 사이트를 인용하게 만드는 셋업.

7-1. robots.txt에 AI 크롤러 명시적 허용

User-agent: GPTBot
Allow: /

User-agent: ChatGPT-User
Allow: /

User-agent: OAI-SearchBot
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: anthropic-ai
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: Google-Extended
Allow: /

User-agent: CCBot
Allow: /

7-2. public/llms.txt (신흥 표준)

# 사이트명

> 한 단락 핵심 가치 설명 (사이트가 뭐 하는 곳인지, 누구를 위해, 무엇을 제공)

## 핵심 페이지

- [페이지 제목](URL): 한 줄 설명
- ...

## 콘텐츠 카테고리

- [카테고리](URL): 설명

## 사이트맵

- [전체 사이트맵](https://도메인/sitemap-index.xml)

## 운영 주체

회사·운영자 소개. 문의: hello@도메인

public/에 두면 빌드 시 dist로 복사 → /llms.txt 접근 가능.

7-3. 구조화 데이터 (이미 있는 것 활용)

  • Article, BlogPosting, Organization, Book, FAQPage, CollectionPage, BreadcrumbList
  • 모두 JSON-LD <script type="application/ld+json">로 페이지에 삽입
  • FAQ 구조 + "이 글의 핵심" 요약 박스는 AI 인용에 특히 유리

8. 마무리 체크리스트 (Pre-launch)

다음 사이트 런칭 시 이 순서대로:

  • [ ] astro.config.mjs site = 최종 도메인 확인
  • [ ] BaseLayout canonical/OG가 site 값 참조
  • [ ] robots.txt + sitemap 자동 생성
  • [ ] CF에 도메인 connect (Free)
  • [ ] DNS 스캔 결과 그대로 두고 Continue
  • [ ] 등록업체에서 NS → CF로 교체 (IP 비움)
  • [ ] 전파 폴링 (Google DNS, cloudflare 매칭)
  • [ ] CF Pages Custom Domain root + www 둘 다 추가
  • [ ] _redirects 작성 (기존 링크 보존)
  • [ ] 내부 href에 trailing slash 통일 (놓치기 쉬움)
  • [ ] CF Email Routing 활성화 + catch-all + Gmail 인증
  • [ ] DMARC TXT 추가 (p=none)
  • [ ] Gmail 화이트리스트 필터
  • [ ] Gmail "다른 주소에서 메일 보내기" (smtp.gmail.com + 앱 비밀번호) + "받은 주소에서 답장"
  • [ ] SPF에 include:_spf.google.com 추가 (네이버 사칭 경고 방지)
  • [ ] GSC 도메인 속성 + 사이트맵 (절대 URL)
  • [ ] 네이버 서치어드바이저 + 사이트맵 (sitemap-0.xml)
  • [ ] CF Web Analytics enable (RUM)
  • [ ] GA4 측정 ID 받아 BaseLayout에 is:inline 스크립트 추가
  • [ ] robots.txt AI 크롤러 허용 추가
  • [ ] llms.txt 작성

각 단계마다 PowerShell Invoke-WebRequest 폴링으로 배포 검증.


9. 주의·함정 모음

함정 해결
CF Pages trailing slash 308 깜빡임 내부 href 끝에 / 통일
Email Routing catch-all Action이 Drop 기본값 Send to email로 꼭 변경
Gmail 발신 별칭 추가 시 SMTP에 도메인 MX 자동 입력 → 연결 실패 smtp.gmail.com:587로 교체 (CF MX는 수신 전용)
앱 비밀번호 메뉴가 보안 설정 화면에 없음 myaccount.google.com/apppasswords 직접 접속
네이버 "발송 주소 다름" 사칭 경고 루트 SPF에 include:_spf.google.com 추가
GSC 도메인 속성 사이트맵 입력 시 상대경로 거부 절대 URL 입력 (https://도메인/sitemap-index.xml)
네이버 사이트맵 sitemap-index는 일부 안 잡힘 sitemap-0.xml 직접 입력이 안전
Astro에서 <script>가 번들링되어 GA4 실행 안 됨 is:inline 속성 추가
183개 카드 이미지 일괄 eager 프리로드 → 탭 무한 로딩 native loading="lazy"만 사용, JS 프리로드 X
모바일 sticky 바 페이지·글로벌 중복 BaseLayout에 hideMobileBar prop, 페이지별 분기
Vercel과 달리 CF Pages는 push 이메일과 무관 깃 푸시만으로 자동 빌드
CF Pages 무음 빌드 실패 (D1 placeholder 등) 빌드 로그·배포 폴링으로 반영 검증 필수

10. 관련 study 문서

  • 35-cf-pages-astro.md — CF Pages + Astro 기초 셋업, wrangler 함정
  • (다음) ??-decap-cms.md — Decap CMS 도입 (예정)
  • (다음) ??-resend-newsletter.md — Resend로 도메인 메일 발송 + SPF·DKIM 강화
  • (다음) ??-subdomain-vercel.md — Vercel 앱을 서브도메인에 연결

11. 실제 사례 (메타)

  • 적용 사이트: storywinner.co.kr
  • 소요 시간: 1세션 (대략 5~6시간, 학습 + 단계별 안내 포함)
  • 결과: 도메인·SSL·이메일·SPF/DKIM/DMARC·GSC·네이버·CF Analytics·GA4·AEO 풀 셋업
  • 셋업 상세: ~/.claude/projects/D--Sites-storywinner/memory/project_launch-complete.md