본문 바로가기

javascript/이외

mermaid.js - 프론트엔드에서 UML을 그리는 라이브러리

과거 사용한 draw.io 파일들: https://github.com/fish9903/Bus-Tour/tree/main/drawio 

 학교에서 소프트웨어 공학 프로젝트를 수행할 때 UML을 다룬 적이 있었다. 당시에는 UML 이미지를 얻는 것 자체가 목적이었기 때문에 plantUML, draw.io을 병행해서 사용했고, 설계 과정에서 큰 도움을 받았다.

 최근 트리 자료구조를 공부하면서 이를 시각화하기 위해 라이브러리를 찾던 중, 위 언급한 plantUML과 문법이 비슷하면서 자바스크립트 기반으로 작성된 라이브러리를 발견했는데, 그게 오늘 설명할 mermaid.js이다.


Mermaid.js

https://mermaid.js.org/#/

 

Mermaid | Diagramming and charting tool

Mermaid Diagramming and charting tool JavaScript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically.

mermaid.js.org

 mermaid.js는 UML을 정의하는 텍스트를 이미지로 동적 변환하는 라이브러리이다. UML을 정의할 때 텍스트를 사용한다는 부분이 plantUML과 비슷하며, 정의할 때 사용되는 문법도 비슷한 부분이 많다.

plantUML과의 유사성

 실제 예시를 들어 살펴보자. 다음 그림은 본인이 프로젝트 진행 중 설계한 데이터베이스의 구조이다. 해당 그림은 draw.io을 통해 그렸으며, 여기에서 찾아볼 수 있다.

코딩 문제에 대한 ER 다이어그램. https://github.com/blaxsior/codesolveronline 참고

 데이터베이스 구조에서 M : N 관계를 위해 추가한 _proInitcodes을 제외한 나머지 엔티티들을 plantUML 및 Mermaid의 공식 페이지에서 제공하는 라이브 에디터를 이용하여 구성해보자. 사용된 코드와 생성된 이미지는 아래와 같다.

plantUML

@startuml
entity TestCase {
  *Id
  input
  output
  type
}

entity Problem {
  *Id
  title
  description
}

entity InitCode {
  *Id
  type
  code
}

TestCase }|-right-|| Problem
Problem }|--o{ InitCode
@enduml

Mermaid

erDiagram
    TestCase {
    int Id
    string input
    string output
    string type
    }

    Problem {
    int Id
    string title
    string description
    }

    InitCode {
    int Id
    string type
    string code
    }

    TestCase }|--|| Problem : has
    Problem }o--|{ InitCode : pinitcode

 위에서 볼 수 있듯이 둘은 문법이 비슷하며, 출력되는 결과도 비슷한 모습을 볼 수 있다. 사실 두 도구는 각각의 홈페이지에서 제공하는 라이브 에디터를 이용하는 수준에서는 거의 차이가 없다.

그런데 두 도구를 이용하여 그래프를 생성하는 동작이 있는 프로젝트를 진행하는 경우에는 말이 달라진다. 그 이유는 두 라이브러리를 작성한 언어가 다르기 때문이다. 

(좌) plantuml을 구성하는 언어 비율, (우) mermaid.js을 구성하는 언어 비율 

 plantUML은 자바 언어 기반으로 작성되어 있으며, 데이터는 서버측에서 처리된다. 이 경우 사용자가 전달한 plantUML을 서버로 전달하면, 이에 대응되는 이미지를 서버 상에서 생성한 후 다시 클라이언트 측으로 반환하는 방식으로 동작하게 된다. 따라서 웹 프로젝트에서 plantUML을 이용하여 이미지를 생성하기 위해서는 서버가 필요하다. 단순한 정적 페이지 기능을 가진다고 해도 반드시 서버와의 통신이 필요하다는 부분은 단점으로 작용할 수 있다.

 반면 Mermaid는 자바스크립트로 언어 기반으로 작성되어 있으므로 프론트엔드 수준에서 작업이 완결된다. 따라서 간단한 프로젝트 수준에서는 서버를 배제할 수 있으며, 서버가 사용되는 경우라도 실제 연산을 클라이언트에서 수행함으로써 서버에 가해지는 부하를 줄일 수 있게 된다.

 정말 plantUML이 서버를 요구하는지, 또는 Mermaid가 클라이언트 수준에서 처리되는지 궁금하다면 각 도구의 공식 라이브 에디터에 들어간 후 인터넷을 끊고 코드를 갱신(또는 submit)해보자. plantUML의 경우 페이지 연결없음을 표시하지만 Mermaid는 문제 없이 화면을 갱신하는 모습을 보일 것이다.


사용법

https://mermaid.js.org/config/usage.html

설치

 CDN에서 다운로드 하도록 구성할 수 있다.

// html에 삽입한다.
<script src="
https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js
"></script>

npm 또는 yarn으로 프로젝트에 포함할 수도 있다.

npm install mermaid
yarn add mermaid

렌더링

<pre class="mermaid" data-tree_name="tree view" id="tree">
    graph TD;0( default )
</pre>
  
 mermaid.initialize({ startOnLoad: true });

 mermaid.initialize( ) 함수를 이용하면 pre 태그 중 class='mermaid'인 모든 엘리먼트에 대해 내부 텍스트를 파싱하여 그래프를 생성한다. 처리된 엘리먼트는 텍스트를 그래프(svg)로 치환하며, data-processed = 'true'로 설정된다.

기존 코드
텍스트가 svg로 치환된 모습. data-processed 속성이 설정되었다.
생성된 노드

그래프를 다시 렌더링해야 하는 경우에는 두가지 방법을 고려할 수 있다.

  1. data-processed 속성을 제거한 후 mermaid.initialize( ) 을 실행한다.
  2. mermaid.render("id", "text", insertSvg)을 실행하여 특정 pre 엘리먼트만 다시 렌더링한다.
const target = document.querySelector('#tree');
target.textContent = treeStr;
target.removeAttribute('data-processed');
mermaid.init({startOnLoad: true});

mermaid는 data-processed 속성을 기준으로 해당 pre 태그가 처리되었는지 여부를 판단한다. 따라서 해당 속성을 없애고 다시 init을 수행하는 경우 UML이 재설정된다.

  const insertSvg = (svgCode: string, 
      bindFunctions?: ((element: Element) => void) | undefined) => {
    const element = document.querySelector("#tree");
    element!.innerHTML = svgCode;
};

const target = document.querySelector('#tree');
const treeStr = drawTree(tree);
mermaid.render("dtree", treeStr, insertSvg, target);

render 함수는 총 4개의 인자를 받는다. 각각 다음과 같다.

  • id: 렌더링 할 엘리먼트의 id
  • text: UML로 변환할 문자열
  • cb(svgCode, bindingFunctions): 렌더링할 엘리먼트를 찾는 방법 등을 정의한 콜백
  • svgContainingElement: svg를 가지고 있는 엘리먼트

 기존에 렌더링 된 그래프를 다시 렌더링 하는 경우 id 값은 크게 상관이 없다. 콜백에서 찾는 엘리먼트가 존재하지 않는 경우 id = `d${id}`인 div 엘리먼트를 생성할 때 사용되며, 페이지 내에서 정상적으로 대상 엘리먼트를 발견한 경우에는 의미 없는 값이다. 위 insertSvg 콜백이 제대로 동작하지 않을 때만 의미가 있다.

 cb 함수는 대상 엘리먼트의 html을 svgCode로 변경할 때 사용된다. 콜백의 존재 자체가 중요한 것인지는 몰라도 insertSvg 없이는 제대로 동작하지 않았으며, 값을 설정하는 코드를 외부로 꺼내는 경우 렌더링이 제대로 수행되지 않는다. 공식 문서를 참고하면 render 함수 자체는 svg 코드를 반환하는데, 이 코드를 특정 엘리먼트에 삽입하기 위한 목적으로 insertSvg를 사용한 것으로 보인다.

https://mermaid.js.org/config/usage.html#api-usage

 

Usage | Mermaid

 

mermaid.js.org

render 함수에 대한 사용법은 위 링크를 참고하자.


Mermaid 라이브러리 사용 예

공식문서에 따르면 Github, GitLab, Notion 등에서 네이티브 수준으로 지원되고 있다고 한다.

나도 해당 라이브러리를 기반으로 중위표기법 형태의 사칙연산식을 expression tree 형태로 보여주는 간단한 페이지를 만들어봤다. 사실 mermaid 라이브러리를 탐색하게 된 계기도 윤성우의 자료구조 스택 / 트리 파트에 나오는 표기법 변환 결과를 나타내고, 수식 트리(expression tree)를 웹 상에서 시각화해보기 위해서였다. 이 글을 보고 있다면 재미로 한번 사용해봐주시길 바란다.

https://blaxsior.github.io/simple-web-calculator/

 

웹 기반 계산기

OperatorTok: (, ), +, -, *, /, %

blaxsior.github.io