|
| 1 | +# Forward Converter 공백 보존 원칙 |
| 2 | + |
| 3 | +## 핵심 원칙 |
| 4 | + |
| 5 | +Forward Converter(FC)는 XHTML 원본의 공백, 단락 구조를 **있는 그대로** MDX로 변환해야 한다. |
| 6 | +FC 출력의 형태는 XHTML 원문이 결정한다. XHTML 원문과 무관하게 "이렇게 생겨야 한다"는 고정된 출력 형태는 없다. |
| 7 | + |
| 8 | +### 하지 말아야 할 것 |
| 9 | + |
| 10 | +- XHTML에 없는 공백을 추가하지 않는다. |
| 11 | +- XHTML에 있는 공백을 임의로 제거하지 않는다. |
| 12 | +- 빈 `<p>` 요소의 콘텐츠를 필터링하지 않는다 (XHTML에 존재하는 구조이다). |
| 13 | +- FC 출력 형태를 미리 정해놓고 그에 맞추지 않는다. |
| 14 | + |
| 15 | +### 해야 할 것 |
| 16 | + |
| 17 | +- XHTML 텍스트 노드의 공백을 그대로 MDX로 옮긴다. |
| 18 | +- `<p>` 요소의 콘텐츠(빈 단락 포함)를 보존한다. |
| 19 | +- FC가 삽입하는 구조적 마커(`<br/>` 등)와 XHTML 원본 공백을 구분한다. |
| 20 | + |
| 21 | +## 구체적 사례: `convert_li`의 `join` |
| 22 | + |
| 23 | +### 문제 상황 |
| 24 | + |
| 25 | +`convert_li()`에서 `<li>` 내 여러 `<p>` 요소를 MDX 한 줄로 합칠 때, |
| 26 | +`<p>` 경계를 `<br/>`로 표현하고 `' '.join(li_itself)`로 연결했다. |
| 27 | + |
| 28 | +이 `' '.join()`은 **모든 항목 사이에 공백 하나를 삽입**하는데, |
| 29 | +이 공백은 XHTML 원문에 존재하지 않는다. |
| 30 | + |
| 31 | +### XHTML → FC 출력 추적 예시 |
| 32 | + |
| 33 | +#### 예시 1: `<br/>` 뒤 빈 `<p>` |
| 34 | + |
| 35 | +```xml |
| 36 | +<li> |
| 37 | + <p>경우<br/> </p> |
| 38 | + <ac:image>...</ac:image> |
| 39 | + <p> </p> |
| 40 | +</li> |
| 41 | +``` |
| 42 | + |
| 43 | +`<p>` 경계에서 `<br/>` 구분자가 삽입되므로 `li_itself` 구성은: |
| 44 | + |
| 45 | +| 인덱스 | 출처 | 값 | 설명 | |
| 46 | +|--------|------|----|------| |
| 47 | +| 0 | 첫 번째 `<p>` | `'경우<br/> '` | XHTML 텍스트 노드의 trailing space 보존 | |
| 48 | +| 1 | `<br/>` 구분자 | `'<br/>'` | FC가 `<p>` 경계를 표현하기 위해 삽입 | |
| 49 | +| 2 | `<p> </p>` | `' '` | 빈 단락의 공백 콘텐츠 보존 | |
| 50 | + |
| 51 | +**`' '.join()` (잘못됨):** |
| 52 | + |
| 53 | +``` |
| 54 | +'경우<br/> ' + ' ' + '<br/>' + ' ' + ' ' |
| 55 | +→ '경우<br/> <br/> ' |
| 56 | +``` |
| 57 | + |
| 58 | +join이 삽입한 공백(` `)이 XHTML 원본 공백과 합쳐져 이중 공백이 된다. |
| 59 | + |
| 60 | +**`''.join()` (올바름):** |
| 61 | + |
| 62 | +``` |
| 63 | +'경우<br/> ' + '<br/>' + ' ' |
| 64 | +→ '경우<br/> <br/> ' |
| 65 | +``` |
| 66 | + |
| 67 | +XHTML 원본의 공백만 존재한다. |
| 68 | + |
| 69 | +#### 예시 2: `<br/>` 없는 `<p>` + 빈 `<p>` |
| 70 | + |
| 71 | +```xml |
| 72 | +<li> |
| 73 | + <p>클릭합니다</p> |
| 74 | + <ac:image>...</ac:image> |
| 75 | + <p></p> |
| 76 | +</li> |
| 77 | +``` |
| 78 | + |
| 79 | +| 인덱스 | 출처 | 값 | |
| 80 | +|--------|------|----| |
| 81 | +| 0 | 첫 번째 `<p>` | `'클릭합니다'` | |
| 82 | +| 1 | `<br/>` 구분자 | `'<br/>'` | |
| 83 | +| 2 | `<p></p>` | `''` | |
| 84 | + |
| 85 | +**`' '.join()` (잘못됨):** `'클릭합니다 <br/> '` — `<br/>` 앞뒤 공백은 XHTML에 없음 |
| 86 | + |
| 87 | +**`''.join()` (올바름):** `'클릭합니다<br/>'` — XHTML 그대로 |
| 88 | + |
| 89 | +#### 예시 3: 연속 `<p>` 텍스트 |
| 90 | + |
| 91 | +```xml |
| 92 | +<li> |
| 93 | + <p>Text A</p> |
| 94 | + <p>Text B</p> |
| 95 | +</li> |
| 96 | +``` |
| 97 | + |
| 98 | +| 인덱스 | 출처 | 값 | |
| 99 | +|--------|------|----| |
| 100 | +| 0 | 첫 번째 `<p>` | `'Text A'` | |
| 101 | +| 1 | `<br/>` 구분자 | `'<br/>'` | |
| 102 | +| 2 | 두 번째 `<p>` | `'Text B'` | |
| 103 | + |
| 104 | +**`' '.join()` (잘못됨):** `'Text A <br/> Text B'` — 경계 공백은 XHTML에 없음 |
| 105 | + |
| 106 | +**`''.join()` (올바름):** `'Text A<br/>Text B'` — `<br/>`가 줄바꿈 역할, 추가 공백 불필요 |
| 107 | + |
| 108 | +## 수정 방법 |
| 109 | + |
| 110 | +```python |
| 111 | +# 변경 전 (XHTML에 없는 공백 삽입) |
| 112 | +itself = ' '.join(li_itself) |
| 113 | + |
| 114 | +# 변경 후 (XHTML 원본 공백만 보존) |
| 115 | +itself = ''.join(li_itself) |
| 116 | +``` |
| 117 | + |
| 118 | +한 줄 변경이다. 각 `<p>`의 SingleLineParser 출력에 이미 XHTML 원문의 공백이 |
| 119 | +보존되어 있으므로, join 시 추가 공백을 넣지 않으면 된다. |
| 120 | + |
| 121 | +## 일반 원칙: FC 버그 수정 시 판단 기준 |
| 122 | + |
| 123 | +FC 출력이 "올바른지" 판단할 때: |
| 124 | + |
| 125 | +1. **XHTML 원문을 확인**한다 — FC 출력을 보기 전에, 입력인 XHTML이 어떤 구조인지 파악 |
| 126 | +2. **XHTML의 공백/구조를 그대로 MDX에 반영하는지** 확인한다 |
| 127 | +3. FC가 삽입하는 구조적 요소(`<br/>` 구분자 등)는 XHTML 원본 공백과 **분리**해서 처리한다 |
| 128 | +4. "출력이 이렇게 생겨야 한다"는 고정관념 없이, XHTML 원문에서 출력을 **도출**한다 |
0 commit comments