Skip to content

confluence-mdx: page 하위 folder가 catalog와 MDX 변환에서 누락됩니다 #1028

Description

@jk-kim0

목표

Confluence content tree의 folder를 정식 구조 노드이자 MDX landing page로 저장·변환합니다.

  • folder metadata와 직계 자식 API 응답을 var/{folder_id}/에 저장합니다.
  • folder와 하위 page/folder를 typed catalog에 포함합니다.
  • folder 자체의 MDX에 직계 자식 문서 목록을 표시합니다.
  • folder 이동·이름 변경·삭제 후 더 이상 유효하지 않은 생성 파일을 안전하게 정리합니다.

Related: #936

조사 기준

  • 기준 브랜치: main
  • 기준 commit: 9eb535ff5d38b9ad99cb677dce8b71b6dd1bb906
  • 확인일: 2026-07-24
  • 재현 대상: QM / MCP Server / 2167636017

확정된 동작

  • Folder MDX는 변환기가 전부 소유합니다.
  • convert_all.py 실행마다 folder MDX의 frontmatter, 제목, 직계 자식 목록을 완전히 재생성하며 수동 편집은 보존하지 않습니다.
  • Folder MDX 목록에는 직계 자식 pagefolder만 표시합니다.
  • Nested folder는 link 한 항목으로 표시하며, nested folder의 자식은 해당 folder MDX에서 조회합니다.
  • database, whiteboard, embed는 당분간 지원하지 않고 식별 가능한 경고를 남깁니다.
  • 빈 folder도 MDX를 생성하고 하위 문서가 없습니다.를 표시합니다.
  • 계층 구조 변경은 즉시 감지할 필요가 없습니다. Folder 생성·이동·이름 변경·삭제는 --remote가 갱신합니다.
  • --recent는 저장된 hierarchy 안의 기존 page 내용만 갱신하고 children.v2.yaml은 갱신하지 않습니다.
  • 이전 conversion manifest가 소유권을 증명하는 stale MDX와 _meta.ts는 성공적인 변환 후 자동 삭제합니다.

재현 대상 구조

저장된 Confluence V1/V2 응답에서 확인한 구조는 다음과 같습니다.

관리자 매뉴얼 (544178405, page)
└─ MCP Server (2167636017, folder)
   ├─ MAC General Configurations (2167144528, page)
   ├─ MCP Server Connection Management (2167242794, page)
   └─ MCP Access Control (2167799919, page)

하위 page의 page.v2.yaml에는 folder 부모가 정확히 기록되어 있습니다.

parentId: "2167636017"
parentType: "folder"

현재 결과

대상 현재 결과
var/2167636017/ 생성되지 않습니다.
folder의 하위 page 디렉터리 --recent로 개별 page data가 저장될 수 있습니다.
var/pages.qm.yaml folder와 하위 page 3개가 모두 없습니다.
target/ko/administrator-manual/mcp-server.mdx 생성되지 않습니다.
target/ko/administrator-manual/mcp-server/ 하위 page MDX가 생성되지 않습니다.
관련 _meta.ts folder와 하위 page navigation이 없습니다.

convert_all.py는 catalog에 있는 항목만 처리하므로, 하위 page의 page.xhtml이 이미 저장되어 있어도 orphan 상태에서는 변환하지 않습니다.

원인

1. Page-only child endpoint를 사용합니다

현재 ApiClient.get_child_pages()는 다음 endpoint를 사용합니다.

GET /api/v2/pages/{id}/children

이 endpoint는 page만 반환합니다. 모든 직계 content type을 발견하려면 다음 endpoint가 필요합니다.

GET /api/v2/pages/{id}/direct-children

2. Folder child endpoint가 올바르지 않습니다

현재 folder 분기는 /api/v2/folders/{id}/children을 구성하지만 공식 직계 자식 endpoint는 다음과 같습니다.

GET /api/v2/folders/{id}/direct-children

3. 재귀 순회가 content type을 버립니다

현재 재귀 입력은 children.v2.yaml에서 추출한 ID 문자열뿐입니다. direct-childrentype: folder를 반환해도 다음 요청의 API routing에 전달할 수 없습니다.

4. Cache가 없는 non-root child를 page로 간주합니다

최초 발견한 non-root folder에는 page.v2.yaml cache가 없으므로 /api/v2/pages/{folder_id}를 잘못 호출합니다. 현재 folder 지원은 사실상 folder가 sync root인 일부 경우로 제한되어 있습니다.

5. Catalog가 content type을 보존하지 않습니다

현재 Pagepages.<code>.yaml에는 type이 없습니다. Fetch와 conversion 단계가 folder를 본문 없는 정식 노드로 구분할 수 없습니다.

6. Breadcrumb 생성이 V1 page ancestor에 의존합니다

Stage4Processorpage.v1.yaml이 없으면 catalog entry를 만들지 않습니다. Folder에는 page V1 body/ancestor를 요구할 수 없으므로 parent traversal context에서 breadcrumb와 path를 계산해야 합니다.

7. Navigation 생성이 XHTML 변환에 결합되어 있습니다

_meta.ts 생성은 개별 page.xhtml 변환의 side effect입니다. Folder는 XHTML이 없으므로 folder 아래 navigation을 생성할 실행 지점이 없습니다.

8. 출력 소유권 기록이 없어 이전 경로를 안전하게 정리할 수 없습니다

Folder 이동·이름 변경 시 새 경로를 생성하는 것만으로는 이전 folder와 descendant MDX가 남습니다. 변환기가 만든 파일과 수동 파일을 구분할 manifest가 필요합니다.

구현 디자인

R1. Typed direct-children 순회

재귀 호출은 ID가 아니라 최소 다음 구조를 전달합니다.

ContentRef
  id
  type
  title
  childPosition
  • page: /api/v2/pages/{id}/direct-children
  • folder: /api/v2/folders/{id}/direct-children
  • cursor pagination을 끝까지 수집합니다.
  • page, folder 외 type은 순회하지 않고 parent_id, id, type, title을 경고로 기록합니다.
  • pagination 중간 실패 시 부분 결과로 이전 children.v2.yaml을 덮어쓰지 않습니다.

R2. Folder raw data 저장

var/{folder_id}/
├── folder.v2.yaml
└── children.v2.yaml
  • folder.v2.yaml: /api/v2/folders/{id} metadata 응답
  • children.v2.yaml: pagination을 합친 직계 자식 snapshot
  • Folder에는 page.v1.yaml, page.v2.yaml, page.xhtml, attachments.v1.yaml을 만들지 않습니다.

R3. Typed catalog

기존 consumer 호환성을 위해 ID key는 page_id를 유지하고 type을 추가합니다.

- page_id: "2167636017"
  type: folder
  title: MCP Server
  breadcrumbs:
    - 관리자 매뉴얼
    - MCP Server
  path:
    - administrator-manual
    - mcp-server

Breadcrumb는 V1 ancestor에만 의존하지 않고 parent traversal context에서 계산합니다. Child 표시 title과 link path는 최신 catalog에서, 순서는 parent의 children.v2.yaml에서 해석합니다.

R4. 실행 mode별 hierarchy freshness

Mode Page 내용 Hierarchy
--remote 갱신 page/folder 전체 갱신
--recent CQL로 발견한 기존 page만 갱신 기존 children.v2.yaml 유지
--local 로컬 data 사용 기존 children.v2.yaml 유지

Folder 생성·이동·이름 변경·삭제는 다음 --remote 실행 때 반영되는 eventual consistency를 허용합니다.

R5. Folder MDX landing page

예상 파일:

target/ko/administrator-manual/mcp-server.mdx

예상 본문:

---
title: 'MCP Server'
confluenceUrl: 'https://querypie.atlassian.net/wiki/spaces/QM/folder/2167636017'
---

# MCP Server

## 하위 문서

- [MAC General Configurations](./mcp-server/mac-general-configurations)
- [MCP Server Connection Management](./mcp-server/mcp-server-connection-management)
- [MCP Access Control](./mcp-server/mcp-access-control)
  • childPosition 순서의 직계 자식 page/folder만 표시합니다.
  • Nested folder의 descendant를 현재 목록에 펼치지 않습니다.
  • Link는 현재 folder MDX에서 child MDX까지의 상대 경로로 계산하고 .mdx suffix를 제거합니다.
  • 지원되는 직계 자식이 없으면 하위 문서가 없습니다.를 표시합니다.
  • Folder MDX는 매 conversion에서 완전히 덮어씁니다.
  • Folder에는 XHTML sidecar 또는 reverse-sync mapping을 만들지 않습니다.

R6. Catalog-level navigation pass

_meta.ts 생성을 XHTML converter에서 분리하여 convert_all.py의 전체 catalog pass로 수행합니다.

administrator-manual/_meta.ts
  mcp-server: MCP Server

administrator-manual/mcp-server/_meta.ts
  mac-general-configurations: MAC General Configurations
  mcp-server-connection-management: MCP Server Connection Management
  mcp-access-control: MCP Access Control

Page/folder를 같은 규칙으로 포함하고 childPosition 순서를 유지합니다.

R7. Manifest 기반 stale output 삭제

var/convert-manifest.<sync-code>.yaml에 변환기가 생성한 MDX와 _meta.ts를 기록합니다.

  1. 이전 manifest를 읽습니다.
  2. 현재 catalog의 전체 output을 생성하고 검증합니다.
  3. 하나라도 실패하면 삭제와 manifest 교체를 하지 않습니다.
  4. 모두 성공하면 previous_paths - current_paths만 삭제합니다.
  5. output root 내부의 허용된 소유 파일인지 검증합니다.
  6. 비어 있는 directory만 제거하고 비소유 파일은 보존합니다.
  7. 현재 manifest를 atomic replace합니다.

Manifest 도입 전 stale 파일은 소유권을 증명할 수 없으므로 최초 실행에서는 삭제하지 않습니다. Folder 이동·이름 변경으로 경로가 바뀐 descendant MDX와 generated _meta.ts도 manifest 기준으로 정리합니다. Attachment cleanup은 이 issue 범위에 포함하지 않습니다.

R8. 기존 profile 호환성

  • QM의 page root와 QCP의 folder root를 같은 typed model로 처리합니다.
  • Sync root는 catalog/path 기준으로 유지하되 기존과 같이 root 자체 MDX는 생성하지 않습니다.
  • 기존 page XHTML, attachment, MDX path와 title translation 동작을 회귀시키지 않습니다.

테스트

API client

  • page/folder direct-children endpoint 선택
  • cursor 2개 이상의 pagination merge
  • 중간 실패 시 기존 snapshot 보존

Fetch/tree

root page
└─ page
   └─ folder
      ├─ page A
      ├─ nested folder
      │  └─ page B
      └─ whiteboard (unsupported)
  • type이 재귀 호출과 catalog까지 유지됩니다.
  • folder와 하위 page/folder path가 올바릅니다.
  • unsupported child가 제외되고 경고가 남습니다.
  • --remote만 hierarchy를 갱신합니다.
  • --recent, --local은 cached hierarchy를 재사용합니다.

Conversion

  • folder frontmatter, H1, 직계 자식 목록, 상대 link, ordering
  • nested folder가 한 항목으로만 표시됨
  • 빈 folder 안내 문구
  • page/folder 혼합 _meta.ts
  • folder 이동·이름 변경·삭제 후 stale 소유 파일 삭제
  • conversion 실패 시 이전 output/manifest 보존
  • QM 2167636017과 QCP folder root smoke

완료 조건

  • var/2167636017/folder.v2.yamlchildren.v2.yaml이 생성됩니다.
  • Folder에는 page-only body/attachment artifact를 생성하지 않습니다.
  • pages.qm.yaml에 folder와 하위 page 3개가 type과 함께 포함됩니다.
  • administrator-manual/mcp-server.mdx가 생성됩니다.
  • Folder MDX에 직계 자식 3개만 Confluence 순서로 표시됩니다.
  • Nested folder는 link 한 항목으로 표시되고 descendant를 펼치지 않습니다.
  • 빈 folder도 안내 문구가 있는 MDX를 생성합니다.
  • Parent와 folder directory의 _meta.ts가 XHTML 유무와 관계없이 생성됩니다.
  • --remote가 hierarchy 변경을 반영합니다.
  • --recent는 hierarchy를 갱신하지 않고 기존 page 내용만 갱신합니다.
  • 이전 manifest가 소유한 stale MDX와 _meta.ts만 성공적인 변환 후 삭제됩니다.
  • database, whiteboard, embed는 제외되고 경고가 남습니다.
  • 기존 QM page root와 QCP folder root가 회귀하지 않습니다.
  • mixed tree와 실제 대상 folder 기반 자동화/smoke 테스트가 통과합니다.

검토 결론

Issue #1028을 endpoint 변경만으로 해결해서는 folder를 올바르게 처리할 수 없습니다. Typed tree, folder raw storage, catalog type, folder MDX generator, catalog-level navigation, mode별 hierarchy freshness, manifest cleanup까지 함께 구현하면 승인된 folder 처리 요구사항을 충족할 수 있습니다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions