애니 시맨틀

개발 로그

← 게임으로 돌아가기

애니 시맨틀이 어떻게 설계되고, 어떤 문제를 겪었고, 어떻게 고쳐왔는지를 최신순으로 기록합니다. 잘 풀린 부분뿐 아니라 시행착오와 실패, 그리고 그걸 바로잡은 과정도 그대로 남겨둡니다. 새로운 변화가 있을 때마다 이 목록 맨 위에 계속 이어서 기록할 예정입니다. 애드센스 심사 관련 사건들을 회고 형태로 정리한 글은 이 회고 글을 참고하세요.

콘텐츠 획일성 가설 검증용 기사 발행 — 답이 안 나온 세 가지

기존 7편의 분석 글이 전부 "수치 제시 + 해설"이라는 같은 틀이라 획일적으로 보일 수 있다는 가설을 검증하기 위해, 형식이 다른 기사를 한 편 추가했습니다. 재료는 mine_content_data.py가 캤지만 기사가 되지 못한 광맥 F·I·K입니다 — 참고로 README에는 한동안 "D/F/I/K"로 적혀 있었는데, D는 이미 rating-myth로 발행된 광맥이라 재활용 대상이 아니었습니다. 오타를 바로잡고 실제로는 F/I/K만 썼습니다. 이 세 광맥은 전부 "재봤는데 답이 안 나왔다"는 결론이라, 다른 글처럼 표+박스+게임 팁으로 끝내지 않고 질문마다 무엇을 몰랐는지를 그대로 남기는 구조로 썼습니다.

GSC 사이트맵 재제출, 6차 신청 전 14일 안정화 마감을 09-20으로 잡음

아래 배포 사고를 복구한 직후 Google Search Console에 sitemap을 다시 제출했습니다(35 URL, 라이브 반영 완료). 6차 애드센스 신청의 관문은 International Targeting 리포트가 0에러로 14일 연속 유지되는지인데, 이 확인 마감을 2026-09-20으로 잡았습니다 — GSC 리포트가 최대 10일까지 지연될 수 있다는 점을 감안한 여유폭입니다.

오래된 로컬 트리로 배포해 라이브 pool을 되돌렸다가 복구

아래 아카이브 수정을 로컬에서 커밋한 뒤 배포했는데, 이 세션의 로컬 클론이 08-25 시점에 멈춰 있는 걸 모른 채 오래된 작업 트리를 그대로 배포해버렸습니다. 그사이 원격에는 08-26 구조 후속 정리, 일간 아카이브 자동 커밋들, 09-01 월간 데이터 갱신(정답 풀 3,110→3,112개) 등 15개 커밋이 쌓여 있었습니다. git status가 "clean, up to date"로 나온 건 애초에 원격에서 pull한 적이 없었기 때문이었습니다 — 로컬이 원격보다 뒤처져 있다는 건 git status가 알려주지 않습니다. 다행히 게임은 깨지지 않았지만(옛 pool끼리는 서로 정합적이었음), 사고 구간(09-06 20:24 KST 전후) 동안 실제 플레이어가 본 정답과 아카이브에 기록될 정답이 달랐을 수 있어 이 날짜를 SKIP_DATES에 추가했습니다. 원격을 병합해 새 pool로 즉시 재배포해 복구했습니다. 교훈: 배포 전에는 항상 git fetch && git log HEAD..origin/main --oneline로 로컬이 원격에 뒤처져 있지 않은지 먼저 확인할 것.

색인 대상 아카이브에서 정답률 노출 버그를 찾아 지우고, 밀도를 다시 짬

URL/IA 구조 개편을 배포하기 전 점검하다가, 색인 대상인 archive.html에서 네 번째 거절 원인 중 하나와 똑같은 패턴을 발견했습니다. 항목마다 07-18부터 "참여 N명 · 정답률 X%"를 렌더링해왔는데, 그동안 조치는 집계 페이지 stats.html의 noindex뿐이었습니다 — 아카이브 개별 항목은 감사 대상에서 빠져 네 번째(08-03)·다섯 번째(08-19) 거절 기간 내내 라이브로 노출되고 있었을 가능성이 높습니다(52건 중 12건에서 확인). 해당 렌더링을 완전히 제거했습니다. 다섯 번째 거절 후 제기했던 미검증 가설 중 "페이지당 밀도"에 대한 저비용 조치로, 아카이브를 최근 14일 상세 + 이전 기록 압축 요약표 1행으로 재구성했습니다 — 총 데이터량을 늘리는 대신 반복되던 자동 생성 문단 수를 줄여 페이지당 신호 대 잡음비를 높이는 방향입니다.

9월 정답 풀 갱신, 배포까지 확인

월간 파이프라인이 09-01에 정상 완료됐습니다. 정답 풀이 3,110개(애니 800+캐릭터 2,310)에서 3,112개(애니 800+캐릭터 2,312)로 소폭 늘었고, 유사도 행 파일도 새 풀 기준으로 다시 생성됐습니다. 라이브 사이트가 받는 데이터 파일을 저장소와 직접 대조해 배포까지 반영된 것을 확인했습니다 — 배포가 늦어지면 아카이브가 플레이어가 본 적 없는 정답을 기록하는 문제가 과거에 있었는데, 이번엔 그 간극이 없었습니다. 신규 편입분의 번역 완료 여부는 원본 파일이 로컬에 없어 이 기록에서는 확인하지 못했습니다.

메뉴가 가리키던 곳, 그리고 첫 화면

전날 만든 게임/AI 분석/데이터 3축 메뉴를 다시 열어보니, 「데이터」 그룹의 링크 세 개가 전부 검색엔진에서 제외해 둔 페이지였습니다. 하필 그 둘은 예전에 거절 사유가 됐던 목록형 페이지였고, 결국 사이트에서 가장 잘 보이는 자리에 그걸 올려둔 셈이 됐습니다. 실제로 데이터를 다룬 분석 글 다섯 편은 정작 메뉴에 없었습니다. 새 글을 쓰지 않고 자리만 바꿔 해결했습니다. 같은 날 언어 선택 화면도 손봤습니다. 263자짜리 화면이 사이트의 첫인상이자 검색엔진이 보는 대표 주소였는데, 무엇을 계산해서 만든 사이트인지 한 줄도 없었습니다. 그리고 한국어 브라우저로 "Continue in English"를 누르면 도착하자마자 "한국어로 플레이하시겠어요?" 배너가 뜨는 문제가 있었습니다 — 선택을 아무 데도 기록하지 않았기 때문입니다.

한국어를 루트에서 내리고, 홈을 다시 짰습니다

그동안 한국어판이 사이트 루트를 차지하고 영어판만 /en/ 아래에 있었습니다. 영어권 사용자가 도메인을 직접 입력하면 한국어 화면을 보게 되는 구조였고, 검색엔진에 "이 사이트의 기본 언어는 무엇인가"를 말해줄 중립 주소도 없었습니다. 한국어판을 /ko/로 옮기고 루트를 언어 선택 화면으로 새로 만들었습니다. 자동 리다이렉트는 쓰지 않았습니다 — 예전에 리디렉션으로 중복 색인 사고를 낸 적이 있고, 검색엔진도 권장하지 않습니다. 기존 주소는 전부 영구 이동으로 연결해 예전 링크가 깨지지 않게 했습니다. 홈도 함께 다시 짰습니다. 게임은 그대로 첫 번째 자리에 두되, 그 아래에 게임·AI 분석·데이터 세 축을 같은 크기로 놓고, 유사도를 어떻게 계산하는지 보여주는 순서도를 넣었습니다.

6.7배로 늘려도 판정은 같았습니다

네 번째 거절의 원인이라고 진단했던 여섯 가지를 전부 조치하고 신청했습니다. 읽을거리로 쓴 글은 5,385자에서 35,832자로 6.7배가 됐고, 정형 페이지는 색인 대상에서 뺐고, 아카이브 해설은 데이터 조건에 따라 갈라지도록 다시 썼습니다. 결과는 1~4차와 똑같은 사유의 거절이었습니다. 여섯 원인이 재발하지 않았다는 건 다시 재어 확인했습니다. 그러면 남는 질문은 "무엇이 새로 고장 났는가"가 아니라 "지금까지의 방향이 애초에 이 판정을 뒤집을 수 있는 종류였는가"입니다. 그래서 글을 더 채우는 대신 사이트 구조 자체를 손보는 쪽으로 방향을 바꿨습니다. 자세한 이야기는 회고 글에 적었습니다.

장르 가이드를 전수 데이터로 다시 쓰고, 두 편을 더했습니다

로맨스·이세계 가이드는 작품 한 편을 기준으로 잡고 그 이웃만 보여주는 구조였습니다. 장르 전체를 다시 재보니 틀린 내용이 세 군데 나왔습니다. 로맨스 편의 기준작으로 쓴 작품은 애초에 로맨스 태그가 없었고, "0.55~0.65면 학원 로맨스 계열"이라고 쓴 기준은 132편 전수로 보면 성립하지 않았으며(학원 로맨스끼리는 0.771~0.855였습니다), 표에는 이미 교정한 옛 번역 제목이 남아 있었습니다. 세 가지를 정정하고 각각 1.4천자에서 3.8천자로 다시 썼습니다. 이어서 스포츠 편과 초자연 편을 새로 썼습니다. 스포츠는 다른 장르와 잘 뭉쳐서가 아니라 바깥이 무너져서 어려운 장르라는 게 열 개 장르를 같은 방식으로 재보고 나서야 드러났습니다.

데이터를 캐서 분석 글 다섯 편을 썼습니다

계산해 둔 유사도를 여러 각도로 훑는 조사 스크립트를 만들고, 결과가 실제로 갈리는 축만 골라 글로 옮겼습니다. 평점과 유사도는 599,852쌍에서 상관이 +0.004로 사실상 무관했고, 794편 중 780편에서는 소속 캐릭터가 가장 가까운 다른 작품보다 가까웠습니다. 장르보다 테마 쪽이 난이도를 두 배 이상 크게 갈랐습니다. 캐 봤지만 글이 되지 못한 축도 있습니다. 극장판이 더 어려울 거라는 짐작은 수치상 성립하지 않았고, 조연이 주연보다 소속작에 가깝게 나온 건 계산 방식이 만든 차이로 보여 게임 팁으로 쓰지 않았습니다. 그리고 이미 공개한 페이지 여섯 곳의 주장이 틀렸다는 것도 이때 드러났습니다 — "줄거리 설명이 짧으면 유사도가 낮게 나온다"고 적어뒀는데, 재보니 짧은 쪽이 오히려 높았습니다. 재보지 않고 쓴 문장이었습니다. 전부 정정문으로 바꿨습니다.

정답 풀이 3,108개에서 142개로 무너진 날

매월 자동으로 도는 데이터 갱신이 정답 풀을 3,108개에서 142개로 줄여놓은 채 완주하고 커밋까지 했습니다. 원인은 데이터를 받아오던 중간 API가 아니라 그 상류였습니다 — 캐시에 있던 요청만 응답이 오고 나머지는 전부 실패하는 상태였는데, 실패가 산발적이 아니라 결정론적이라 재시도를 아무리 늘려도 뚫리지 않았습니다. 중간 계층을 걷어내고 공식 API로 갈아탔습니다. 식별자가 같아서 그동안 쌓은 번역 자산을 그대로 재사용할 수 있었고, 이용 약관상으로도 정식 이용 형태가 됐습니다. 같은 사고가 조용히 반복되지 않도록, 수집량이 목표의 90%에 못 미치면 파일을 아예 쓰지 않고 실패로 끝내는 방어선을 넣었습니다.

받아야 할 데이터를 95MB에서 0.18MB로

게임을 열 때 브라우저가 유사도 데이터 전량을 받고 있었습니다. 원본 95MB, 압축 전송으로도 34MB 남짓이었고, 받은 뒤에는 그걸 통째로 파싱해 합치느라 화면이 몇 초씩 멈췄습니다. 모바일 회선에서는 수십 초에 데이터 요금까지 드는 구조였습니다. 필요한 건 사실 그날 정답 한 줄뿐이라, 정답 후보마다 미리 한 줄씩 파일로 만들어 두고 그것만 받도록 바꿨습니다. 32KB입니다. 값이 달라지지 않는다는 건 표본이 아니라 가능한 모든 추측 조합을 대조해 확인했습니다 — 930만 쌍이 전부 일치했습니다.

공략 가이드·정답 통계 페이지, 정답률 수집 시작

실제 유사도 데이터를 직접 분석해 만든 공략 가이드를 공개했습니다. "정답이 원피스였던 날 나루토는 0.552, 하이큐는 0.000" 같은 실측 수치로 유사도 구간 기준표와 플레이 워크스루를 구성했습니다. 매일 쌓이는 정답 기록을 집계해 장르 분포·작품/캐릭터 비율을 보여주는 통계 페이지도 추가했고, 매일 자동 갱신됩니다. 그리고 Cloudflare Worker + D1로 일별 참여자·정답·포기 수를 집계하는 API를 배포했습니다 — 데이터가 쌓이면 날짜별 정답률을 공개할 예정입니다.

SEO 기술 부채 청산 — 리디렉션과 sitemap의 발견

3차 심사도 같은 사유로 거절당해 이번엔 크롤러 관점에서 사이트를 전수 점검했습니다. 두 가지 문제를 찾았습니다. 첫째, 내부 링크와 캐노니컬이 .html 확장자 URL을 가리키는데 호스팅이 이를 임시(307) 리디렉션해 중복 색인과 캐노니컬 오류를 만들고 있었습니다 — 사이트 전체 50개 파일의 내부 URL을 확장자 없는 형태로 통일했습니다. 둘째, 구글이 sitemap을 3주째 다시 읽지 않고 있었는데, 과거 버전에 있던 잘못된 changefreq 값 (quarterly) 오류가 원인이었습니다. 갱신 자동화 스크립트의 정규식 버그도 함께 발견해 수정했고, Search Console에서 sitemap 재제출과 신규 페이지 색인 요청까지 마쳤습니다.

문의 이메일 주소 변경

문의 접수 이메일을 hograywm@gmail.com으로 변경했습니다. 메인 페이지 하단 안내문, 문의 페이지, 개인정보처리방침 페이지의 이메일을 KO/EN 모두 일괄 갱신했습니다.

robots.txt 정리, 문의 페이지, 배포 설정 정비

noindex 메타 태그와 충돌하고 있던 robots.txt의 Disallow 규칙을 제거해 검색엔진이 noindex 처리를 정상적으로 읽을 수 있게 했습니다. 독립된 문의 페이지를 새로 만들었고, Cloudflare Workers 배포 설정 파일이 없어서 배포가 막혔던 문제도 wrangler.jsonc를 추가해 해결했습니다.

문제를 키웠던 시도, 그리고 방향 전환

1차 대응으로 만든 목록 페이지에 더해 장르별 페이지 36개를 추가했는데, 2차 심사에서 다시 "가치가 별로 없는 콘텐츠"로 거절당했습니다. 원인을 분석해보니 MAL(MyAnimeList) 데이터를 그대로 재포장한 페이지를 34배로 늘린 셈이었습니다. 해당 페이지 전체를 noindex 처리해 검색 노출에서 제외하고, 대신 이 사이트에서만 나오는 매일 갱신되는 정답 아카이브를 새로 만들었습니다. about 페이지에는 유사도 가중치를 어떻게 실측 테스트해서 정했는지 데이터를 그대로 공개했습니다. 이 과정에서 about/ privacy/archive 페이지의 리다이렉트 루프 SEO 이슈도 함께 발견해 고쳤습니다.

"콘텐츠 가치 부족" 거절과 목록 페이지 추가

AdSense 심사에서 "가치가 별로 없는 콘텐츠"라는 사유로 거절당했습니다. 게임 하나만 덩그러니 있는 구조가 문제라고 보고, 전체 작품·캐릭터 목록 페이지(wordlist)를 만들어 대응했습니다. 지금 돌이켜보면 이 대응 방향 자체가 다음 거절의 원인이 되었습니다.

월간 자동 갱신 + Discord 알림

데이터 자동 갱신이 매월 1일 정기적으로 돌아가도록 스케줄을 확정하고, 갱신 결과(신규·삭제 작품)를 Discord로 알려주는 알림을 추가했습니다. 초기 버전은 기존 항목을 신규로 잘못 판정하는 버그가 있어 바로 다음 날 고쳤습니다.

CLS(레이아웃 밀림)와의 씨름

페이지 로드 중 요소가 밀리는 CLS 문제를 잡기 위해 여러 번 갈아엎었습니다. 스크롤바를 고정하고, 결과 영역의 레이아웃 순서를 바꾸고, 그래도 남은 미세한 밀림을 다시 잡는 과정을 며칠에 걸쳐 반복했습니다. 같은 시기에 유사도 계산 로직도 처음부터 다시 설계했는데, 이후 정착된 "구조 70% + 시놉시스 30%" 가중치 방식이 여기서 출발했습니다.

자동 갱신 파이프라인 구축

GitHub Actions로 데이터 수집·번역·유사도 계산을 자동화하는 CI/CD를 처음 구축했습니다. 이때 만든 파이프라인 골격이 지금도 매월 자동 실행되는 데이터 갱신 워크플로우의 기반입니다.

정답 풀 확장과 청크 분할

항목당 유사도 이웃 저장 개수를 100개에서 3,000개까지 단계적으로 늘렸습니다. 데이터가 커지면서 Cloudflare Pages의 파일당 25MB 제한에 걸려, 유사도 JSON을 5개 파일로 쪼개 나눠 저장하도록 구조를 바꿨습니다. 이 시기에 영어 버전과 캐릭터 번역도 함께 추가했습니다.

첫 배포

애니 시맨틀을 처음 공개했습니다. 광고 심사에 필요한 메타 태그와 ads.txt, 기본 SEO 태그를 함께 세팅하며 시작했습니다.