블로그 게시물 노출 강화: IndexNow API로 자동화 시스템 구축

발행: (2026년 6월 7일 PM 01:00 GMT+9)
7 분 소요
원문: Dev.to

출처: Dev.to

박준희

많은 분들이 새 블로그 글을 발행했지만 검색 엔진 결과에 바로 나타나지 않아 답답함을 겪어보셨을 겁니다. 최근에 Bing과 Yandex 같은 검색 엔진이 IndexNow API를 통해 새 글을 빠르게 알릴 수 있는 방법을 제공한다는 것을 알게 되었고, 저는 이 기능을 제 블로그에 통합하기로 했습니다.

시도와 함정

우선 services/indexnow_service.py에 글이 발행될 때 IndexNow API를 호출하는 헬퍼 함수를 만들었습니다. BlogRepository.update_status 메서드에서 글 상태가 'published' 로 바뀔 때마다 asyncio.create_task 로 비동기적으로 ping을 보내도록 코드를 구성했습니다.

# services/indexnow_service.py (partial)
import asyncio
import httpx

async def ping_urls(urls: list[str], api_key: str):
    async with httpx.AsyncClient() as client:
        for url in urls:
            try:
                response = await client.post(
                    "https://api.indexnow.org/submit-url",
                    json={"url": url, "key": api_key}
                )
                response.raise_for_status()
                print(f"Successfully pinged {url}")
            except httpx.HTTPStatusError as e:
                print(f"Error pinging {url}: {e}")
            except Exception as e:
                print(f"An unexpected error occurred for {url}: {e}")

async def ping_blog_post(post_url: str, api_key: str):
    await ping_urls([post_url], api_key)

# BlogRepository.update_status (partial)
async def update_status(self, post_id: int, new_status: str):
    # ... existing logic ...
    if new_status == 'published' and INDEXNOW_KEY:
        post = await self.get_post_by_id(post_id) # In reality, you'd get the URL from the post object
        asyncio.create_task(ping_blog_post(post.url, INDEXNOW_KEY))
    # ...

또한 관리용 API 엔드포인트를 만들어 수동으로 ping을 트리거할 수 있게 했고, public/.txt 파일을 설정하고 미들웨어까지 구성했습니다. 그런데 의외로 ping이 전혀 전송되지 않았습니다. 약 3시간 정도 디버깅한 끝에, IndexNow API가 요구하는 소유권 검증 파일의 경로가 제가 예상한 것과 다르다는 것을 발견했습니다. 경우에 따라서는 /public/.txt 가 아니라 /KEY.txt 로 접근해야 했습니다.

원인

문제는 IndexNow API가 소유권을 검증하는 방식에 있었습니다. 저는 파일을 public/ 디렉터리 안에 두었지만, IndexNow는 루트 디렉터리에 바로 두는 것을 선호하거나 경로 설정에 더 엄격했습니다. 또한 INDEXNOW_KEY 환경 변수가 올바르게 설정되지 않아 기능이 비활성화된 경우도 있었습니다.

해결책

몇 가지 수정을 통해 문제를 해결했습니다.

소유권 파일 경로 수정: public/ 디렉터리를 없애고 KEY.txt 파일을 루트 디렉터리에 직접 두었습니다. 웹 프레임워크의 미들웨어를 설정해 이 파일을 바로 제공하도록 했습니다.

환경 변수 체크 강화: INDEXNOW_KEY 환경 변수가 설정되어 있는지, 그리고 유효한 값인지 명시적으로 확인하는 로직을 추가했습니다.

비동기 Ping 로직 개선: BlogRepository.update_status 에서는 여전히 asyncio.create_task 를 사용해 ping 요청이 메인 요청 흐름을 방해하지 않도록 했습니다.

# services/indexnow_service.py (after modification)
import asyncio
import httpx
import os

INDEXNOW_KEY = os.environ.get("INDEXNOW_KEY")

async def ping_urls(urls: list[str]):
    if not INDEXNOW_KEY:
        print("INDEXNOW_KEY is not set. Skipping ping.")
        return

    async with httpx.AsyncClient() as client:
        for url in urls:
            try:
                response = await client.post(
                    "https://api.indexnow.org/submit-url",
                    json={"url": url, "key": INDEXNOW_KEY}
                )
                response.raise_for_status()
                print(f"Successfully pinged {url}")
            except httpx.HTTPStatusError as e:
                print(f"Error pinging {url}: {e}")
            except Exception as e:
                print(f"An unexpected error occurred for {url}: {e}")

async def ping_blog_post(post_url: str):
    await ping_urls([post_url])

# main.py or app.py (example middleware setup)
# from fastapi import FastAPI
# from fastapi.staticfiles import StaticFiles
#
# app = FastAPI()
#
# # Configure to serve KEY.txt file directly from the root directory
# app.mount("/", StaticFiles(directory=".", html=True), name="static")
#
# # BlogRepository.update_status (after modification)
# async def update_status(self, post_id: int, new_status: str):
#     # ... existing logic ...
#     if new_status == 'published' and INDEXNOW_KEY:
#         post = await self.get_post_by_id(post_id)
#         asyncio.create_task(ping_blog_post(post.url))
#     # ...

# Example admin API endpoint
# @router.post("/blog/indexnow-ping-all")
# async def indexnow_ping_all():
#     all_posts = await blog_repository.get_all_published_posts()
#     for post in all_posts:
#         asyncio.create_task(ping_blog_post(post.url))
#     return {"message": "Initiated ping for all published posts."}

결과

  • 글을 발행한 뒤 검색 엔진 결과에 나타나는 시간이 눈에 띄게 단축되었습니다.
  • INDEXNOW_KEY 환경 변수를 통해 언제든 기능을 켜고 끌 수 있어 보안 관리가 용이해졌습니다.
  • 관리용 API 덕분에 초기 설정 시나 놓친 글을 일괄 ping하는 작업이 훨씬 쉬워졌습니다.

asyncio.create_task 덕분에 ping 작업이 백그라운드에서 처리되어 사용자 경험에 전혀 영향을 주지 않습니다.

요약 — 같은 함정을 피하는 방법

  • IndexNow API를 사용할 때는 소유권 검증 파일(KEY.txt)의 정확한 경로 설정을 반드시 재확인하세요. 웹 프레임워크의 정적 파일 제공 설정을 점검해야 합니다.
  • INDEXNOW_KEY 환경 변수는 필수이며, 기능을 켜고 끄는 용도로 안전하게 관리하세요.
  • 글이 발행될 때 IndexNow ping을 비동기(asyncio.create_task)로 처리해 사용자 경험 저하를 방지하세요.
  • 초기 설정 및 누락된 글을 재처리하기 위해 모든 글을 일괄 ping하는 관리 API를 구축해 두면 매우 유용합니다.
0 조회
Back to Blog

관련 글

더 보기 »