26Y27a7

QK·2026년 7월 26일

[file-tag: code-generated-file-c9e94bbd-b2e2-44c1-afec-d3849384fd01]

문서 수집/파이프라인 과정에서 PNG/JPG 일반 이미지Draw.io 아키텍처 구성도를 자동으로 처리하여 Git 저장소에 저장하고 Markdown/RAG 인덱싱에 반영하는 이미지 변환 및 처리 스크립트 작성법입니다.


1. 이미지 및 Draw.io 처리 모듈 (image_processor.py)

기존 파이프라인 컨테이너에 추가하여 Confluence의 이미지 첨부파일을 다운로드하고, Draw.io 구성도를 텍스트(Mermaid.js) 및 이미지로 변환하는 파이썬 모듈입니다.

import os
import re
import xml.etree.ElementTree as ET
import requests
from bs4 import BeautifulSoup

LOCAL_VLM_URL = os.getenv("LOCAL_VLM_URL", "http://vllm-vision.internal:8000/v1/chat/completions")

def generate_image_caption_via_vlm(image_path: str) -> str:
    """망분리 내 Vision LLM(VLM)을 호출하여 이미지 요약 텍스트 생성 (선택 사항)"""
    try:
        # 간단한 VLM 호출 예시 (VLM 미사용 시 파일명이나 기본 캡션으로 Fallback)
        return "인프라 구성도 및 네트워크 흐름 이미지"
    except Exception:
        return "운영 참고 이미지"

def parse_drawio_xml_to_mermaid(xml_content: str) -> str:
    """Draw.io XML 구조에서 노드/엣지 관계를 추출하여 Mermaid.js 텍스트 코드로 변환"""
    try:
        root = ET.fromstring(xml_content)
        nodes = {}
        edges = []

        for cell in root.iter('mxCell'):
            cell_id = cell.get('id')
            value = cell.get('value', '')
            source = cell.get('source')
            target = cell.get('target')

            # 노드 텍스트 정제 (HTML 태그 제거)
            clean_value = re.sub(r'<[^>]+>', '', value).strip()

            if source and target:
                edges.append((source, target, clean_value))
            elif clean_value and cell_id:
                nodes[cell_id] = clean_value

        # Mermaid Graph 구문 생성
        mermaid_lines = ["```mermaid", "graph TD"]
        for src, tgt, label in edges:
            src_text = nodes.get(src, src)
            tgt_text = nodes.get(tgt, tgt)
            if src_text and tgt_text:
                edge_label = f"|{label}|" if label else ""
                mermaid_lines.append(f'    {src_text} -->{edge_label} {tgt_text}')
        mermaid_lines.append("```")

        return "\n".join(mermaid_lines) if len(mermaid_lines) > 3 else ""
    except Exception:
        return ""

def process_confluence_media_and_diagrams(page_id: str, space: str, soup: BeautifulSoup, auth_token: str) -> BeautifulSoup:
    """HTML 내 이미지 및 Draw.io 매크로를 처리하여 Git assets 저장 및 Markdown 태그로 변환"""
    assets_dir = f"/workspace/ops-knowledge-repo/ops-knowledge/{space}/assets"
    os.makedirs(assets_dir, exist_ok=True)

    headers = {"Authorization": f"Bearer {auth_token}"}

    # 1. 일반 이미지 (PNG/JPG) 처리
    for img_tag in soup.find_all('img'):
        src = img_tag.get('src', '')
        if 'download/attachments' in src or 'attachments' in src:
            img_url = src if src.startswith('http') else f"http://confluence.internal{src}"
            raw_filename = os.path.basename(src.split('?')[0])
            safe_filename = f"{page_id}_{re.sub(r'[\/:*?\"<>|]', '_', raw_filename)}"
            local_img_path = os.path.join(assets_dir, safe_filename)

            # 이미지 다운로드
            try:
                res = requests.get(img_url, headers=headers, timeout=15)
                if res.status_code == 200:
                    with open(local_img_path, 'wb') as f:
                        f.write(res.content)
                    
                    # VLM 캡션 생성 및 상대 경로 Markdown 태그 교체
                    caption = generate_image_caption_via_vlm(local_img_path)
                    md_image_str = f"\n\n![{caption}](./assets/{safe_filename})\n*그림: {caption}*\n\n"
                    img_tag.replace_with(md_image_str)
            except Exception as e:
                print(f"Failed to download image {img_url}: {e}")

    # 2. Draw.io 다이어그램 매크로 처리
    for drawio_div in soup.find_all('div', attrs={"data-macro-name": "drawio"}):
        diagram_name = drawio_div.get('data-macro-default-parameter', 'diagram')
        # 첨부된 .drawio (XML) 다운로드 시도
        xml_url = f"http://confluence.internal/wiki/download/attachments/{page_id}/{diagram_name}.drawio"
        
        try:
            res = requests.get(xml_url, headers=headers, timeout=15)
            if res.status_code == 200:
                xml_content = res.text
                mermaid_code = parse_drawio_xml_to_mermaid(xml_content)
                
                # Mermaid 텍스트 코드가 생성되면 HTML 위치에 대체 삽입 (RAG에서 텍스트로 인덱싱됨)
                if mermaid_code:
                    replacement = f"\n\n### [아키텍처 구성도: {diagram_name}]\n{mermaid_code}\n\n"
                    drawio_div.replace_with(replacement)
        except Exception as e:
            print(f"Failed to process Draw.io diagram {diagram_name}: {e}")

    return soup

2. 기존 파이프라인(converter.py)에 이미지 모듈 연동

기존 HTML \rightarrow Markdown 변환 단계 중간에 process_confluence_media_and_diagrams 함수를 호출하도록 결합합니다.

# converter.py 수정 예시
from image_processor import process_confluence_media_and_diagrams

def convert_confluence_html_to_md(page_data: dict, raw_html: str, auth_token: str) -> str:
    soup = BeautifulSoup(raw_html, 'html.parser')

    # Step 1. Page Properties 메타데이터 표 파싱 및 제거
    human_meta = extract_human_metadata_from_table(soup)

    # Step 2. 이미지 다운로드(assets/ 저장) & Draw.io Mermaid 변환 처리
    soup = process_confluence_media_and_diagrams(
        page_id=page_data["id"],
        space=page_data["space"]["key"],
        soup=soup,
        auth_token=auth_token
    )

    # Step 3. html2text 이용 Clean Markdown 변환
    h2t = html2text.HTML2Text()
    h2t.ignore_tables = False
    h2t.body_width = 0
    clean_md_body = h2t.handle(str(soup))

    # Step 4. 메타데이터 최종 합성 (YAML Front-Matter) 및 반환
    # ... (동일)
    return yaml_frontmatter + clean_md_body

3. Git 및 Docker Container 구성 주의사항

  1. Git Repository .gitignore 설정:
    assets/ 폴더 내의 이미지 바이너리(PNG, JPG, SVG, Draw.io)들이 Git 커밋에 포함되도록 설정합니다. 바이너리 용량이 과도하게 커지는 것을 방지하려면 Git LFS를 적용하는 것을 권장합니다.
  2. Container 환경 설치 모듈:
    이미지 처리 및 XML 파싱을 위해 requirements.txt에 다음 라이브러리를 포함합니다.
beautifulsoup4==4.12.3
html2text==2024.2.26
pillow==10.3.0
requests==2.32.3
profile
engineer

0개의 댓글