o2o-site-AEO/docs/SEARCH_CONSOLE.md
Mina Choi 3f47d5ecd2 [feat] solution/backend: 서치콘솔 자동 제출·색인 상태 추적 추가
사이트 발행 성공과 Google 색인 관측은 별도 상태다. 외부 API 장애로 발행이 실패하거나 재시작 때 추적 정보가 사라지지 않도록 분리.

- Google 클라이언트·배치·DB·Teams 알림 모듈 분리
- 기존 스케줄러 연결, 재시도·중복 실행 방지와 선택 설정 추가
- ORM·초기 DDL·마이그레이션·운영 설정 문서 동시 갱신

검증: 관련 59건 통과, compose 설정·diff 검사 통과. 추가 회귀 23건 통과, 기존 발행 검수 실패 1건은 변경 전 코드에서도 재현. 운영 배포·Google/Teams 실호출 미실행.
2026-09-15 14:50:30 +09:00

6.5 KiB

Google Search Console 자동 추적

발행 DB 감지 → 공개 사이트맵 확인/제출 → 색인 조회 → 상태 저장·Teams 알림

경계

  • 기존 API의 스케줄러에서 10분마다 실행한다. 컨테이너 추가 없음.
  • sites.status=PUBLISHED인 사이트만 등록하므로 초안/목업 디렉토리 나열을 작업 원장으로 쓰지 않는다.
  • 발행 DB에서 재발견한다. 발행 순간 별도 큐 적재가 실패하는 틈이 없고 재시작해도 이어진다.
  • 발행 트랜잭션/잡과 독립적이다. Google 실패가 사이트 발행을 실패로 바꾸지 않는다.
  • 한 번에 신규 발행 100개 등록, 조회는 오래 기다린 5개 처리. 정상 조회는 24시간 후 반복.
  • 현재 렌더러의 단일 루트 urlset만 지원하고 읽기 상한은 5MB다. 향후 sitemap index 분할 시 확장한다.
  • 오류는 1·2·4·8·16·24시간 간격 재시도. 기본 주기 기준 하루 최대 720회 검사이며, 다른 도구의 같은 속성 사용량도 Google 할당량에 포함된다. 대량 백로그는 여러 날에 걸쳐 소진한다.
  • PostgreSQL transaction advisory lock으로 다중 API 프로세스의 동시 배치를 막는다. 단일 배치는 외부 호출 동안 트랜잭션/연결 1개를 점유한다(검사 1건 최대 90초, 최대 5건).
  • 사이트맵 제출 성공과 URL 색인 성공은 별개다. first_indexed_at우리가 처음 PASS를 관측한 시각이다. Google 내부 색인 시각이나 최신 발행 버전 반영 시각이 아니다. 원본 lastCrawlTime도 함께 보관한다.
  • 재발행 시 해당 발행의 관측 상태를 초기화한다. 지난 관측 이력 전체를 누적하는 이벤트 저장소는 아니다.
  • SITE_PUBLIC_HOST 변경은 기존 지침대로 재발행이 필요하다. 사이트 주소의 단일 출처는 site_payload다.

최초 설정 (운영자)

  1. Search Console에서 발행 도메인의 소유권 확인. URL-prefix 속성이면 https://web4ai.o2osolution.ai/, 도메인 속성이면 sc-domain:web4ai.o2osolution.ai 형태.
  2. Google Cloud에서 Search Console API 활성화, 전용 서비스 계정 생성.
  3. Search Console 속성 설정 → 사용자 및 권한에서 그 서비스 계정 이메일에 전체 사용자 권한 부여. Google 로그인용 GOOGLE_CLIENT_ID와는 다른 인증이다.
  4. 서비스 계정 JSON 키는 저장소 밖에 보관한다. 권한을 최소화하고 git/이미지/로그에 넣지 않는다.
  5. 루트 .env 설정:
GSC_ENABLED=1
GSC_PROPERTY_URL=https://web4ai.o2osolution.ai/
GSC_CREDENTIALS_HOST_FILE=/secure/location/search-console.json
GSC_ALERT_DAYS=7
GSC_ALERT_WEBHOOK_URL=

키 생성/권한 부여/실제 알림 전송은 구현 검증 중 자동 수행하지 않는다.

배포

먼저 새 이미지에 requirements를 설치하고 0014_search_console.sql을 기존 마이그레이션 도구로 적용한다. 프로젝트 전체 마이그레이션 순서를 확인한 뒤 실행한다. 아래는 운영자가 실행할 명령이며 자동 배포하지 않았다.

docker compose exec -T solution-backend python scripts/migrate.py
docker compose -f docker-compose.yml -f docker-compose.search-console.yml up -d --build solution-backend

선택 compose 파일은 API에만 키를 읽기 전용 마운트하고 GSC_CREDENTIALS_FILE을 설정한다. 없는 파일을 디렉토리로 자동 생성하지 않는다. 이후 배포에서도 이 override를 함께 사용해야 한다. 로컬 Python 실행은 GSC_CREDENTIALS_FILE에 로컬 키 파일 경로를 지정한다. 켜진 스케줄러는 첫 10분 주기부터 기존 발행 사이트도 등록한다. GSC_ENABLED=0이면 DB/Google 호출 모두 생략한다.

알림

Teams Workflows의 webhook 수신 → 채널에 Adaptive Card 게시 흐름 URL을 GSC_ALERT_WEBHOOK_URL에 넣는다. 비우면 외부 전송 없이 경고 로그/DB만 남는다. API/사이트맵 오류 또는 발행 후 기본 7일 미색인 시 알린다. 성공한 알림은 사이트별 24시간 중복 억제. 전송 실패는 alerted_at을 갱신하지 않아 다음 검사 때 재시도한다. 외부 전송 후 DB commit 전에 죽으면 중복 알림이 가능하다(at-least-once). 키·토큰·webhook URL·Google 오류 본문은 알림에 포함하지 않는다.

결과 확인

docker compose exec -T solution-backend python scripts/search_console_status.py

읽기 전용이며 Google API를 추가 호출하지 않는다. 프론트 화면/API 계약은 변경하지 않았다.

파일 책임
services/search_console_client.py 인증·Google HTTP·오류 정규화
services/search_console_settings.py 선택 설정·속성 URL 범위
services/search_console_service.py 배치 흐름·재시도·관측 결과
crud/search_console_crud.py 발행 감지·등록·조회 순서·동시 실행 잠금
services/search_console_alerts.py 알림 조건·Teams 전송

구글 지원 범위 / 남은 운영 작업

  • 사이트맵 제출 API는 지원된다.
  • URL Inspection API는 Google이 이미 알고 있는 상태 조회용이며 실시간 페이지 테스트나 색인 요청 API가 아니다.
  • 일반 숙박 사이트는 Indexing API 대상이 아니다.
  • 검사 할당량은 속성당 하루 2,000회다.
  • Teams webhook 형식.
  • 실제 서비스 계정 권한/사이트맵 제출/색인 관측/Teams 수신은 설정 후 운영 검증이 필요하다.
  • 기존 루트 사이트맵의 백업 URL 정리와 IndexNow 개별 사이트맵 참조 문제는 이 기능과 별도다. 이 기능은 기존 공개 사이트맵을 제출하며 내용을 다시 만들거나 목업을 삭제하지 않는다.

구현 검증 (2026-09-15)

  • 격리 PostgreSQL에서 클라이언트·배치·스키마·IndexNow 관련 59건 통과.
  • 발행·설정·사이트 목록 회귀검사: 23건 통과, test_unverified_fact_blocks_publish 1건 실패. 해당 실패는 변경 전 HEAD 9773bc0의 발행 코드에서도 동일 재현됨(GSC 비활성).
  • Google/Teams 실호출 없음. 서비스 계정 권한·실제 제출·채널 수신은 운영 설정 후 검증 대상.