🐛 이미지 검증: data URI 썸네일 오판과 과잉 수정 범위 수정 - #18
Closed
Kevinlee7250 wants to merge 966 commits into
Closed
Kevinlee7250 wants to merge 966 commits into
Kevinlee7250 wants to merge 966 commits into
Conversation
측정 결과 발행 글 431건 중 175건(40.6%)이 들어오는 내부 링크 0개였고, 최근 고아 50건은 전부 8월 발행분이었습니다. 오래된 글은 새 글이 계속 링크해 인바운드가 쌓이는데(최대 17) 새 글은 아무도 가리키지 않는 구조입니다.⚠️ 기대치를 분명히 해둡니다: 같은 측정에서 색인 검사 20건 중 고아 10건과 링크 있는 글 10건이 똑같이 미색인이었습니다. 내부 링크를 만든다고 색인이 풀린다는 근거는 없습니다(표본이 전부 미색인이라 인과 판정 불가). 이 작업은 색인 해결책이 아니라 사이트 구조상 정당한 개선입니다. 발행된 글을 수정하므로 안전장치를 우선했습니다: - 기본 dry-run — --apply가 있어야 실제 반영 - 고아당 링크 2개, 출처 글당 최대 3개 (본문이 링크 목록이 되는 것 방지) - 관련성 점수 임계값 미만이면 링크하지 않음 (억지 링크 금지) - 본문에 자연스럽게 쓸 표현이 없으면 문장을 지어내지 않고 건너뜀 - 고아보다 먼저 발행된 글만 출처로 사용 - 관련성이 같으면 인바운드가 많은 글 우선 — 크롤 경로를 잇기 위함 - 제목·기존 앵커·script/style 안에는 삽입하지 않음 → internal_linker의 검증된 삽입 로직을 재사용 (규칙을 두 번 구현하지 않음) 테스트 13건 추가 (전체 434건 통과) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgLYqM9puPW1aqHTccbX69
- inbound-links.yml: 주 1회(토요일 08시 KST) 자동 + 수동 실행
· 링크 생성 전에 internal_link_audit로 고아 목록을 먼저 갱신
· 수동 실행 기본값은 미리보기 — 발행된 글을 수정하는 작업이므로
apply를 명시적으로 켤 때만 반영합니다 (스케줄 실행은 반영)
- 대시보드 색인 현황 위에 "내부 링크 구조" 패널
· 고아 건수·비율, 블로그별 분포, 최근 고아 10건
· 색인과의 인과가 확인되지 않았다는 점을 패널에 명시 —
숫자만 보고 원인으로 단정하지 않도록
· 미리보기 / 링크 생성 버튼 (건수 지정, 실행 전 확인)
테스트 434건 통과
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgLYqM9puPW1aqHTccbX69
고아 40.6%는 사실로 확인됐지만, 색인과의 인과는 근거를 못 찾았습니다. 링크 있는 글 10건도 똑같이 미색인이었고 표본이 전부 미색인이라 대조군이 없어 판정 자체가 불가능합니다. 도구(inbound_linker + PR #16)는 만들되 "색인 해결책"이 아니라 사이트 구조상 개선으로 위치를 명시했습니다. 반증 표에도 추가. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgLYqM9puPW1aqHTccbX69
기존 이미지 도구가 다루지 않던 빈틈을 채웁니다: fix_unlicensed_images → 저작권(화이트리스트) fix_duplicate_images → 같은 이미지 중복 image_audit(신규) → 이미지가 없는가 / 링크가 죽었는가 / 내용과 맞는가 판정 3종과 조치: missing/insufficient → 관련 이미지 첨부 broken → 교체, 대체 못 구하면 삭제 irrelevant → 교체, 대체 못 구하면 삭제 suspect → 확정 판정 전이므로 손대지 않음 관련성은 2단계입니다. 파일명·alt를 본문 핵심어와 대조해 거르고(무료), 애매한 것만 --ai로 Claude에게 확정 판정을 받습니다. 모든 이미지를 AI로 판정하면 비용이 크고, 반대로 문자열 매칭만으로 "무관"을 확정하면 오삭제가 나기 때문입니다. 오삭제 방지를 설계 원칙으로 삼았습니다 — 판단이 서지 않으면 항상 유지: - 네트워크 오류·5xx는 살아있는 것으로 간주 (404/410만 죽음으로 판정) - Content-Type이 이미지가 아닐 때만 형식 오류로 판정 - AI 호출이 실패하면 "관련 있음"으로 처리 - 비교할 핵심어가 없으면 판단하지 않음 - AI 생성 썸네일은 그 글을 위해 만든 것이므로 항상 관련 있음 교체·삭제·캡션 동기화는 fix_unlicensed_images의 검증된 함수를 재사용합니다 (화이트리스트 규칙을 두 번 구현하면 반드시 어긋납니다). - image-audit.yml: 주 1회(일요일 07시 KST) + 수동. 수동 기본값은 검사 전용. 스케줄 실행은 AI 판정 없이 돌아 broken/missing만 고칩니다 - 대시보드 패널: 검사만 / AI 관련성 판정 / 수정 실행 버튼과 결과 표시 - 테스트 22건 추가 (전체 456건 통과) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgLYqM9puPW1aqHTccbX69
첫 실행 결과 431건 중 broken 227건이 전부 오판이었습니다. 이 시스템이
만드는 썸네일은 <img src="data:image/svg+xml;base64,..."> 로 본문에
직접 박혀 있는데, check_alive가 startswith("http")만 보고 "URL 형식
아님"으로 죽은 링크 처리했습니다. 일요일 스케줄이 돌면 멀쩡한 썸네일
227장을 교체·삭제할 상태였습니다.
- check_alive: data:image/* 는 살아있음, 그 외 data URI만 broken
- _image_terms: data URI의 base64 덩어리를 파일명으로 쓰지 않음
- relevance_score: data URI 썸네일은 그 글을 위해 생성된 것이므로 1.0
- fix_post에 kinds 인자 추가, DEFAULT_FIX_KINDS에서 insufficient 제외
(282건 = 전체의 65%라 기본 적용 시 사실상 전 게시글을 수정하게 됨)
--attach-insufficient 로만 켜지고, 워크플로 주석의 기존 설명과도 일치
- 회귀 테스트 6건 추가 (총 462건 통과)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgLYqM9puPW1aqHTccbX69
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
무엇이 문제였나
이미지 검증(#17)을 처음 돌린 결과입니다.
링크 깨짐 227건은 전부 오판이었습니다. 사유가 예외 없이 "URL 형식 아님"이었고, 실제 URL을 열어 보니 전부 이렇습니다.
이 시스템이 만드는 썸네일은 파일이 아니라 본문에 직접 박힌 인라인 SVG입니다. 죽을 수가 없는데
check_alive가startswith("http")만 보고 죽은 링크로 처리했습니다.스케줄 실행은
--ai없이도broken을 자동 수정하므로, 다음 일요일에 멀쩡한 썸네일 227장을 교체·삭제할 상태였습니다.변경 내용
image_audit.pycheck_alive—data:image/*는 살아있음, 그 외 data URI만 broken_image_terms— base64 덩어리를 파일명으로 쓰지 않음 (alt만 사용)relevance_score— data URI 썸네일은 그 글을 위해 생성된 것이므로 1.0fix_post(post, audit, kinds)— 손댈 문제 유형을 호출 쪽에서 결정DEFAULT_FIX_KINDS에서insufficient제외 +--attach-insufficient플래그.github/workflows/image-audit.ymltests/test_image_audit.pyHANDOFF.mdinsufficient를 기본에서 뺀 이유
282건은 전체 431건의 65%입니다. 기본 수정 대상으로 두면 사실상 모든 게시글에 이미지를 한 장씩 밀어 넣게 되고, 이는 검증 도구가 아니라 일괄 편집입니다. 워크플로 주석은 원래도 "스케줄은 broken/missing만 고친다"고 적혀 있었는데 코드가 그와 달랐던 것이라, 코드를 주석에 맞췄습니다.
검증
추가된 회귀 테스트는 data URI가 살아있음으로 판정되는지, base64가 파일명으로 새지 않는지,
insufficient가 기본으로는 수정되지 않고 옵트인 시에만 수정되는지를 고정합니다.남은 것
insufficient282건과suspect15건은 판정만 되고 손대지 않은 상태로 남습니다. 실제로 이미지를 늘릴지는CHARS_PER_IMAGE(현재 2500자당 1장) 기준이 이 블로그에 맞는지 먼저 볼 문제라 이 PR에서는 정하지 않았습니다.🤖 Generated with Claude Code
https://claude.ai/code/session_01RgLYqM9puPW1aqHTccbX69
Generated by Claude Code