Marp IFrame 경로 에러 해결 방법

Marp IFrame 경로 에러 해결 방법

HugoMarp를 통합하여 슬라이드 문서를 자동으로 변환하고 표시하는 작업 중, 경로 처리 문제로 인해 예상치 못한 오류가 발생했습니다. 이 글에서는 문제의 현상, 원인, 해결 과정을 상세히 설명하며, 최종적으로 경로 문제를 해결한 방법을 공유합니다.

문제 현상

  • Hugo 템플릿에서 iframe을 사용하여 변환된 Marp 슬라이드를 삽입하려 할 때, 404This page could not be found 에러 발생
  • src 경로가 다음과 같이 잘못 생성됨
<iframe src=/slides%5cc%5co%5cn%5ct%5ce%5cn%5ct%5cethercat-presentation.html width=100% height=800 frameborder=0></iframe>

현상의 주요 특징

  • src 경로가 URL 인코딩되어 %5c(백슬래시) 형태로 표시.
  • 슬라이드 HTML 파일은 static/slides 디렉토리에 정상적으로 생성되었음에도 불구하고 iframe이 올바른 파일을 로드하지 못함.
  • 결과적으로 브라우저에서 슬라이드가 표시되지 않음.

문제 발생 원인

Hugo의 경로 처리

  • Hugo는 파일 경로를 Windows 스타일(\)로 반환하며, 이는 웹 브라우저가 이해할 수 없는 백슬래시가 포함된 URL을 생성함.
  • 특히 .File.Dir과 같은 Hugo 템플릿 함수는 운영 체제의 파일 경로 형식을 그대로 반환하므로, Windows 환경에서는 이 문제가 발생함.

URL 인코딩

  • Hugo 템플릿에서 생성된 경로가 브라우저로 전달되는 과정에서 백슬래시(\)가 %5c로 URL 인코딩됨.
  • 이는 웹 경로가 슬래시(/)만 지원하기 때문에 발생

계산 방식의 제한

  • Hugo 템플릿 코드에서 .File.Dir.File.BaseFileName을 조합해 경로를 생성할 때, 백슬래시를 슬래시로 변환하거나 불필요한 부분을 제거하는 작업이 불완전함(?)

문제 해결 과정

템플릿에서 경로 처리 로직 수정

  • Hugo 템플릿에서 replace "\\" "/"와 같은 변환 작업을 통해 경로 수정
<iframe 
    src="/slides{{ .File.Dir | replace "content" "" | replace "\\" "/" | lower }}{{ .File.BaseFileName | lower }}.html" 
    width="100%" 
    height="800"
    frameborder="0">
</iframe>
  • 여러 번의 시도에도 백슬래시 문제가 완전히 해결되지 않음.

.RelPermalink 활용

  • Hugo의 .RelPermalink는 파일의 상대 경로를 자동으로 생성해 주는 함수로, 운영 체제의 경로 형식과 무관하게 URL 형태를 반환함
  • 이를 사용하여 경로를 생성하려 했지만, 템플릿 상에서 .md 확장자를 .html로 변환하는 작업이 여전히 필요함을 확인.
<iframe 
    src="/slides{{ .RelPermalink | replace ".md" ".html" }}" 
    width="100%"
    height="800"
    frameborder="0">
</iframe>
  • 이 접근 방식은 .RelPermalink가 슬래시(/)를 보장했으나, 변환 과정에서 Markdown 파일의 상대 경로만 처리할 수 있어 모든 케이스를 커버하지 못함

최종 해결 방안

메타데이터에 명시적 경로 추가

  • 마지막으로, 템플릿과 경로 계산 문제를 완전히 제거하기 위해 Markdown 메타데이터에 슬라이드 경로를 명시적으로 추가하는 방법을 채택
  • Python 스크립트를 수정하여 변환된 HTML 파일 경로를 자동으로 메타데이터에 삽입하도록 설정

Python 스크립트를 통해 Marp로 변환된 HTML 파일의 경로를 계산하고, 이를 slide_path라는 메타데이터 항목으로 Markdown 파일에 추가함.

Python 스크립트

def parse_and_update_yaml_header(file_path, slide_path):
    """Markdown 파일의 YAML 헤더를 파싱하고 slide_path를 추가"""
    with open(file_path, "r", encoding="utf-8") as f:
        lines = f.readlines()

    if lines and lines[0].strip() == "---":
        yaml_lines = []
        for line in lines[1:]:
            if line.strip() == "---":
                break
            yaml_lines.append(line)
        metadata = yaml.safe_load("\n".join(yaml_lines))
        metadata['slide_path'] = slide_path  # slide_path 추가

        new_metadata = yaml.dump(metadata, default_flow_style=False).strip()
        rest_of_content = "".join(lines[len(yaml_lines) + 2:])

        # 파일 갱신
        with open(file_path, "w", encoding="utf-8") as f:
            f.write(f"---\n{new_metadata}\n---\n{rest_of_content}")

변환된 경로는 /slides/... 형식으로 작성되며, Markdown 파일의 메타데이터에 추가됨.

Markdown 파일 예시

---
title: EtherCAT Presentation
type: slide
slide_path: /slides/ethercat/ethercat-presentation.html
---

Hugo 템플릿 : Single.html

{{ define "main" }}
  <div class='hx-mx-auto hx-flex {{ partial "utils/page-width" . }}'>
    <article class="hx-w-full hx-break-words hx-flex hx-min-h-[calc(100vh-var(--navbar-height))] hx-min-w-0 hx-justify-center hx-pb-8 hx-pr-[calc(env(safe-area-inset-right)-1.5rem)]">
      <main class="hx-w-full hx-min-w-0 hx-max-w-6xl hx-px-6 hx-pt-4 md:hx-px-12">
        <br class="hx-mt-1.5 hx-text-sm" />

        <h1 class="hx-text-center hx-mt-2 hx-text-4xl hx-font-bold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100">
          {{ .Title }}
        </h1>

        {{ if eq .Params.type "slide" }}
          <div class="marp-slide-container">
            <iframe 
    src="{{ .Params.slide_path }}" 
    width="100%" 
    height="800" 
    frameborder="0"
    class="hx-w-full hx-shadow-md">
</iframe>

          </div>
        {{ else }}
          <div class="content">
            {{ .Content }}
          </div>
        {{ end }}
      </main>
    </article>
  </div>
{{ end }}