diff --git a/confluence-mdx/bin/converter/core.py b/confluence-mdx/bin/converter/core.py index f8b177acc..c50448e85 100644 --- a/confluence-mdx/bin/converter/core.py +++ b/confluence-mdx/bin/converter/core.py @@ -930,7 +930,7 @@ def convert_li(self, node, list_type, counter=None): logging.debug(f'li_itself={li_itself}') logging.debug(f'child_markdown={child_markdown}') - itself = ' '.join(li_itself) + itself = ''.join(li_itself) self.markdown_lines.append(f'{prefix}{itself}\n') for line in child_markdown: self.markdown_lines.append(prefix_for_children + line) diff --git a/confluence-mdx/docs/fc-whitespace-preservation.md b/confluence-mdx/docs/fc-whitespace-preservation.md new file mode 100644 index 000000000..e8e532cb9 --- /dev/null +++ b/confluence-mdx/docs/fc-whitespace-preservation.md @@ -0,0 +1,128 @@ +# Forward Converter 공백 보존 원칙 + +## 핵심 원칙 + +Forward Converter(FC)는 XHTML 원본의 공백, 단락 구조를 **있는 그대로** MDX로 변환해야 한다. +FC 출력의 형태는 XHTML 원문이 결정한다. XHTML 원문과 무관하게 "이렇게 생겨야 한다"는 고정된 출력 형태는 없다. + +### 하지 말아야 할 것 + +- XHTML에 없는 공백을 추가하지 않는다. +- XHTML에 있는 공백을 임의로 제거하지 않는다. +- 빈 `

` 요소의 콘텐츠를 필터링하지 않는다 (XHTML에 존재하는 구조이다). +- FC 출력 형태를 미리 정해놓고 그에 맞추지 않는다. + +### 해야 할 것 + +- XHTML 텍스트 노드의 공백을 그대로 MDX로 옮긴다. +- `

` 요소의 콘텐츠(빈 단락 포함)를 보존한다. +- FC가 삽입하는 구조적 마커(`
` 등)와 XHTML 원본 공백을 구분한다. + +## 구체적 사례: `convert_li`의 `join` + +### 문제 상황 + +`convert_li()`에서 `

  • ` 내 여러 `

    ` 요소를 MDX 한 줄로 합칠 때, +`

    ` 경계를 `
    `로 표현하고 `' '.join(li_itself)`로 연결했다. + +이 `' '.join()`은 **모든 항목 사이에 공백 하나를 삽입**하는데, +이 공백은 XHTML 원문에 존재하지 않는다. + +### XHTML → FC 출력 추적 예시 + +#### 예시 1: `
    ` 뒤 빈 `

    ` + +```xml +

  • +

    경우

    + ... +

    +
  • +``` + +`

    ` 경계에서 `
    ` 구분자가 삽입되므로 `li_itself` 구성은: + +| 인덱스 | 출처 | 값 | 설명 | +|--------|------|----|------| +| 0 | 첫 번째 `

    ` | `'경우
    '` | XHTML 텍스트 노드의 trailing space 보존 | +| 1 | `
    ` 구분자 | `'
    '` | FC가 `

    ` 경계를 표현하기 위해 삽입 | +| 2 | `

    ` | `' '` | 빈 단락의 공백 콘텐츠 보존 | + +**`' '.join()` (잘못됨):** + +``` +'경우
    ' + ' ' + '
    ' + ' ' + ' ' +→ '경우

    ' +``` + +join이 삽입한 공백(` `)이 XHTML 원본 공백과 합쳐져 이중 공백이 된다. + +**`''.join()` (올바름):** + +``` +'경우
    ' + '
    ' + ' ' +→ '경우

    ' +``` + +XHTML 원본의 공백만 존재한다. + +#### 예시 2: `
    ` 없는 `

    ` + 빈 `

    ` + +```xml +

  • +

    클릭합니다

    + ... +

    +
  • +``` + +| 인덱스 | 출처 | 값 | +|--------|------|----| +| 0 | 첫 번째 `

    ` | `'클릭합니다'` | +| 1 | `
    ` 구분자 | `'
    '` | +| 2 | `

    ` | `''` | + +**`' '.join()` (잘못됨):** `'클릭합니다
    '` — `
    ` 앞뒤 공백은 XHTML에 없음 + +**`''.join()` (올바름):** `'클릭합니다
    '` — XHTML 그대로 + +#### 예시 3: 연속 `

    ` 텍스트 + +```xml +

  • +

    Text A

    +

    Text B

    +
  • +``` + +| 인덱스 | 출처 | 값 | +|--------|------|----| +| 0 | 첫 번째 `

    ` | `'Text A'` | +| 1 | `
    ` 구분자 | `'
    '` | +| 2 | 두 번째 `

    ` | `'Text B'` | + +**`' '.join()` (잘못됨):** `'Text A
    Text B'` — 경계 공백은 XHTML에 없음 + +**`''.join()` (올바름):** `'Text A
    Text B'` — `
    `가 줄바꿈 역할, 추가 공백 불필요 + +## 수정 방법 + +```python +# 변경 전 (XHTML에 없는 공백 삽입) +itself = ' '.join(li_itself) + +# 변경 후 (XHTML 원본 공백만 보존) +itself = ''.join(li_itself) +``` + +한 줄 변경이다. 각 `

    `의 SingleLineParser 출력에 이미 XHTML 원문의 공백이 +보존되어 있으므로, join 시 추가 공백을 넣지 않으면 된다. + +## 일반 원칙: FC 버그 수정 시 판단 기준 + +FC 출력이 "올바른지" 판단할 때: + +1. **XHTML 원문을 확인**한다 — FC 출력을 보기 전에, 입력인 XHTML이 어떤 구조인지 파악 +2. **XHTML의 공백/구조를 그대로 MDX에 반영하는지** 확인한다 +3. FC가 삽입하는 구조적 요소(`
    ` 구분자 등)는 XHTML 원본 공백과 **분리**해서 처리한다 +4. "출력이 이렇게 생겨야 한다"는 고정관념 없이, XHTML 원문에서 출력을 **도출**한다 diff --git a/confluence-mdx/tests/testcases/1454342158/expected.html b/confluence-mdx/tests/testcases/1454342158/expected.html index e844db534..f003caf3d 100644 --- a/confluence-mdx/tests/testcases/1454342158/expected.html +++ b/confluence-mdx/tests/testcases/1454342158/expected.html @@ -71,7 +71,7 @@

    LDAP 연동

  • Server URL : ldap://ldap.example.com:389 과 같은 형식으로 LDAP server의 주소를 입력합니다. LDAPS의 경우 scheme을 ldaps:// 로 입력합니다.
  • -
  • BindDN : LDAP 서버에 접속(바인드)할 때 사용할 서비스 계정의 고유 이름(Distinguished Name, DN)을 입력합니다. 이 계정은 최소한 사용자 정보를 검색(Read)할 수 있는 권한이 필요합니다.
    예시: cn=admin,ou=Services,dc=example,dc=com
  • +
  • BindDN : LDAP 서버에 접속(바인드)할 때 사용할 서비스 계정의 고유 이름(Distinguished Name, DN)을 입력합니다. 이 계정은 최소한 사용자 정보를 검색(Read)할 수 있는 권한이 필요합니다.
    예시: cn=admin,ou=Services,dc=example,dc=com
  • Bind Password : BindDN의 암호를 입력합니다.
  • @@ -552,11 +552,11 @@

    QueryPie에서 Okta 연동 및 동기화 설정

  • Additional Settings