시즌 번호가 적힌 리포트는 166편 중 24편 — 제목 표기 규약 전수 감사

칼라톤의 시즌 리포트는 사람이 쓰지 않는다. 시즌이 끝나면 파이프라인이 데이터베이스를 읽어 국문·영문 한 쌍의 HTML을 찍고, 목록 페이지에 카드를 한 장 얹는다. 지금까지 그렇게 쌓인 국문 리포트가 166편, 영문까지 더하면 332편이다.

이 시리즈를 쓰면서 리포트를 수백 번 열었는데, 그때마다 사소하지만 반복되는 불편이 있었다. 브라우저 탭만 봐서는 지금 어느 시즌을 보고 있는지 알 수 없다는 것이다. 탭 세 개에 똑같이 "청순의 정점"이라고 적혀 있으면, 어느 탭이 시즌 21이고 어느 탭이 시즌 191인지 URL을 다시 눌러 봐야 한다. 반대로 트레이딩 리포트는 탭 제목이 "시즌1: AI 트레이더, 비트코인 대전"이라 한눈에 구분된다.

왜 모듈마다 다를까. 리포트를 찍는 코드는 하나의 저장소 안에 있고 바깥 껍데기도 똑같이 생겼는데, 제목만 제각각인 이유가 있을까. 이번에는 그 제목 자체를 데이터로 삼았다. 국문 166편·영문 166편, 합계 332편의 <title>·h1·히어로 윗줄·메타 설명·JSON-LD 헤드라인을 전부 꺼내 서로 맞댔다. 질문, 방법, 결과, 한계의 순서로 적는다.

질문 — 규약은 하나인가, 모듈 수만큼인가

검증 가능한 형태로 다듬으면 질문은 셋이다. 첫째, 바깥 틀(제목의 고정 부분)은 전 모듈이 같은 공식을 쓰는가. 둘째, 안쪽 작품명은 어떤 문형을 쓰며 시즌 번호를 담는가. 셋째, 그 결과로 제목만으로 시즌이 구분되는가.

셋째 질문이 실무적으로 가장 중요하다. 제목은 검색 결과에 뜨는 줄이고, 브라우저 탭에 들어가는 줄이고, 다른 사이트가 우리 리포트를 링크할 때 기본으로 가져가는 줄이다. 같은 제목을 가진 페이지가 스물한 장이면 그 스물한 장은 서로의 색인 품질을 깎아먹는다.

방법 — 332편을 파싱해 다섯 자리를 대조한다

집계 대상은 2026년 10월 6일(KST) 기준 저장소의 공개 리포트 전부다. 외부 API 없이 저장소의 HTML만 읽었다. 각 파일에서 다섯 자리를 꺼냈다 — <title>, 본문 <h1>, 히어로 상단의 작은 윗줄(이하 '키커'), meta description, JSON-LD의 headline. 여기에 목록 페이지 네 장(리포트 대문 국문·영문, 인물 전체목록 국문·영문)의 카드 텍스트 332건을 더해 대조군으로 썼다.

모듈국문 편수고유 제목제목에 시즌 번호가시 텍스트에 시즌 번호작품명 평균 길이
AI 인물 생성14211006.7자
AI 유머 대결151541515.4자
AI 커플매칭666617.7자
AI 트레이딩222221.0자
AI 마인드게임111119.0자
합계1663513248.1자

'가시 텍스트에 시즌 번호'는 제목뿐 아니라 페이지 본문 어디에든 "시즌 N"이라는 표기가 있는지를 뜻한다. 유머·마인드게임은 제목에 없어도 키커가 번호를 적어 주기 때문에 제목 열보다 수가 크다. 인물 리포트는 두 열이 모두 0이다 — 142편 전부, 페이지를 끝까지 읽어도 몇 번째 시즌인지 알 수 없다. 번호는 URL과 이미지 경로에만 있다.

결과 ① — 바깥 틀은 166편 전부 같다

먼저 좋은 소식. 제목의 고정 부분은 예외가 없다. 국문 166편이 전부 {작품명} — {모듈 라벨} 시즌 리포트 | 칼라톤 한 공식을 따르고, 모듈 라벨도 다섯 종(AI 인물 생성·AI 유머 대결·AI 커플매칭·AI 트레이딩·AI 마인드게임)에서 한 글자도 흔들리지 않는다. 영문 166편도 마찬가지로 {작품명} — {모듈 라벨} — Season Report | ColorTone를 전수로 지킨다.

딸려 있는 자리들도 거의 깨끗하다. og:title이 <title>과 다른 페이지는 332편 중 0편, 목록 페이지의 카드 텍스트가 해당 리포트의 h1과 다른 경우도 국문 166·영문 166 모두 0건이다. JSON-LD headline이 '제목에서 브랜드 꼬리만 뗀 값'과 다른 페이지는 331편 중 0편, 나머지 한 편이 다음 단락의 유일한 예외다. 한 번 정해진 자리는 대체로 정확히 복제된다.

그 한 편은 영문 유머 시즌 2다. 제목 원문이 Bright &amp;amp; Pretty AI Humor로 적혀 있다 — 앰퍼샌드가 두 번 이스케이프됐다. 브라우저 탭과 검색 스니펫에는 "Bright &amp; Pretty AI Humor"라는 글자가 그대로 뜬다. 같은 페이지의 h1과 JSON-LD headline, 목록 카드는 모두 정상이라 화면 본문에서는 보이지 않고, 오직 제목 자리 둘(<title>·og:title)에서만 어긋난다. 332편의 제목 가운데 앰퍼샌드를 담은 제목은 이 한 편뿐이니, 이 결함은 기회가 한 번 있었고 그 한 번에 발생했다. 비(非)ASCII 문자가 대부분인 국문 제목 166편에서는 한 건도 나오지 않았다.

다만 국문과 영문의 공식이 완전히 같지는 않다. 국문 제목의 줄표(—)는 166편 전부 정확히 한 개인데, 영문은 모듈 라벨 뒤에 줄표가 한 번 더 들어가 157편이 두 개, 작품명 안에 줄표를 품은 유머 9편은 세 개다. 영문 제목 AI Humor — July 2026, Week 1 — AI Humor Battle — Season Report | ColorTone은 줄표 세 개로 네 토막이 난다. 같은 자료를 국문은 "2026년 7월 1주차 AI 유머"라는 한 덩어리로 적는다.

결과 ② — 안쪽은 문형 6종으로 갈린다

바깥이 같은 만큼 안쪽은 다르다. 166편의 작품명을 문형으로 묶으면 여섯 가지가 나온다.

문형예시편수쓰는 모듈
{테마}의 정점청순의 정점142인물
시즌N: {부제}시즌2: AI 트레이더, 비트코인 대전9매칭 6 · 트레이딩 2 · 마인드게임 1
{연·월·주차} AI 유머2026년 8월 3주차 AI 유머9유머
AI 유머 시즌 NAI 유머 시즌 273유머
{수식어} AI 유머여름 한 컷 AI 유머2유머
AI 유머 배틀 시즌 NAI 유머 배틀 시즌 11유머

여섯 중 넷이 유머 모듈 하나에서 나왔다. 그러나 이것은 난맥이 아니라 연표다. 발행일 순으로 세우면 문형이 뒤섞이지 않고 한 번씩만 바뀐다 — 2026-06-20 시즌 1이 "AI 유머 배틀 시즌 1", 06-26과 07-03의 두 시즌이 수식어형("밝고 예쁜 AI 유머", "여름 한 컷 AI 유머"), 07-10부터 09-03까지 아홉 시즌이 전부 주차형, 그리고 09-11의 시즌 23부터 세 시즌이 번호형이다. 네 구간이 날짜로 깔끔하게 끊긴다.

마지막 전환에는 저장소에 남은 근거가 있다. html2pdf/src/humor-season-title.js는 2026-09-06에 "제목에서 날짜 삭제" 정책이 적용되면서 모든 신규 유머 시즌의 제목이 "AI 유머" 하나로 같아졌고, 그래서 제목만으로 시즌을 구분해야 하는 표면에서는 시즌 번호를 붙인다고 적고 있다. 파일 머리말은 그 이유를 "시즌마다 <title>·앵커 텍스트가 같아지면 색인 품질이 떨어진다(중복 콘텐츠)"라고 명시한다. 우리 저장소에서 제목 중복 문제를 정면으로 다룬 규칙은 이 파일 하나뿐이다.

결과 ③ — 인물 리포트 142편이 제목 11개를 나눠 쓴다

그 규칙이 없는 쪽이 어떻게 되는지는 바로 옆 모듈이 보여 준다. 인물 생성 리포트 142편의 제목은 11종뿐이고, 11종 모두 "{테마}의 정점"이다.

제목같은 제목을 쓰는 리포트제목같은 제목을 쓰는 리포트
청순의 정점21편카리스마의 정점9편
예쁨의 정점21편미남의 정점9편
귀여움의 정점19편청량의 정점9편
매력의 정점16편훈훈함의 정점8편
아름다움의 정점15편톰보이시의 정점6편
소년미의 정점9편합계142편

메타 설명도 같이 복제된다. 인물 리포트 142편의 meta description은 11종뿐이다 — 테마가 같으면 설명문이 바이트까지 동일하다. 즉 검색엔진이 보는 제목·설명·헤드라인 세 줄이 스물한 장씩 겹친다.

목록 페이지에서는 더 또렷하다. 인물 전체목록의 카드 142장은 제목 아래에 "테마: 청순 · 최고 75.0%" 같은 한 줄을 달고 있는데, 테마는 이미 제목에 들어 있으므로 실질적으로 구분에 쓰이는 값은 최고 일치율 하나뿐이다. 그 결과 카드 142장 중 64장은 다른 카드와 보이는 글자가 완전히 같다(26개 조합). 번호도 날짜도 없으니 "아까 본 그 시즌"을 목록에서 다시 찾으려면 링크를 하나씩 눌러 보는 수밖에 없다.

결과 ④ — 배지 넷까지 동원해도 18편은 구분되지 않는다

그렇다면 리포트를 열면 구분이 될까. 페이지가 상단에 내놓는 식별 정보는 제목, 대상 인물 이름, 그리고 배지 네 개(테마·라운드 수·최고 일치율·최종 일치율)다. 이 여섯 값을 모두 이어 붙여도 겹치는 쌍이 아홉 있다.

제목대상 인물라운드최고 = 최종같은 값을 가진 시즌
청순의 정점윤하늘1575.0%71 · 191
청순의 정점윤하늘1577.0%21 · 31
매력의 정점신유나1567.0%29 · 79
매력의 정점신유나1575.0%39 · 49
예쁨의 정점백채원1576.0%27 · 137
귀여움의 정점이서윤1568.0%23 · 53
아름다움의 정점정세아1574.0%25 · 35
소년미의 정점백승원1576.0%86 · 116
훈훈함의 정점김태율1575.0%145 · 155

열여덟 편이다. 이 쌍들을 가르는 것은 URL과, 그 아래에 실린 이미지·심사평 본문뿐이다. 대상 인물 이름이 도움이 되지 않는 이유는 그 이름도 돌려쓰기 때문이다 — 142편의 대상 인물 이름은 20종이고, "윤하늘"만 열다섯 번 나온다.

결과 ⑤ — 히어로 윗줄은 모듈마다 다른 축을 적는다

제목 바로 위의 작은 줄(키커)은 전 모듈에 있지만, 둘째 칸에 무엇을 넣을지는 합의가 없다.

모듈키커 형태둘째 칸의 축편수
AI 인물 생성AI 인물 생성 · 청순테마142
AI 유머 대결AI 유머 대결 · 시즌 27시즌 번호15
AI 마인드게임AI 마인드게임 · 시즌 1시즌 번호1
AI 트레이딩AI 트레이딩 · VIRTUAL_RANK내부 모드 코드2
AI 커플매칭AI 커플매칭없음6

트레이딩 두 편이 눈에 띈다. VIRTUAL과 VIRTUAL_RANK는 트레이딩 백엔드가 시즌의 진행 방식을 구분하려고 쓰는 trading_mode 컬럼의 값이다. 사람에게 보여 주려고 만든 문자열이 아니라 코드가 분기에 쓰는 값인데, 그대로 독자 눈앞에 나와 있다. 유머 리포트가 그 자리에 "시즌 27"을 적는 것과 같은 자리다.

결과 ⑥ — 번호를 적는 쪽에도 규약은 둘로 갈린다

시즌 번호를 제목에 적는 13편 안에서도 표기가 나뉜다. 매칭·트레이딩·마인드게임 9편은 "시즌1:"처럼 숫자를 붙여 쓰고 콜론으로 부제를 끊는 반면, 유머 4편은 "AI 유머 시즌 23"처럼 띄어 쓰고 부제가 없다. 서수 표기도 한 모듈 안에서 흔들린다 — 매칭 6편 중 "첫 번째·두 번째"는 띄어 쓰고 "세번째"는 붙여 쓴다(2편).

더 성가신 건 매칭의 번호가 URL의 번호와 다르다는 점이다. 여섯 편 전부 그렇다.

URL국문 제목영문 제목
/reports/matching/season-3.html시즌1: 칼라톤, 첫 번째 시그널Season 1: Colortone, The First Signal
/reports/matching/season-4.html시즌1: 칼라톤, 두 번째 시그널Season 1: Colortone, The Second Signal
/reports/matching/season-5.html시즌1: 칼라톤, 세번째 시그널Season 1: Colortone, The Third Signal
/reports/matching/season-6.html시즌2: 칼라톤, 첫 번째 시그널Season 1: Colortone, The Third Signal
/reports/matching/season-7.html시즌2: 칼라톤, 두 번째 시그널Season 2: Colortone, The Second Signal
/reports/matching/season-8.html시즌2: 칼라톤, 세번째 시그널Season 2: Colortone, The Third Signal

굵게 표시한 줄이 이번 감사에서 나온 유일한 국문–영문 불일치다. season-6의 영문 제목은 국문이 말하는 "시즌2 첫 번째"가 아니라 바로 앞 편인 season-5의 영문 제목과 글자까지 같다. 번호도 서수도 어긋났으니 영문 독자에게는 같은 회차가 두 번 올라온 것처럼 보인다. 국문과 영문 양쪽에 시즌 번호가 적힌 13편 중 1편이다.

매칭 리포트는 JSON-LD에서도 혼자 다르다. 160편이 datePublished를 담는데 매칭 6편에는 그 항목이 없다. 제목의 "시즌1/시즌2"는 매칭이 자체적으로 세는 리그 회차이고 URL의 3~8은 데이터베이스가 세는 시즌 번호다. 둘 다 '시즌'이라 부르는 탓에 발행일조차 없는 페이지에서 독자가 순서를 복원할 단서는 사실상 없다.

한계 — 이 감사가 말할 수 없는 것

첫째, 이 글이 센 것은 현재 저장소에 있는 출력물이다. 제목의 원재료는 데이터베이스의 시즌 제목 컬럼이고, 리포트 생성기는 그 값을 받아 틀에 끼울 뿐이다. 따라서 "문형이 여섯 가지"는 생성기가 여섯 갈래라는 뜻이 아니라, 그동안 데이터베이스에 들어간 제목이 여섯 모양이었다는 뜻이다. 어느 쪽을 고쳐야 하는지는 이 집계만으로 결정되지 않는다.

둘째, 표본이 모듈마다 극단적으로 비대칭이다. 인물 142편과 마인드게임 1편을 같은 표에 놓았지만, 1편짜리 모듈의 "고유 제목 1종·번호 표기 100%"는 규약의 증거가 아니라 산술의 결과다. 모듈 간 비교로 읽을 수 있는 것은 인물(142)·유머(15)·매칭(6) 정도까지다.

셋째, '구분 가능성'을 가시 텍스트로만 쟀다. 실제 독자는 URL을 보고, 이미지를 보고, 북마크 이력을 본다. 열여덟 편이 "구분 불가"라는 말은 페이지의 글자만 비교했을 때 그렇다는 뜻이지, 사람이 두 페이지를 나란히 놓고 구별하지 못한다는 뜻이 아니다. 반대로 검색엔진과 링크 공유는 대체로 그 글자만 가져간다.

넷째, 중복 제목이 색인에 실제로 얼마나 손해인지는 이 데이터로 알 수 없다. 우리는 "같은 제목이 21장"이라는 사실만 확인했고, 그것이 노출·클릭에 미친 영향은 검색 콘솔의 영역이라 이 글의 바깥이다. 다만 저장소 안의 규칙 파일이 같은 위험을 이유로 유머 제목에 번호를 붙이기로 한 것은 사실이다.

다섯째, 이스케이프 결함의 모집단이 1이다. 앰퍼샌드를 담은 제목이 332편 중 한 편뿐이므로 "한 번의 기회에 한 번 발생"은 발생률이 아니라 사례 보고다. 같은 결함이 다른 특수문자(<, 따옴표 등)에서도 일어나는지는 그런 제목이 아직 없어 확인할 수 없었다.

여섯째, 영문 불일치 1건은 원인까지 규명하지 못했다. 국문·영문 제목이 데이터베이스에서 별도 컬럼으로 관리되는 구조라면 한쪽만 갱신되고 다른 쪽이 앞 시즌 값을 그대로 물려받는 일이 가능하지만, 그 경로를 이 감사에서 확인하지는 않았다.

그래서 규약은 있는가

있다. 다만 한 층에만 있다. 제목의 바깥 틀, 목록 카드와 h1의 일치, og:title과 JSON-LD 헤드라인의 파생 — 자동화가 책임지는 자리의 예외는 332편에서 이스케이프 1건뿐이다. 기계가 복제하기로 한 것은 거의 완벽하게 복제된다.

규약이 없는 층은 그 안쪽, 즉 "무엇을 작품명이라 부를 것인가"다. 인물은 테마를 적고, 유머는 시기 또는 번호를 적고, 매칭은 리그 회차와 부제를 적고, 트레이딩은 내부 모드 코드를 키커에 흘린다. 모듈마다 자기 모듈 안에서는 자연스러운 선택이지만, 166편을 한 줄로 세우면 같은 자리에 서로 다른 축이 들어가 있다. 제목이 시즌을 가리키는 모듈과, 테마를 가리키는 모듈과, 주차를 가리키는 모듈이 한 사이트에 공존한다.

고칠 방향은 이미 저장소 안에 적혀 있다. 유머가 2026년 9월에 내린 결론 — 제목만으로 시즌을 구분해야 하는 표면에서는 번호를 붙인다 — 을 인물 리포트에 그대로 적용하면 제목 11종은 142종이 되고, 목록의 중복 카드 64장과 구분 불가 18편이 동시에 사라진다. 바깥 틀을 건드리지 않고 작품명 한 자리만 바꾸면 되는 변경이다. 이 글은 그 변경을 제안하기 위한 계수이지, 변경 자체는 아니다. 언제 어떻게 반영할지는 따로 적을 일이다.

그동안 리포트를 읽으실 분들께는 이렇게 권한다. 인물 리포트를 여러 편 비교할 때는 탭 제목 대신 주소창의 번호를 보시라. 적어도 그 번호는 166편 전부에서 유일하다.

집계 기준. 2026년 10월 6일(KST) 기준 저장소의 공개 시즌 리포트 전부 — 국문 166편(인물 142 · 유머 15 · 매칭 6 · 트레이딩 2 · 마인드게임 1)과 영문 166편, 목록 페이지 4장의 카드 332건. 외부 API를 쓰지 않고 HTML의 <title>·<h1>·히어로 키커·meta description·og:title·JSON-LD를 파싱해 집계했다. '작품명'은 제목에서 모듈 라벨과 브랜드 꼬리를 뗀 앞부분이고, 평균 길이는 공백 포함 문자 수다. '가시 텍스트에 시즌 번호'는 <body>에서 태그를 제거한 문자열에 "시즌 N" 표기가 있는지로 판정했으며 URL·이미지 경로는 제외했다. 문자열 비교는 HTML 엔티티를 한 번 복원한 뒤 수행했고, 이중 이스케이프 1건은 그 복원 이후에도 남은 차이다. 리포트는 계속 발행되므로 편수와 제목 종류는 집계 시점 이후 달라진다.