Discourse에서 Markdown으로 변환하는 플러그인

**discourse-to-markdown**는 클라이언트가 Accept: text/markdown 헤더를 보내거나 콘텐츠 URL에 .md를 붙일 때 포럼 콘텐츠를 Markdown으로 반환하는 새로운 플러그인입니다.

우리는 https://discourse.roots.io의 자체 포럼에서 이를 실행하고 있습니다:

curl -H "Accept: text/markdown" https://discourse.roots.io/latest
curl https://discourse.roots.io/t/serve-your-wordpress-posts-as-markdown/30321.md

LLM에 HTML을 공급하는 것은 비용이 많이 들며, 콘텐츠만 포함된 Markdown을 서빙하면 토큰 사용량을 3~5배까지 줄일 수 있습니다. 이는 더 저렴한 API 호출, 더 빠른 응답, 그리고 모델이 추론할 때 컨텍스트 윈도우에 더 많은 여유 공간을 의미합니다. 더 자세한 설명과 모든 사이트의 준비 상태 확인은 https://acceptmarkdown.com을 참고하세요.

클라이언트가 Markdown을 요청하는 방법

세 가지 진입점이 있습니다:

  1. Accept: text/markdown 헤더 (LLM에 이상적)
  2. .md URL 접미사
  3. Discovery (모든 HTML 응답은 Link: <...>; rel="alternate"; type="text/markdown" 헤더와 <head> 내의 <link rel="alternate"> 태그를 통해 해당 Markdown 버전을 광고하며, RSS 피드는 Markdown 버전으로 가리키는 <atom:link>를 포함합니다)

지원되는 경로

경로 HTML Markdown
토픽 /t/:slug/:id /t/:slug/:id.md
단일 게시글 /t/:slug/:id/:post_number /t/:slug/:id/:post_number.md
카테고리 /c/:slug/:id /c/:slug/:id.md
태그 /tag/:tag /tag/:tag.md
최신 /latest /latest.md
인기 /top /top.md
/hot /hot.md
사용자 활동 /u/:username/activity /u/:username/activity.md

설치

플러그인을 app.yml에 추가합니다:

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/roots/discourse-to-markdown.git

컨테이너를 재빌드합니다:

cd /var/discourse
./launcher rebuild app

그런 다음 관리자 → 설정 → 플러그인 → Markdown 출력에서 활성화합니다.

변환에 대한 참고 사항

이 플러그인은 raw가 아니라 Discourse의 cooked HTML을 변환합니다. cooked HTML은 독자가 보는 렌더링된 표현으로, 원박스(oneboxes)가 확장되고, 멘션이 링크되며, 인용이 출처를 명시한 상태입니다. 이는 독자가 실제로 보는 것을 보존하고, 어떤 GFM 호환 렌더러에서도 출력을 이동 가능하게 유지합니다. Discourse 전용 구조(인용, 원박스, 디테일, 멘션, 해시태그, 이모지, 라이트박스, 투표)는 변환 전에 적절히 재작성됩니다.

변환된 Markdown은 post.id + post.updated_at을 키로 사용하여 게시글별로 Redis에 캐시되며, 편집 시 자동으로 무효화됩니다.

설정

설정 기본값 목적
discourse_to_markdown_enabled false 플러그인의 마스터 스위치
discourse_to_markdown_md_urls_enabled true HTML 경로의 쌍으로 .md URL 접미사를 허용
discourse_to_markdown_strict_accept false 클라이언트의 Accept 헤더가 text/htmltext/markdown을 모두 제외할 때 406 Not Acceptable 반환
discourse_to_markdown_emit_vary true 캐시가 서로 다른 표현을 교차 서빙하지 않도록 Markdown 및 406 응답에 Vary: Accept 출력
discourse_to_markdown_include_post_metadata true Markdown 표현에 URL, 카테고리, 태그, 작성자, 타임스탬프 포함

리소스

10개의 좋아요

정말 좋습니다, 공개해 주셔서 감사합니다!

꽤 멋진 접근 방식입니다. Discourse의 cooking 인프라를 활용하기 때문에 원본 출력보다 더 풍부하고 완성도 높은 마크다운을 제공합니다.

2개의 좋아요

와, 제가 찾고 있던 바로 그거네요! 타이밍이 정말 좋습니다. 우리는 API나 MCP를 자주 사용하는데, 원본 콘텐츠에 이미지의 실제 URL이 해결되지 않은 상태인 것이 항상 좀 거슬렸거든요.

수고 많으셨습니다!

수정: 혹시 이걸 Discourse MCP에도 사용할 수 있을까요?

1개의 좋아요

도움이 되셨다니 기쁩니다!

Discourse MCP는 콘텐츠 협상(content negotiation)을 선택적 경로로 추가할 수 있습니다. 이 플러그인을 필수로 요구할 필요는 없으며, 정통 주제/게시물(canonical topic/post) URL에서 Accept: text/markdown을 요청하고, 사이트가 Markdown을 지원하지 않는 경우 현재 JSON API 동작으로 폴백(fallback)할 수 있습니다.

이 플러그인은 현재 Discourse 사이트가 해당 요청을 충족시키는 하나의 방법일 뿐입니다. 이 플러그나 Discourse 코어에 대한 동등한 지원이 없다면, Accept 헤더만으로는 JSON API 출력을 변경할 수 없습니다.

따라서 이상적인 MCP 통합 방식은 다음과 같을 수 있습니다: 먼저 콘텐츠 URL에서 text/markdown을 시도하고, 그런 다음 /t/:id.json?include_raw=true로 폴백하는 것입니다.

1개의 좋아요

그렇게 말하니 재미있네요. Claude가 정확히 그 방식으로 해결했거든요…