사이드프로젝트 slecs

README 외부 이미지 과다로 생기는 렌더링 누락 해결

목차

머지 직후 README 확인하는 건 습관처럼 해온 일인데, 이날은 페이지 중간이 그냥 뚫려 있었음. 처음엔 마크다운 문법이 깨진 줄 알았다. raw 파일 열어보면 이상 없고, 렌더된 페이지에서만 이미지 절반이 X 박스거나 placeholder조차 없는 상태. 개발자 도구 네트워크 탭을 열기 전까지는 원인이 전혀 안 잡혔음.

원인 - GitHub의 외부 이미지 프록시 동작 방식

GitHub은 README에 삽입된 외부 이미지를 직접 브라우저에 때려주지 않음. camo라는 자체 이미지 프록시를 거쳐 캐싱한 다음 서빙한다. 이 구조 덕분에 HTTPS 혼합 콘텐츠 경고가 안 뜨고, 원본 호스팅이 요청자 IP를 못 보는 프라이버시 이점도 생김.

문제는 한 페이지에 외부 이미지가 많을수록 camo가 초기 로딩 시 원본 도메인들을 병렬로 긁어야 한다는 거다. 이 과정에서 특정 도메인이 느리거나 rate-limit을 걸면 타임아웃이 나고, 해당 이미지는 조용히 누락됨. 에러 메시지 없음. 그냥 빈 공간.

이날 카운트해보니 단일 README에 외부 도메인 이미지가 38개였음. 블로그 스크린샷, 이슈 트래커 첨부파일, 외부 CDN에 올린 다이어그램 등이 섞여서 별 생각 없이 쌓인 결과. 레포를 오래 관리하다 보면 이런 게 조금씩 누적됨. 한 번에 38개를 박은 게 아니라.

항목 이전 이후
외부 이미지 38개 6개
평균 첫 페인트 4.2s 1.1s
렌더 누락 가끔 발생 없음

첫 페인트가 4초대였다는 게 더 충격이었음. README 한 장 로드가 SPA 초기 렌더보다 느렸던 거다.

정리한 방식과 기준

기계적으로 다 내부화하면 관리가 오히려 번거로워짐. 외부를 유지해야 의미 있는 것과 아닌 것을 구분하는 게 핵심이었다.

내부로 옮긴 것

docs/images/ 폴더를 만들고 핵심 아키텍처 다이어그램, 설치 흐름 스크린샷처럼 "이 레포를 설명하는 데 필수인 시각자료"를 PNG로 직접 커밋했음. 파일 크기 문제는 있는데, 다이어그램급 PNG는 보통 100KB 이하라 레포 용량 부담이 거의 없다. 대신 얻는 건 명확함 - 레포가 살아있는 한 이미지도 살아있음.

외부 유지한 것

빌드 상태, 커버리지, 라이선스 같은 동적 배지는 그대로 뒀음. 이건 실시간 값을 보여줘야 의미가 있고, 내부화하면 오히려 정보가 stale해짐. 배지 서비스(shields.io 류)는 이미 CDN을 갖추고 있어서 camo 프록시 부하도 적음.

링크 텍스트로 강등한 것

외부 블로그 스크린샷, 이슈 링크 썸네일 등. 인라인 이미지로 있어야 할 이유가 없었음. 링크 텍스트로 바꿔도 정보 손실이 없는데 괜히 외부 의존을 하나 더 만들고 있었던 것.


외부 이미지 개수를 한 줄로 체크하는 방법:

# 외부 이미지 카운트
grep -oE '!\[.*\]\(https?://[^)]+\)' README.md | wc -l

이걸 PR 단계에서 가드로 거는 것도 검토 중이다. 아래처럼 간단한 스크립트로 임계값 초과 시 경고를 띄우는 정도면 충분함. 강제 차단은 배지까지 막혀서 득보다 실이 많다.

#!/bin/bash
# .github/scripts/check-readme-images.sh
COUNT=$(grep -oE '!\[.*\]\(https?://[^)]+\)' README.md | wc -l)
LIMIT=10

if [ "$COUNT" -gt "$LIMIT" ]; then
  echo "경고: README 외부 이미지 ${COUNT}개 (권장 ${LIMIT}개 이하)"
  echo "내부화 또는 링크 텍스트 전환 검토 필요"
  exit 1
fi

GitHub Actions에 붙이면 PR마다 자동으로 체크됨. exit 1을 경고로만 쓰고 싶으면 exit 0으로 바꾸면 됨 - 워닝은 띄우되 머지는 막지 않는 구성.

문서도 인프라다

이번 일에서 다시 확인한 게 있음. 문서가 깨지는 건 코드가 깨지는 것보다 발견이 훨씬 늦다. 본인 브라우저엔 camo 캐시가 이미 올라와 있으니 멀쩡하게 보임. 처음 보는 사람, 캐시 없는 사람, 시크릿 창에서 여는 사람만 깨진 걸 경험한다. 그러니까 사실상 README 관리자는 그 문제를 가장 나중에 알게 되는 사람임.

시크릿 창에서 주요 문서를 한 번씩 열어보는 걸 체크리스트에 넣기로 했음. 귀찮긴 한데, 안 하면 못 잡는다.

더 넓게 보면, 외부 의존을 문서에 쌓는 것도 SPOF를 추가하는 거다. 이미지 호스팅 도메인 하나가 죽으면 그 도메인에 연결된 이미지들이 전부 X 박스가 됨. 텍스트는 그대로지만 독자 입장에서는 문서 전체가 망가진 인상을 받는다. 코드 의존성이야 package.json이나 go.mod에 명시적으로 기록되고 lock 파일로 버전 고정이라도 되는데, 문서 안 외부 URL은 어느 날 갑자기 404가 나도 아무도 모름.

README 이미지 정리는 작업 자체는 30분이면 됨. 근데 그 결과로 문서 신뢰성이 상당히 올라갔다는 체감이 있었음. 빠른 작업 대비 효과가 확실한 편.

댓글 0

첫 댓글 달아줘.