1. 이 예제가 맞는 사이트인가요?

쭈니박스는 about.html을 /about으로 공개하는 평면 HTML 구조입니다. 페이지를 복사해 글을 쓸 때 이전 제목이나 canonical이 남고, 제목의 id를 고친 뒤 목차 링크는 그대로일 수 있습니다. 화면이 멀쩡해도 이런 실수는 놓치기 쉽습니다.

이 예제는 그 작업 흐름을 위해 만들었습니다. 지정 폴더의 *.html과 sitemap.xml을 읽고, HTML의 a·link·img·script가 가리키는 로컬 파일도 확인합니다. 다른 도메인에는 요청하지 않습니다.

  • 맞는 경우: 공개 HTML이 폴더 바로 아래에 있고, 각 파일이 확장자 없는 주소와 대응하는 소개 사이트나 작은 문서 사이트.
  • 그대로 적용하면 안 되는 경우: SPA 가상 경로, /about/index.html 같은 디렉터리 라우팅, 다국어 하위 경로, 서버가 요청마다 만드는 페이지, 사이트맵 인덱스.
  • 별도 확인: CSS 안의 이미지, srcset, JavaScript가 만든 링크, 외부 링크, HTTP 응답. 이 예제의 검사 대상이 아닙니다.

404.html과 robots 메타에 noindex가 있는 페이지는 사이트맵 대조에서 제외합니다. 리디렉션 설정과 HTTP 헤더의 noindex는 읽지 않습니다. 자신의 배포 규칙이 다르면 오류가 아니라 도구의 가정이 맞지 않는 것일 수 있습니다.

2. 실제 사이트 대신 작은 실험 폴더를 만듭니다

Windows 탐색기에서 새로 만들기 → 폴더로 site-check-demo를 만들고, 그 안에 public 폴더를 만드세요. 다운로드한 예제는 바깥 폴더에 둡니다. 편집기에서 아래 두 파일을 UTF-8로 저장합니다. 예시의 example.com은 설명용 주소입니다.

site-check-demo/
  check-static-site.py
  public/
    index.html
    sitemap.xml

index.html: 열리지만 불완전한 첫 버전

<!doctype html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>나의 작은 사이트</title>
  <link rel="canonical" href="https://example.com/old">
</head>
<body>
  <h1>나의 작은 사이트</h1>
  <p>직접 만든 도구의 사용법을 기록합니다.</p>
  <a href="/guide">사용법 읽기</a>
  <a href="#contact">문의 방법</a>
</body>
</html>

sitemap.xml: 홈 하나만 등록

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url><loc>https://example.com/</loc></url>
</urlset>

파일 이름이 index.html.txt로 저장되지 않았는지 확인하세요. 탐색기의 보기 → 표시 → 파일 확장명을 켜면 전체 확장자를 볼 수 있습니다. 여기서는 링크 테스트를 위해 파일을 새로 만들 뿐 기존 사이트를 덮어쓰지 않습니다.

3. 실행 결과를 수정할 위치와 연결합니다

탐색기에서 site-check-demo를 연 뒤 빈 공간 우클릭 → 터미널에서 열기 → PowerShell을 선택합니다. Python이 이미 설치된 환경에서 실행하세요. macOS·Linux에서는 같은 폴더의 터미널에서 py -3 대신 python3를 씁니다.

py -3 .\check-static-site.py .\public --origin https://example.com
$LASTEXITCODE

py를 찾을 수 없으면 파일 오류가 아니라 실행 환경 문제입니다. Python 공식 다운로드에서 사용 중인 운영체제의 설치 안내를 확인합니다. 이 실습을 위해 npm이나 Python 패키지를 설치할 필요는 없습니다.

index.html: META expected one non-empty description
index.html: CANONICAL expected this page URL, not another URL
index.html: LINK local target does not exist (line 11)
index.html: ANCHOR target id does not exist (line 12)
Checked 1 HTML files; 4 issue(s).
HTTP, JavaScript rendering, external links and content quality: NOT CHECKED.

위 출력은 아래 수정본과 함께 2026년 9월 22일 독립 실험 폴더에서 실행해 대조한 결과입니다. 종료 코드는 1입니다. 링크 오류의 line은 HTML 파일의 줄 번호입니다. 주소에 비공개 값이 들어 있을 수 있어 링크 원문은 출력하지 않습니다. 오류 개수는 서로 독립적인 문제의 수와 항상 같지는 않습니다. 빠진 파일 하나가 여러 링크에서 반복 보고될 수 있습니다.

META → 설명이 빠졌습니다
head에 이 페이지를 설명하는 description을 넣습니다. 설명의 존재를 검사할 뿐 검색 결과에 그대로 표시된다고 보장하지 않습니다.
CANONICAL → 홈이 다른 주소를 대표로 선언했습니다
이 예제에서 홈의 대표 주소는 https://example.com/입니다. 현재 파일의 URL로 바꾸며, 모든 페이지에 홈 주소를 복사하지 않습니다.
LINK → /guide에 대응하는 파일이 없습니다
실제 사용법 글을 만들어 연결하거나 이미 있는 본문의 관련 부분으로 연결합니다. 통과를 위해 빈 페이지를 만드는 것은 해결이 아닙니다.
ANCHOR → #contact로 이동할 요소가 없습니다
존재하는 id로 바꾸거나 해당 정보를 담은 섹션을 만듭니다. 클릭해도 아무 변화가 없던 링크를 이 단계에서 발견할 수 있습니다.

4. 내용을 채우고 다시 실행합니다

실험 폴더의 index.html만 아래 내용으로 바꿉니다. 새 페이지를 추가하지 않고 홈 안에 실제 사용 순서와 수정 기록을 썼습니다. 사이트맵은 같은 홈 주소이므로 수정할 필요가 없습니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>나의 작은 사이트</title>
  <meta name="description" content="정적 파일을 검사하는 예제의 사용 순서와 수정 기록입니다.">
  <link rel="canonical" href="https://example.com/">
</head>
<body>
  <h1>나의 작은 사이트</h1>
  <p>직접 만든 도구의 사용법을 기록합니다.</p>
  <a href="#guide">사용 순서</a>
  <a href="#changes">수정 기록</a>
  <section id="guide">
    <h2>사용 순서</h2>
    <p>공개 파일을 별도 폴더에 모으고, 점검 예제로 링크와 메타데이터를 검사합니다.</p>
  </section>
  <section id="changes">
    <h2>수정 기록</h2>
    <p>없는 문서로 가던 링크를 홈 안의 설명으로 연결했습니다.</p>
  </section>
</body>
</html>
Checked 1 HTML files; 0 issue(s).
HTTP, JavaScript rendering, external links and content quality: NOT CHECKED.

같은 명령의 종료 코드는 이제 0입니다. 2는 잘못된 인자나 존재하지 않는 폴더처럼 실행 조건부터 고쳐야 한다는 뜻입니다. 운영 주소는 --origin에 경로나 계정 정보 없이 넣습니다. 이 옵션은 비교 기준이며 그 주소에 접속하지 않습니다.

다음으로 sitemap의 홈 주소를 /missing으로 바꿔 보세요. HTML이 그대로여도 SITEMAP 오류가 생깁니다. 반대로 새 글을 만들어 놓고 사이트맵에 빠뜨려도 이 예제는 차이를 보고합니다. 실험 뒤에는 주소를 원래대로 돌립니다.

점검 코드 자체도 시험해 보고 싶다면

자동 테스트 예제 (.py)를 점검 파일과 같은 폴더에 저장하고 py -3 -B ./test-static-site.py로 실행할 수 있습니다. 메타데이터 중복·빈 값, 없는 파일·앵커, 한글 앵커, 사이트맵 불일치, UTF-8 오류, 종료 코드 등 13개 테스트를 포함합니다. 테스트가 만든 임시 폴더만 사용하며 원래 사이트는 수정하지 않습니다.

Python 3.10.1에서 본문 실습 두 버전의 출력과 자동 테스트를 대조했습니다. 최소 요구 버전은 사용 API 기준 3.9이며 모든 운영체제·Python 버전을 실행해 본 것은 아닙니다.

5. 오류 0개는 출발점이지 공개 완료가 아닙니다

이 저장소에서도 예제를 실행해 로컬 파일 간 대응을 확인했습니다. 하지만 Pages의 배포 폴더가 잘못됐거나 이전 버전이 게시됐다면, 같은 파일 검사 결과로 운영 사이트가 정상이라고 말할 수 없습니다. 이 차이 때문에 파일 검사와 실제 주소 검사를 나눴습니다.

  1. 운영 응답에서 글의 고유한 문장을 찾습니다. 로컬에만 새 글이 있는지 구분합니다.
  2. 정상 주소·리디렉션·없는 주소를 따로 요청합니다. 화면이 404처럼 보여도 HTTP 200일 수 있습니다.
  3. 키보드와 작은 화면으로 실제로 읽습니다. 표나 예제가 잘리지 않고 링크 목적이 분명한지 확인합니다.

설명의 정확성, 독창성, 비밀정보 노출 여부, 외부 서비스의 권한, 검색 색인, 광고 심사 결과는 판정하지 않습니다. 이 도구는 HTML 표준 검사기도 아닙니다. Python HTMLParser 문서에서 설명하듯 태그를 읽는 것과 문법 적합성을 검증하는 것은 다릅니다.

파일과 공개 URL의 대응은 Cloudflare Pages의 HTML 제공 규칙을 기준으로 삼았습니다. 파일 배치나 호스팅을 바꾼다면 이 예제의 가정부터 다시 살펴보세요. 도구가 통과하도록 사이트를 억지로 바꾸는 순서가 되어서는 안 됩니다.