2026년 블로그 SEO 자동화: IndexNow 스크립트로 초기 검색 인지 작업 끝내기
2026년 블로그 SEO 자동화: IndexNow 스크립트로 초기 검색 인지 작업 끝내기
2026년 블로그 SEO 자동화: IndexNow 스크립트로 초기 검색 인지 작업 끝내기
새로운 글을 발행하거나, 기존 블로그에 많은 글이 쌓였을 때 초기 검색 엔진 인지 작업을 어떻게 자동화할까 고민이 많았어요. 블로그의 초기 SEO 성능을 높이려면 IndexNow API를 활용해서 사이트맵을 검색 엔진에 제출하는 게 중요한데, 일일이 URL을 넣는 건 정말 비효율적이더라고요.
시도와 함정
일단 데이터베이스에서 발행된 모든 블로그 글의 slug를 가져와서 완전한 URL 목록을 만드는 것부터 시작했어요. 이걸 IndexNow API에 일괄적으로 ping 해줄 Python 스크립트를 짜야 했죠. asyncio를 써서 비동기적으로 처리하고, 효율을 위해 URL을 100개씩 묶어서 보내기로 했습니다.
# riel_backend/scripts/indexnow_seed.py (일부) import asyncio from typing import List from riel_backend.services import indexnow_service from riel_backend.database import session_scope, Blogasync def ping_urls_in_chunks(urls: List[str], chunk_size: int = 100): tasks = [] for i in range(0, len(urls), chunk_size): chunk = urls[i:i + chunk_size] tasks.append(indexnow_service.ping_urls(chunk)) await asyncio.gather(*tasks)
async def main(): async with session_scope() as session: slugs = await Blog.get_published_slugs(session) urls = [f"https://{os.environ['INDEXNOW_HOST']}/blog/{slug}" for slug in slugs] await ping_urls_in_chunks(urls)
if name == "main": asyncio.run(main())
처음에는 그냥 모든 URL을 한 번에 보내려고 했는데, API 응답이 너무 느리거나 간혹 타임아웃이 발생하더라고요. 이게 비동기 처리가 제대로 안 되는 건지, 아니면 단순히 요청이 너무 많아서 그런 건지 헷갈렸어요. 3시간 정도 삽질 끝에 발견한 건, asyncio.gather를 제대로 사용하지 않으면 병렬 처리가 안 된다는 점이었어요.
원인
결국 문제는 asyncio.gather를 사용하긴 했지만, 각 chunk별로 ping_urls 함수를 호출하는 tasks 리스트를 제대로 관리하지 못해서 발생했던 거였어요. 각 chunk마다 별도의 asyncio.gather를 호출했어야 했는데, 이걸 하나의 asyncio.gather로 묶지 못했던 거죠.
해결
riel_backend/scripts/indexnow_seed.py 스크립트를 수정해서 데이터베이스에서 published 상태의 블로그 글 slug를 조회하고, 이를 기반으로 https://{INDEXNOW_HOST}/blog/{slug} 형식의 URL 목록을 생성했습니다. 생성된 URL 목록을 100개씩 분할하여 services.indexnow_service.ping_urls 함수를 비동기적으로 호출하여 IndexNow API에 ping하도록 코드를 변경했어요. 각 chunk 처리 결과를 출력하고 최종 성공 chunk 수를 요약하며, 작업 완료 후 데이터베이스 연결을 종료하도록 했습니다.
# riel_backend/scripts/indexnow_seed.py (수정 후) import asyncio import os from typing import List from riel_backend.services import indexnow_service from riel_backend.database import session_scope, Blogasync def ping_urls_in_chunks(urls: List[str], chunk_size: int = 100): total_chunks = (len(urls) + chunk_size - 1) // chunk_size successful_chunks = 0 for i in range(0, len(urls), chunk_size): chunk = urls[i:i + chunk_size] try: await indexnow_service.ping_urls(chunk) print(f"Successfully pinged chunk {i//chunk_size + 1}/{total_chunks}") successful_chunks += 1 except Exception as e: print(f"Error pinging chunk {i//chunk_size + 1}/{total_chunks}: {e}") return successful_chunks
async def main(): async with session_scope() as session: slugs = await Blog.get_published_slugs(session) urls = [f"https://{os.environ['INDEXNOW_HOST']}/blog/{slug}" for slug in slugs] print(f"Found {len(urls)} URLs to ping.") successful_chunks = await ping_urls_in_chunks(urls) print(f"Finished pinging. Successfully processed {successful_chunks}/{len(urls)//100 + (1 if len(urls)%100 else 0)} chunks.")
if name == "main": # 실제 실행 시에는 INDEXNOW_HOST 환경변수가 설정되어 있어야 합니다. # 예: export INDEXNOW_HOST="your-blog.com" if 'INDEXNOW_HOST' not in os.environ: print("Error: INDEXNOW_HOST environment variable not set.") exit(1) asyncio.run(main())
이 스크립트를 실행하면 python -m scripts.indexnow_seed 명령어로 동작합니다.
결과
- 블로그의 모든 기존 글에 대한 IndexNow API 초기 ping 작업이 자동화되었습니다.
- 검색 엔진의 콘텐츠 발견이 촉진되어 초기 SEO 성능 향상에 기여했습니다.
- 수동 작업 대비 시간과 노력을 크게 절감했습니다.
정리 — 같은 함정 안 빠지려면
- [ ] 대량의 URL을 비동기적으로 처리할 때는
asyncio.gather를 올바르게 사용하여 모든 태스크가 병렬로 실행되도록 합니다. - [ ] URL 목록을 chunk로 나누어 처리할 때는 각 chunk별로 성공/실패 여부를 명확히 로깅하고, 전체 작업 결과를 요약합니다.
- [ ] 외부 API 호출 시 발생할 수 있는 타임아웃이나 오류에 대비하여 적절한 예외 처리를 구현합니다.
- [ ] 스크립트 실행에 필요한 환경 변수(예:
INDEXNOW_HOST)가 제대로 설정되었는지 확인하는 로직을 추가합니다.
태그
📨 박주니에게 한마디
스팸·악성 메시지 방지를 위해 구글 로그인 후 메시지를 보낼 수 있어요. 비공개로 전달되며, 운영자 외에는 볼 수 없습니다.
Google 로그인 후 메시지 남기기