댓글창이 4주 동안 비어 있었습니다 — 스피너가 멈춘 걸 위젯이 떴다고 읽었습니다
티스토리 스킨을 직접 만들어 쓰는 블로그 두 개에서, 글 페이지의 댓글창이 4주 동안 화면에 없었습니다. 댓글을 쓸 수 없던 게 아니라 댓글 영역 자체가 렌더되지 않았습니다. 그 자리에 아무것도 없으니 빈 공간처럼 보였고, 저는 그게 정상인 줄 알았습니다.
더 나쁜 건, 그동안 제가 댓글 문제를 이미 해결했다고 믿고 그 내용을 글로 써서 발행해뒀다는 것입니다. 스킨을 직접 만들며 깨진 것들에서 글 페이지 무한 로딩의 원인을 「댓글 마운트 자리 부재」로 단정했습니다. 그 진단은 틀렸고, 고쳤다고 생각한 것도 안 고쳐져 있었습니다.
무엇을 잘못 읽었나
당시 증상은 글 페이지만 무한 로딩이었습니다. 스피너가 계속 돌고 본문이 안 떴습니다. 스킨에 댓글 치환자를 넣었더니 로딩이 풀렸고, 저는 두 가지를 한꺼번에 해결했다고 판단했습니다 — 로딩이 풀렸으니 댓글 위젯도 떴을 것이다.
앞은 맞고 뒤는 틀렸습니다. 그리고 앞도 이유가 달랐습니다.
당시 메모에는 무한 로딩의 후보를 두 개 적어뒀습니다. (a) 댓글 마운트 자리 부재, (b) 발행 글 0개. 로딩을 실제로 푼 것은 (b) 였습니다. 글을 하나 발행하니 로딩이 끝났습니다. 같은 시점에 넣은 (a)의 치환자는 아무것도 렌더하지 않는 상태 그대로 4주를 갔습니다.
증상이 사라진 것과 기능이 살아난 것은 다른 사건인데, 같은 시각에 일어나면 하나로 보입니다. 저는 스피너가 멈춘 것을 위젯이 떴다고 읽었습니다. 그리고 확인을 안 했습니다 — 글 페이지를 열어 아래로 내려보기만 했어도 5초면 알았을 일입니다.
세 층으로 다시 쟀습니다
의심이 든 뒤에는 눈으로 보지 않고 세 군데서 따로 쟀습니다. 같은 화면을 보는 방법이 세 가지고, 셋이 서로를 검증합니다.
① 서버가 내려주는 HTML. 브라우저를 거치지 않고 curl 로 받았습니다.
댓글 영역으로 잡아둔 <div class="area-reply"> 안이 완전히 빈 문자열이었습니다.
여는 태그와 닫는 태그 사이에 공백조차 없었습니다. 이 시점에서
「JS가 늦게 붙는 중」이라는 가능성이 사라집니다. 붙을 자리가 안 왔습니다.
② 브라우저가 렌더한 뒤의 DOM. .area-reply 의 높이가 0px 였고,
티스토리가 자기 위젯을 붙일 때 쓰는 data-tistory-react-app="Comment" 노드가
없었습니다. 같은 페이지에 Reaction·SupportButton·Menubar 는 정상적으로
붙어 있었습니다. 즉 티스토리 스크립트 자체는 잘 돌고 있었고, 댓글만 붙일
자리를 못 찾은 상태였습니다.
③ 정상 블로그 대조군. 기본 스킨을 쓰는 공개 블로그(notice.tistory.com/2701)의
같은 자리를 받아 비교했습니다. 거기엔 이런 것들이 있었습니다.
<div data-tistory-react-app="Namecard"></div>
<div id="entry2701Comment">
<div class="comments"><div data-tistory-react-app="Comment"></div></div>
</div>
그리고 페이지 끝에 loadedComments[2701] = true 가 있었습니다.
제 쪽에는 이 셋이 하나도 없었습니다. 대조군이 없었으면
「원래 이렇게 나오는 건가」에서 멈췄을 겁니다.
세 층을 나란히 놓으면 이렇습니다.
| 재는 곳 | 내 블로그 | 대조군(기본 스킨) |
|---|---|---|
| 서버 HTML의 댓글 영역 안 | 빈 문자열 (공백 0자) | Namecard + entry{N}Comment 마크업 |
렌더 후 .area-reply 높이 | 0px | 위젯 높이만큼 |
data-tistory-react-app="Comment" | 없음 | 있음 |
| 같은 페이지의 다른 위젯 | Reaction·SupportButton·Menubar 정상 | 정상 |
페이지 끝 loadedComments[{N}] | 없음 | = true |
| 서버에 있는 댓글 데이터 | 있음 (글 3개에 각 1건) | — |
마지막 줄이 이 표에서 제일 아팠습니다. 데이터는 있는데 표시할 자리가 없던 상태입니다. 읽지 못한 댓글이 4주 동안 서버에 있었습니다.
그리고 넷째 줄이 범인을 좁힙니다. 다른 위젯 셋이 정상이라면 티스토리 스크립트도, 스킨 로딩 순서도, 네트워크도 아닙니다. 댓글 하나만 자리를 못 찾았습니다.
원인은 치환자가 아니라 그것을 감싸는 것이었습니다
제가 스킨에 넣은 것은 댓글 치환자 한 줄이었습니다. 그 태그는 유효한 태그가
맞습니다. 문제는 그것을 감싸는 그룹 래퍼 <s_rp> 가 없었다는 것이었습니다.
티스토리 스킨 엔진에서 <s_rp> 는 단순한 조건 블록이 아닙니다.
서버가 이 태그를 처리하면서 안쪽 내용 말고 바깥 구조를 같이 만들어냅니다.
- 여는
<s_rp>가Namecard노드와<div id="entry{글번호}Comment">를 만들고 - 닫는
</s_rp>가loadedComments[{글번호}]스크립트를 만듭니다
그러니까 대조군에서 봤던 셋이 전부 이 래퍼의 산물이었습니다. 그리고 결정적으로, 그룹 밖에 있는 댓글 치환자는 빈 문자열로 치환됩니다. 에러도 아니고 주석도 아니고, 그냥 사라집니다.
이게 이 버그가 4주를 간 이유입니다. 잘못 쓰면 화면에 뭔가 이상한 게 나와야 알아채는데, 아무것도 안 나오는 방식으로 실패했습니다. 빈 자리는 「아직 안 만든 자리」와 구분되지 않습니다.
근거는 티스토리가 공개 배포하는 공식 Poster 스킨입니다.
tistory1.daumcdn.net/tistory/0/pg_Poster/skin.html 을 그대로 받아보면
345~349행에 이 구조가 있습니다(2026-09-09 재확인, HTTP 200).
치환자가 <s_rp> 안에 들어가 있습니다.
하마터면 반대 방향으로 갈 뻔했습니다
원인을 찾는 도중에 한 번 더 틀렸습니다.
<s_rp> 를 검색하다 보면 그 안에서 쓰는 s_rp_container·s_rp_rep·s_rp_input_form
같은 태그들이 같이 나옵니다. 예전 ray 테마 계열 스킨이 이 방식으로 댓글 목록과
입력폼을 직접 그렸습니다. 저는 그 마크업을 통째로 이식하려고 했습니다.
그쪽이 오히려 레거시였습니다. 지금은 서버가 만들어준 자리에 React 위젯이
스스로 목록과 폼을 그리므로, 스킨이 할 일은 두 줄뿐입니다 —
<s_rp> 로 감싸고 그 안에 댓글 치환자를 두는 것.
옛 마크업을 이식했으면 위젯과 수동 마크업이 겹쳐 더 어려운 문제가 됐을 겁니다.
「검색 결과에 나온다」와 「지금 쓰는 방식이다」는 다릅니다. 스킨 태그는 문서화가 얇아서 블로그 글로 배우게 되는데, 그 글들의 작성 시점이 잘 안 보입니다. 공식 스킨을 직접 받아 보는 쪽이 빠르고 정확했습니다.
관리자 설정은 무죄였습니다
원인을 확정하기 전에 「혹시 댓글 허용이 꺼져 있나」를 의심했습니다. 관리 화면의 댓글 설정을 두 블로그와 대조군에서 확인했더니 셋 다 같았습니다 — 댓글 허용 켜짐, 승인 필요 없음, 비회원 작성만 꺼짐.
마지막 항목은 설정된 동작이라 버그가 아닙니다. 다만 비회원에게는 작성할 때 로그인 유도가 뜨므로, 「댓글이 안 달린다」는 제보를 받으면 이것과 위젯 미렌더를 구분해야 합니다. 증상이 비슷하고 원인이 완전히 다릅니다.
고친 뒤
두 스킨 모두 댓글 영역 안쪽을 <s_rp> 로 감쌌습니다.
위젯이 서버가 만든 래퍼 안에 붙으므로, 그 래퍼까지 본문 폭 안에 들어오도록
<s_rp> 를 기존 컨테이너 안쪽에 두었습니다. 바깥에 두면 댓글 영역만
본문보다 넓게 튀어나옵니다.
고친 뒤 한쪽 블로그의 글 41편을 전수로 확인했습니다. 41편 전부에서 댓글 영역이 정상 마운트됐습니다. 그리고 이후 스킨을 다시 저장할 때마다 같은 검사를 한 번씩 돌립니다 — 이 버그는 스킨 파일을 건드리다 래퍼를 잃어버리면 조용히 재발합니다.
남은 것
- 주석에 치환자 이름을 그대로 쓰면 안 됩니다. 엔진이 주석 안까지 치환해서 설명하려던 문장이 깨집니다. 이걸 모르고 「이 태그가 필요하다」고 주석을 달았다가 주석이 이상해진 적이 있습니다.
- 이 글은 앞선 글의 진단을 뒤집습니다. 그 글은 지우지 않고 두겠습니다. 틀린 경로를 지워버리면 다음 사람이 같은 곳에 다시 들어갑니다.
배운 것을 한 줄로 적으면 이렇습니다. 증상이 사라진 것을 기능이 살아난 증거로 쓰지 말 것. 둘은 같은 시각에 일어나도 다른 사건이고, 확인 비용은 대개 5초입니다.