fix(cafe24): 소스 들여쓰기 정상화 + 줄 번호·가로 스크롤 편집기

1) 들여쓰기가 안 되던 원인

인라인 요소가 여러 줄에 걸쳐 있으면 그 구간을 하나의 텍스트 덩어리로 다뤄
첫 줄에만 들여쓰기를 붙이고 있었다. 그래서 <img> 가 한 줄에 하나씩 적힌
상세페이지에서 두 번째 <img> 부터 1열에 붙어 나왔다.

원문 줄바꿈을 살리면서 각 줄을 현재 깊이로 들여쓰도록 고쳤다. 줄 앞 공백은
렌더링에 영향이 없으므로 안전하다. 상세페이지는 <img> 를 한 줄에 하나씩 적어두는
경우가 많고 그 모양이 저자의 의도라, 한 줄로 합치는 것보다 살리는 쪽이 읽기 좋다.

주석도 줄을 강제로 나누지 않게 바꿨다. `<!-- 대파_타임랩스 --><img ...>` 처럼 바로
뒤 요소를 설명하는 주석이 많아서, 나누면 라벨과 대상이 떨어져 오히려 읽기 나빠진다.

빈 줄은 구획 표시로 한 줄까지 유지한다. 이 과정에서 멱등성이 다시 깨지는 것을
테스트가 잡았다 — 텍스트 끝에 남은 "\n  " 조각이 매번 빈 줄로 바뀌고 있었고,
양 끝 공백을 함께 제거하도록 고쳤다.

2) 줄 번호와 가로 스크롤

전문 편집기처럼 줄 번호 칸을 넣었다. 줄바꿈을 허용하면 한 논리 줄이 여러 행이 되어
번호가 어긋나므로, 줄바꿈을 끄고(white-space:pre + wrap=off) 가로로 스크롤한다.
번호 칸은 position:sticky 라 가로로 스크롤해도 왼쪽에 남는다.

크기 계산도 단순해졌다. <pre> 가 흐름에 남아 상자 크기를 정하고 textarea 가
inset:0 으로 그 위를 덮는다 — JS 로 높이를 맞추지 않으니 어긋날 여지가 없다.
색상 계열은 그대로 뒀다(태그 초록·속성 갈색·값 남색·주석 회색).

검증: 유닛테스트 45개 통과(신규 4개 — 모든 줄 들여쓰기, 주석과 요소 붙임,
빈 줄 1개 유지, 실제 상세페이지 모양 멱등). 브라우저 실측: 줄 번호 개수가 줄 수와
일치(18/18, 19/19), 번호와 코드의 세로 위치 일치, 긴 줄에서 편집기 안에서만 가로
스크롤(1920 > 716)되고 페이지는 가로 스크롤 없음, 400px 스크롤 후에도 번호 칸 고정,
textarea 내부 스크롤 0(두 층 정렬 유지).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 13:03:54 +09:00
parent 1d3dac3bec
commit 5284fb1c6e
8 changed files with 159 additions and 42 deletions
+15 -6
View File
@@ -166,12 +166,15 @@ cafe24_oauth_tokens 저장
**문법 강조** — 색칠된 `<pre>` 위에 **투명한 `<textarea>`** 를 정확히 겹쳐 놓는
방식이다. 외부 라이브러리를 쓰지 않는다(자체 호스팅 원칙).
- 두 층의 **폰트·글자크기·줄높이·padding·`white-space`·`overflow-wrap`·`tab-size`
가 완전히 같아야** 글자가 어긋나지 않는다. `cafe24.css` 의
`.cf24-code-hl, .cf24-code-input` 규칙을 항상 함께 수정할 것.
- 높이는 **`<pre>` 의 `scrollHeight` 를 기준**으로 textarea 에 지정한다.
textarea 의 `scrollHeight` 를 쓰면 브라우저가 한두 줄 더 잡아 두 층이 어긋난다
(실측 830 vs 792). 스크롤은 바깥 `.cf24-code` 가 담당한다.
- 두 층의 **폰트·글자크기·줄높이·padding·`white-space`·`tab-size` 가 완전히 같아야**
글자가 어긋나지 않는다. 줄 번호 칸(`.cf24-gutter`)도 같은 글꼴 지표를 써야 줄이
맞는다. `cafe24.css` 의 세 선택자를 항상 함께 수정할 것.
- **크기는 `<pre>` 가 정하고 textarea 가 `inset:0` 으로 그 위를 덮는다.** JS 로 높이를
계산하지 않는다(textarea 의 `scrollHeight` 브라우저가 한두 줄 더 잡아 어긋난다
실측 830 vs 792). 스크롤은 바깥 `.cf24-code` 가 담당한다.
- **줄바꿈하지 않고 가로로 스크롤한다**(`white-space: pre` + `wrap="off"`).
줄바꿈을 허용하면 한 논리 줄이 여러 행이 되어 줄 번호가 맞지 않는다.
- 줄 번호 칸은 `position: sticky; left: 0` 이라 가로로 스크롤해도 왼쪽에 남는다.
- 20만 자를 넘으면 강조를 끄고 평문으로 보여준다(타이핑마다 재색칠하면 느려짐).
- 색: 태그 초록 / 속성이름 갈색 / 속성값 남색 / 주석 회색 기울임 / 기호 회색.
@@ -181,6 +184,12 @@ cafe24_oauth_tokens 저장
- 줄을 나누는 것은 **블록 요소 경계에서만** 한다. HTML 에서 공백은 의미가 있어서
인라인 요소 사이에 줄바꿈을 넣으면 화면에 공백이 생긴다(이미지 사이가 벌어지는
고전적인 사고). `img`·`br`·`span`·`a` 는 블록 목록에서 **의도적으로 제외**했다.
- **원문에 이미 있던 줄바꿈은 살리고, 각 줄을 현재 깊이로 들여쓴다.** 상세페이지는
`<img>` 를 한 줄에 하나씩 적어두는 경우가 많고 그 모양이 저자의 의도다. 줄 앞
공백은 렌더링에 영향이 없으므로 들여쓰기는 안전하다.
- 주석은 줄을 강제로 나누지 않는다. `<!-- 대파_타임랩스 --><img ...>` 처럼 바로 뒤
요소를 설명하는 주석이 많아, 나누면 라벨과 대상이 떨어져 오히려 읽기 나빠진다.
- 구획용 **빈 줄은 한 줄까지 유지**한다(여러 줄은 하나로 줄인다).
- `<style>`·`<script>`·`<pre>`·`<textarea>` 안쪽은 한 글자도 건드리지 않는다.
- 내용이 한 줄뿐인 짧은 블록은 다시 한 줄로 합친다(`<td>1</td>`).
- **멱등**이다 — 편집하지 않고 다시 적용해도 저장값이 계속 바뀌지 않는다(테스트로 고정).