Astro + Netlify 블로그에 IndexNow 자동 색인 요청 적용하기

Astro + Netlify 블로그에 IndexNow 자동 색인 요청 적용하기

새 글 발행할 때마다 수동으로 색인 요청하기 귀찮다면? Netlify Build Plugin으로 Naver, Bing 자동 색인 요청 구현 완벽 가이드

블로그를 운영하다 보면 새 글을 발행할 때마다 검색엔진에 색인 요청을 해야 한다. Google Search Console, Naver Search Advisor, Bing Webmaster Tools… 하나씩 들어가서 URL을 입력하는 게 여간 귀찮은 일이 아니다.

특히 초기 블로그는 검색엔진이 자동으로 크롤링해주는 주기가 길어서 (며칠~몇 주) 수동 색인 요청이 거의 필수다. 이걸 자동화할 수는 없을까?

IndexNow가 뭔가요?

IndexNow는 Microsoft와 Yandex가 만든 프로토콜로, 웹사이트가 검색엔진에게 “이 URL이 새로 생겼어요!” 또는 “이 URL이 업데이트됐어요!”라고 즉시 알려주는 API다.

지원 검색엔진

  • ✅ Bing (Microsoft)
  • ✅ Naver
  • ✅ Yandex
  • ✅ Seznam.cz

지원 안 하는 검색엔진

  • ❌ Google (자체 시스템만 사용)

Google은 IndexNow를 지원하지 않으므로, Google에는 별도로 Search Console에서 수동 요청하거나 Sitemap으로 대응해야 한다.

왜 IndexNow를 쓰려고 했나?

내 상황

  • Astro로 만든 블로그
  • Netlify로 SSG 배포
  • Git push → 자동 배포
  • 새 글 발행할 때마다 수동 색인 요청 중 😫

기존 방식의 문제점

  1. 시간 낭비: 매번 Search Console/Webmaster 들어가서 URL 입력
  2. 요청 제한: Google은 하루 10개만 수동 요청 가능
  3. 깜빡할 수 있음: 글 쓰는 데 집중하다 색인 요청 잊어버림
  4. 느린 자동 색인: Sitemap만 제출하면 며칠~몇 주 걸림

목표

Git push하면 → Netlify 빌드 완료 후 → 자동으로 IndexNow 요청

어떻게 구현했나?

1단계: IndexNow API 키 생성

IndexNow는 도메인 소유권 증명을 위해 API 키가 필요하다.

키 생성 방법

UUID 형식의 키를 생성한다. https://www.uuidgenerator.net/ 같은 사이트에서 생성 가능.

예시: a1b2c3d4-e5f6-7890-abcd-ef1234567890

키 파일 생성

/public/a1b2c3d4-e5f6-7890-abcd-ef1234567890.txt 파일을 만들고, 내용도 똑같이 입력:

a1b2c3d4-e5f6-7890-abcd-ef1234567890

중요: 파일명(확장자 제외)과 파일 내용이 완전히 동일해야 한다!

이 파일은 IndexNow가 https://yourblog.com/a1b2c3d4-e5f6-7890-abcd-ef1234567890.txt로 접속해서 도메인 소유권을 확인하는 용도다.

2단계: Netlify 환경변수 설정

Netlify Dashboard에서:

  1. Site settings → Environment variables
  2. Add variable
    • Key: INDEXNOW_KEY
    • Value: a1b2c3d4-e5f6-7890-abcd-ef1234567890 (1단계에서 생성한 키)

코드에 직접 키를 쓰면 GitHub에 노출되므로 환경변수로 관리한다.

3단계: Netlify Build Plugin 작성

plugins/indexnow/index.js:

export default {
  async onSuccess() {
    const INDEXNOW_KEY = process.env.INDEXNOW_KEY;
    
    if (!INDEXNOW_KEY) {
      console.log('❌ INDEXNOW_KEY 환경변수가 설정되지 않았습니다');
      return;
    }
    
    const { execSync } = await import('child_process');
    
    // Git으로 변경된 블로그 파일만 찾기
    let changedFiles = [];
    try {
      changedFiles = execSync(
        'git diff --name-only HEAD~1 HEAD -- "src/content/blog/**/*.{md,mdx}"',
        { encoding: 'utf-8' }
      )
        .trim()
        .split('\n')
        .filter(Boolean);
    } catch (e) {
      console.log('⚠️ 변경된 파일 없음');
      return;
    }
    
    if (changedFiles.length === 0) {
      console.log('✅ 새 블로그 글 없음 - IndexNow 건너뜀');
      return;
    }
    
    // 변경된 파일을 URL로 변환
    const urls = changedFiles.map(file => {
      const slug = file
        .replace('src/content/blog/', '')
        .replace(/\.(md|mdx)$/, '');
      return `https://yourblog.com/blog/${slug}`;
    });
    
    // IndexNow API 호출
    try {
      const response = await fetch('https://api.indexnow.org/indexnow', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          host: 'yourblog.com',
          key: INDEXNOW_KEY,
          keyLocation: `https://yourblog.com/${INDEXNOW_KEY}.txt`,
          urlList: urls
        })
      });
      
      if (response.ok) {
        console.log(`✅ IndexNow 제출 완료: ${urls.length}개 URL (Bing, Naver)`);
        urls.forEach(url => console.log(`   📄 ${url}`));
      } else {
        console.log(`❌ IndexNow 오류: ${response.status}`);
      }
    } catch (error) {
      console.log(`❌ IndexNow 실패: ${error.message}`);
    }
  }
};

plugins/indexnow/manifest.yml:

name: indexnow-auto-submit

4단계: netlify.toml 설정

netlify.toml:

[[plugins]]
  package = "./plugins/indexnow"

전체 파일 구조

프로젝트/
├── public/
│   └── [your-api-key].txt  # API 키 파일
├── plugins/
│   └── indexnow/
│       ├── index.js          # 플러그인 로직
│       └── manifest.yml      # Netlify 플러그인 선언
└── netlify.toml              # 플러그인 활성화

작동 방식

1. 새 글 작성/수정
2. Git commit & push
3. Netlify 자동 빌드 시작
4. Astro SSG 빌드 완료
5. onSuccess 훅 실행 ← 여기서 IndexNow 호출!
6. Git diff로 변경된 .md/.mdx 파일만 감지
7. IndexNow API에 URL 제출
8. Bing, Naver가 빠르게 크롤링
9. 배포 완료

Netlify 빌드 로그 예시:

✅ IndexNow 제출 완료: 2개 URL (Bing, Naver)
   📄 https://yourblog.com/blog/my-awesome-post
   📄 https://yourblog.com/blog/another-great-article

실제 경험과 팁

좋았던 점

  1. 완전 자동화: 글 쓰기에만 집중, 색인은 신경 안 써도 됨
  2. 변경 감지: Git diff로 새 글/수정된 글만 정확히 감지
  3. 빠른 색인: Naver, Bing 색인이 1 ~ 2일 내로 완료 (기존 1 ~ 2주 → 1 ~ 2일)
  4. 무료: Netlify 무료 플랜으로 충분 (월 125,000회 Function 호출 제공)

주의할 점

  1. Google은 별도: IndexNow로 해결 안 됨, Search Console 수동 요청 or 기다리기
  2. 샌드박스 기간: 새 도메인은 1~3개월 샌드박스 기간 있음
  3. 과도한 요청 금지: 같은 URL을 하루에 여러 번 요청하면 스팸 처리될 수 있음
  4. 작은 수정은 불필요: 오타 한 글자 고친 거로 색인 요청할 필요 없음

개선 아이디어

중복 방지 (24시간 내 재요청 방지)

캐시 파일로 최근 제출 이력 저장:

// 캐시 로드
let cache = {};
try {
  cache = JSON.parse(await readFile('./indexnow-cache.json', 'utf-8'));
} catch (e) {}

const now = Date.now();
const ONE_DAY = 24 * 60 * 60 * 1000;

// 24시간 이내 제출한 URL 제외
const urlsToSubmit = urls.filter(url => {
  const lastSubmit = cache[url];
  if (!lastSubmit) return true;
  return (now - lastSubmit) > ONE_DAY;
});

Frontmatter 플래그로 선택적 제출

중요한 글만 색인 요청:

---
title: "중요한 글"
indexNow: true  # 이 플래그 있을 때만 색인 요청
---

대안: 수동 스크립트

완전 자동화가 부담스럽다면, 필요할 때만 실행하는 스크립트:

scripts/request-index.js:

const INDEXNOW_KEY = 'your-api-key-here';

// 사용법: node scripts/request-index.js blog/my-post
const slugs = process.argv.slice(2);
const urls = slugs.map(slug => `https://yourblog.com/blog/${slug}`);

await fetch('https://api.indexnow.org/indexnow', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    host: 'yourblog.com',
    key: INDEXNOW_KEY,
    keyLocation: `https://yourblog.com/${INDEXNOW_KEY}.txt`,
    urlList: urls
  })
});

console.log(`✅ 색인 요청 완료: ${urls.length}개`);

실행:

node scripts/request-index.js my-awesome-post another-article

결론

IndexNow는 설정만 제대로 하면 블로그 색인 관리를 완전히 자동화할 수 있는 강력한 도구다.

Naver와 Bing 색인이 빨라지면 초기 트래픽 확보에 큰 도움이 된다. Google은 여전히 수동 관리가 필요하지만, 전체 색인 관리 부담은 크게 줄어든다.

블로그를 막 시작했거나, 매번 수동 색인 요청이 귀찮다면 IndexNow를 강력히 추천한다.


참고 자료