Skip to content

xhtml-to-mdx: markdown 변환 로직 재검토 #577

@jk-kim0

Description

@jk-kim0

문제현상

#576 의 구현내용에 따르면, page.xhtml 파일의 <ac:link> xhtml node 에서는 다른 Space 의 문서를 가리키는 pageId 값이 포함되지 않았습니다. 이에 따라, pageId 를 이용한 정확한 url 링크를 만들지 못합니다.

수행할 과제

문제현상을 해결하는 방안을 도출하기 위해, 탐색과 조사, 분석을 수행합니다.

  1. pageId 가 없이, <ac:link> xhtml node 내부의 정보를 활용하여, url 을 생성하고 연결하는 것이 가능한지, 찾아봅니다.

  2. pageId 를 다른 입력 데이터에서 찾아봅니다. page.adf, page.html, page.v1.yaml, page.v2.yaml 등 포맷 데이터가 어떻게 다른지, 비교분석하고, 이를 활용할 수 있는 방안을 찾아봅니다.

과제의 결과

1단계: 데이터 포맷 분석 (2026-01-28)

조사 대상

테스트 케이스 1844969501 (문서 ID: "지원")의 다양한 포맷 파일을 분석하여 pageId 정보의 존재 여부를 확인했습니다.

발견 사항

page.xhtml (현재 변환에 사용 중)

  • pageId 없음: <ac:link> 태그에는 space-keycontent-title만 포함되어 있습니다.

예시:

<ac:link><ri:page ri:space-key="QCP" ri:content-title="QueryPie Architecture (KO)" ri:version-at-save="6" /><ac:link-body>QueryPie Architecture</ac:link-body></ac:link>

page.adf (Atlassian Document Format)

  • ✅ pageId 포함: 완전한 Confluence URL이 포함되어 있으며, pageId를 추출할 수 있습니다.

예시:

{
  "text": "QueryPie Architecture",
  "type": "text",
  "marks": [{
    "type": "link",
    "attrs": {
      "href": "https://querypie.atlassian.net/wiki/spaces/QCP/pages/400064797"
    }
  }]
}

page.v2.yaml (atlas_doc_format)

  • ✅ pageId 포함: page.adf와 동일한 JSON 구조를 YAML로 포맷팅한 것으로, 완전한 URL 포함

page.v1.yaml (Confluence REST API v1 응답)

  • ✅ pageId 포함: body.view 섹션의 HTML에 data-linked-resource-id 속성으로 pageId가 포함됨

예시:

<a href="/wiki/spaces/QCP/pages/400064797/QueryPie+Architecture+KO"
   data-linked-resource-id="400064797"
   data-linked-resource-version="6"
   data-linked-resource-type="page">QueryPie Architecture</a>

주요 발견

파일 포맷 pageId 포함 여부 정보 형태
page.xhtml space-key + content-title만 존재
page.adf 완전한 URL (pageId 포함)
page.v2.yaml 완전한 URL (pageId 포함)
page.v1.yaml HTML 속성 (data-linked-resource-id)

결론 및 다음 단계

  1. page.adf 또는 page.v2.yaml을 활용하면 정확한 pageId 기반 링크 생성이 가능합니다.
  2. 다음 조사 항목:
    • 현재 변환 스크립트가 어떤 파일을 입력으로 사용하는지 확인
    • page.adf 또는 page.v2.yaml을 대신 사용할 수 있는지 검토
    • 또는 보조 데이터로 활용할 수 있는 방안 검토

2단계: 변환 스크립트 분석 및 매칭 전략 검증 (2026-01-28)

현재 변환 스크립트 구조

confluence_xhtml_to_markdown.py 분석 결과:

  • 주 입력 파일: page.xhtml (XHTML 콘텐츠 파싱)
  • 보조 데이터: page.v1.yaml (현재는 현재 페이지의 제목과 경로 정보만 사용)

ADF 및 Confluence URL 구조 조사

Confluence URL 포맷 (공식 문서)

  1. 새로운 포맷 (Confluence 9.1+):

    https://confluence.example.com/spaces/{SPACEKEY}/pages/{pageId}/{Page+Title}
    
    • pageId로 페이지를 식별
    • 페이지 제목은 가독성을 위한 것
    • 페이지 이름 변경 시 자동 리다이렉트
  2. pageId 포맷:

    http://confluence.example.com/pages/viewpage.action?pageId={pageId}
    

ADF 링크 구조 (공식 문서)

{
  "type": "text",
  "text": "Link Text",
  "marks": [{
    "type": "link",
    "attrs": {
      "href": "https://example.com/...",
      "title": "Optional title"
    }
  }]
}

매칭 전략 검증

전략 1: page.adf 사용

  • ✅ 링크 텍스트를 기준으로 page.xhtml의 <ac:link-body>와 매칭 가능
  • ⚠️ 단, inlineCard 노드는 링크 텍스트가 없어 매칭 실패 (5개 중 1개 실패)

테스트 결과:

✓ MATCHED: QueryPie Architecture -> 400064797
✓ MATCHED: Advanced Environment Setup -> 887947577
✓ MATCHED: Advanced Integration Guide -> 841449834
✗ NOT MATCHED: 릴리스 버전 별 문서 (inlineCard)
✓ MATCHED: Troubleshooting -> 920486841

전략 2: page.v1.yaml의 body.view 사용 ⭐ 권장

  • ✅ 100% 매칭 성공
  • ✅ 모든 링크 (일반 링크 + inlineCard)에 대해 링크 텍스트와 pageId 매핑 제공

테스트 결과:

✓ MATCHED: QueryPie Architecture -> 400064797
✓ MATCHED: Advanced Environment Setup -> 887947577
✓ MATCHED: Advanced Integration Guide -> 841449834
✓ MATCHED: 릴리스 버전 별 문서 -> 841351486
✓ MATCHED: Troubleshooting -> 920486841

구현 방안

권장 방안: page.v1.yaml body.view 활용

  1. 초기화 단계: page.v1.yamlbody.view HTML을 파싱하여 링크 매핑 생성

    def build_link_mapping(page_v1: PageV1) -> Dict[str, str]:
        """
        Build a mapping of link text -> pageId from page.v1.yaml body.view
    
        Returns:
            Dict mapping link text to pageId
        """
        link_map = {}
        view_html = page_v1.get('body', {}).get('view', {}).get('value', '')
        soup = BeautifulSoup(view_html, 'html.parser')
    
        for link in soup.find_all('a', {'data-linked-resource-id': True}):
            text = link.get_text()
            page_id = link.get('data-linked-resource-id', '')
            link_map[text] = page_id
    
        return link_map
  2. 변환 단계: <ac:link> 처리 시 링크 텍스트로 pageId 조회

    link_text = link_body  # <ac:link-body> 내용
    space_key = ri_page.get('space-key', '')
    
    # Try to find pageId from mapping
    page_id = link_mapping.get(link_text)
    
    if page_id:
        # Generate accurate URL with pageId
        href = f'https://querypie.atlassian.net/wiki/spaces/{space_key}/pages/{page_id}'
    elif space_key:
        # Fallback to space overview
        href = f'https://querypie.atlassian.net/wiki/spaces/{space_key}/overview'
    else:
        # Error case
        href = '#link-error'
  3. URL 생성 전략:

    • pageId 있음: https://querypie.atlassian.net/wiki/spaces/{space_key}/pages/{page_id}
    • pageId 없음 + space_key 있음: https://querypie.atlassian.net/wiki/spaces/{space_key}/overview (현재 구현)
    • 둘 다 없음: #link-error (현재 구현)

대안: page.adf 활용

  • 대부분의 링크에 대해 작동
  • inlineCard 처리를 위한 추가 로직 필요
  • page.v1.yaml보다 복잡

결론

page.v1.yaml의 body.view HTML을 활용하는 방안이 가장 효과적입니다:

  1. ✅ 100% 매칭 성공률
  2. ✅ 현재 스크립트에서 이미 page.v1.yaml을 로드하므로 통합 용이
  3. ✅ 구현이 간단하고 명확
  4. ✅ 모든 링크 타입 (일반 링크, inlineCard) 지원

다음 단계

  1. confluence_xhtml_to_markdown.py에 링크 매핑 빌드 함수 추가
  2. <ac:link> 처리 로직에 pageId 조회 기능 통합
  3. 테스트 케이스로 검증
  4. PR 생성

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions