블로그 게시물 노출 강화: IndexNow API로 자동화 시스템 구축
출처: 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를 구축해 두면 매우 유용합니다.
