<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://blog.markab-j.com/</id>
  <title type="text">markab-j 블로그</title>
  <subtitle type="text">개인 블로그</subtitle>
  <updated>2026-10-22T17:30:00.000Z</updated>
  <author><name>markab-j</name></author>
  <link rel="alternate" href="https://blog.markab-j.com/"/>
  <link rel="self" href="https://blog.markab-j.com/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://blog.markab-j.com/posts/astro-pele-diagrams/</id>
      <title type="text">블로그 다이어그램 렌더러를 Pele로 바꾸기.</title>
      <published>2026-10-22T17:04:00.000Z</published>
      <updated>2026-10-22T17:30:00.000Z</updated>
      <author><name>markab-j</name></author>
      <link rel="alternate" href="https://blog.markab-j.com/posts/astro-pele-diagrams/"/>
      <summary type="text">렌더러를 Merman에서 Pele로 바꿨다.</summary>
      <content type="html"><![CDATA[<section><h2>시발점<a href="#시발점"><span>#</span></a></h2><p>이제 막 블로그를 꾸미고 있다. Firefly 템플릿을 적용하고 설정과 코드를 하나씩 살펴보다가, 글에 Mermaid 다이어그램을 넣을 수 있다는 걸 발견했다.</p><p>그러자 전에 옵시디언 쪽에서 봤던 <a href="https://github.com/obsidianmd/pele" target="_blank">Pele</a>가 떠올랐다.</p><p>아직 글도 별로 없는데 렌더러부터 바꿀 필요가 있나 싶었지만, 블로그를 손보는 김에 붙여보고 싶었다.</p><p>처음에는 Mermaid.js를 교체하면 될 줄 알았다. 그런데 코드를 열어보니 이 블로그 템플릿은 이미 Merman으로 빌드할 때 SVG를 만들고 있었다.</p><p>이미 충분히 빠르겠지만, 그냥 생각한 거 바꾸기로 했다.</p></section>
<section><h2>Pele는 무엇인가<a href="#pele는-무엇인가"><span>#</span></a></h2><figure>
  <img src="https://pele.run/pele-home.svg" alt="화산과 여성의 옆모습처럼 보이는 Pele 공식 아이콘" width="449" height="336" loading="lazy" />
  <figcaption>
    <a href="https://pele.run/">Pele 공식 아이콘</a>
  </figcaption>
</figure><p>Pele는 Mermaid 문법을 읽어 SVG를 만드는 렌더러다.</p><p>이름은 화산과 불의 여신 펠레에서 따왔다고 한다.
맨 처음 봤을 때, 화산 같기도 사람의 옆모습 같기도 했던 아이콘이 정말 이중적 의미를 담고 있었을 줄은 몰랐다.</p><p>아이콘 이쁘지 않나. 되게 잘 만든 아이콘 같은데.</p><p><a href="https://pele.run/#why-pele" target="_blank">공식 홈페이지</a>는 Pele의 장점을 다음과 같이 소개한다. 아래 수치는 Pele에서 주장하는 <strong>Pele와 Mermaid.js의 공식 비교</strong>다.</p>

<table><thead><tr><th>항목</th><th>Pele</th><th>Mermaid.js</th></tr></thead><tbody><tr><td>gzip 압축 번들 크기</td><td>230 kB</td><td>1.6 MB</td></tr><tr><td>렌더링 시간</td><td>0.05–0.3 ms</td><td>6–82 ms</td></tr><tr><td>직접 의존성 수</td><td>0</td><td>23</td></tr><tr><td>테마 설정</td><td>CSS 변수</td><td>테마 설정 객체</td></tr></tbody></table><p>공식 소개의 표현으로는 최대 500배 빠르다고 한다.</p><p>뭐 자기네들 기술이 제일 좋다고 하겠으니 저렇게 극단적인 수치가 아닐 수도 있지만, 빠른 건 확실하겠지.</p></section>
<section><h2>Astro에 Pele 연결하기<a href="#astro에-pele-연결하기"><span>#</span></a></h2><p>블로그 템플릿에서 제공하는 기존 구조를 유지하면서 다이어그램을 만드는 부분만 바꿨다.</p><p>적용한 Pele 버전은 <code>1.8.0</code>이다.</p><p>먼저 Pele 자체의 사용법은 짧다. <code>render()</code>에 Mermaid 문법을 전달하면 SVG 문자열을 받을 수 있다. 다음 코드는 Pele가 설치된 프로젝트에서 <code>.mjs</code> 파일로 실행할 수 있다.</p><div><figure><figcaption><span>pele 사용법.mjs</span></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>render</span><span> } </span><span>from</span><span> </span><span>"pele"</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>source</span><span> </span><span>=</span><span> </span><span>`flowchart LR</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>A[가] --&gt; B[나] --&gt; C[다]`</span><span>;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>const</span><span> { </span><span>svg</span><span> } </span><span>=</span><span> </span><span>render</span><span><span>(</span><span>source</span><span>, {</span></span></div></div><div><div><div>7</div></div><div><span>  </span><span>idPrefix</span><span>:</span><span> </span><span>"example-0"</span><span>,</span></div></div><div><div><div>8</div></div><div><span>  </span><span>fontFamily</span><span>:</span><span> </span><span>"system-ui, sans-serif"</span><span>,</span></div></div><div><div><div>9</div></div><div><span>  </span><span>autoDirection</span><span>:</span><span> </span><span>false</span><span>,</span></div></div><div><div><div>10</div></div><div><span>});</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span><span>console</span><span>.</span></span><span>log</span><span><span>(</span><span>svg</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div>가나다</div></div><section><h3>구조<a href="#구조"><span>#</span></a></h3><p>정적 블로그를 직접 만들어 본 사람들은 알겠지만, <code>remark-*</code>, <code>rehype-*</code>라는 라이브러리를 매우 많이 볼 수 있다.</p><ul>
<li>remark:</li>
<li>rehype:</li>
</ul><p>그래서 이 블로그에서는 작성할 코드는 생각보다 적었다.</p><div><div>Markdown 코드 블록remarkPele: 대상 추출rehypePele: SVG 생성기존 확대·축소 기능 연결글의 HTML</div></div></section><section><h3>remark: 다이어그램 코드 블록 찾기<a href="#remark-다이어그램-코드-블록-찾기"><span>#</span></a></h3><p><code>src/plugins/remark-pele.js</code>는 언어가 <code>mermaid</code> 코드 블록을 골라낸다.
일반 코드 하이라이터로 넘어가지 않도록 노드 타입을 바꾸고, 다음 단계에서 읽을 원본을 남긴다.</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> { </span><span>visit</span><span> } </span><span>from</span><span> </span><span>"unist-util-visit"</span><span>;</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>export</span><span> </span><span>function</span><span> </span><span>remarkPele</span><span>() {</span></div></div><div><div><div>4</div></div><div><span>  </span><span>return</span><span><span> (</span><span>tree</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>5</div></div><div><span>    </span><span>visit</span><span><span>(</span><span>tree</span><span>, </span></span><span>"code"</span><span><span>, (</span><span>node</span><span>) </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>6</div></div><div><span>      </span><span>if</span><span><span> (</span><span>node</span><span>.</span></span><span>lang</span><span> </span><span>===</span><span> </span><span>"mermaid"</span><span>) {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>const</span><span> </span><span>code</span><span> </span><span>=</span><span><span> </span><span>node</span><span>.</span></span><span>value</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>node</span><span>.</span><span>type</span><span> </span><span>=</span><span> </span><span>"pele"</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>node</span><span>.</span><span>data</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>10</div></div><div><span>          </span><span>hName</span><span>:</span><span> </span><span>"div"</span><span>,</span></div></div><div><div><div>11</div></div><div><span>          </span><span>hProperties</span><span>:</span><span> {</span></div></div><div><div><div>12</div></div><div><span>            </span><span>className</span><span>:</span><span> [</span><span>"pele-source"</span><span>],</span></div></div><div><div><div>13</div></div><div><span>            </span><span>"data-pele-code"</span><span>:</span><span><span> </span><span>code</span><span>,</span></span></div></div><div><div><div>14</div></div><div><span><span>          </span></span><span>},</span></div></div><div><div><div>15</div></div><div><span>          </span><span>hChildren</span><span>:</span><span> [{ </span><span>type</span><span>:</span><span> </span><span>"text"</span><span>, </span><span>value</span><span>:</span><span><span> </span><span>code</span><span> }],</span></span></div></div><div><div><div>16</div></div><div><span><span>        </span></span><span>};</span></div></div><div><div><div>17</div></div><div><span><span>        </span></span><span>node</span><span>.</span><span>value</span><span> </span><span>=</span><span> </span><span>undefined</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>      </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>};</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>더 보기</span><span>간략히 보기</span></div></div></section><section><h3>rehype: 원본을 SVG로 바꾸기<a href="#rehype-원본을-svg로-바꾸기"><span>#</span></a></h3><p><code>src/plugins/rehype-pele.mjs</code>에서는 <code>pele-source</code> 요소를 찾아 원본을 <code>render()</code>에 전달한다. 아래는 실제 플러그인의 렌더링 부분을 발췌한 코드다.</p><p>remark에서 변환된 html 구조를 토대로 pele의 렌더링을 적용시키면 된다.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> { </span><span>svg</span><span> } </span><span>=</span><span> </span><span>render</span><span><span>(</span><span>source</span><span>, {</span></span></div></div><div><div><div>2</div></div><div><span>  </span><span>idPrefix</span><span>:</span><span> </span><span>`pele-</span><span>${</span><span>diagramIndex</span><span>++</span><span>}</span><span>`</span><span>,</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>...</span><span><span>options</span><span>,</span></span></div></div><div><div><div>4</div></div><div><span>});</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>node</span><span>.</span></span><span>children</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>7</div></div><div><span>  </span><span>h</span><span>(</span></div></div><div><div><div>8</div></div><div><span>    </span><span>"div"</span><span>,</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>{ </span><span>class</span><span>:</span><span> </span><span>`</span><span>${</span><span>DIAGRAM_WRAPPER</span><span>}</span><span> </span><span>${</span><span>PELE_WRAPPER</span><span>}</span><span>`</span><span> },</span></div></div><div><div><div>10</div></div><div><span>    </span><span>fromHtml</span><span><span>(</span><span>svg</span><span>, { </span></span><span>fragment</span><span>:</span><span> </span><span>true</span><span> }).</span><span>children</span><span>,</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>),</span></div></div><div><div><div>12</div></div><div><span>];</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>마지막으로 <code>astro.config.mjs</code>에서 <code>remarkPele</code>와 <code>[rehypePele, peleConfig]</code>를 기존 플러그인 목록에 등록했다.</p></section></section>
<section><h2>가장 마음에 든 변화<a href="#가장-마음에-든-변화"><span>#</span></a></h2><p>바꾸고 보니 의미없는 변경은 아니었다.</p><p>무려 css로 색상을 관리할 수 있게 됐다.</p><p>…</p><p>사실 theme 기능을 merman에서도 지원은 하고 있었다. 그게 조금 귀찮게 생겼을 뿐이지.</p><p>아무튼 좋은게 좋은 거니까.</p><p>배경과 노드 색상은 블로그가 이미 쓰는 변수에 연결해서 통일성을 유지하기 쉬워졌다.</p></section>
<section><h2>꼭 바꿔야 했을까<a href="#꼭-바꿔야-했을까"><span>#</span></a></h2><p>사실 꼭 필요한 작업은 아니었다. 이미 Merman으로 빌드할 때 SVG를 만들고 있었고, 블로그에 성능 문제를 느낄 만큼 다이어그램이 많은 것도 아니었다.</p><p>속도 때문에 바꿔야 할 이유도 없었다.</p><p>이제 블로그 꾸미는 것도, 이쯤 손봤으니 여기에 넣을 글을 써야겠다.</p><p>이래놓고 또 꾸미러 갈 수도 있겠지만.</p></section>]]></content>
    </entry>
</feed>
