웹페이지에 전문적인 마인드맵 기능을 쉽게 통합하기 - Mind Elixir
/ 20 min read
정보 폭발의 시대에 우리는 매일 대량의 복잡한 정보와 아이디어를 처리해야 합니다. 전통적인 선형 노트는 사고의 도약성과 연관성을 표현하기 어려운 반면, 마인드맵은 반자동 노트 도구로서 우리가 사고를 더 잘 정리하고, 지식 구조를 체계화하며, 창의적 영감을 자극하는 데 도움을 주면서도 화이트보드나 그림 소프트웨어보다 더 편리한 조작을 제공합니다.
지식 관리 플랫폼, 온라인 교육 웹사이트, 협업 도구 구축 등 어떤 경우든 마인드맵 기능을 통합하면 사용자 경험을 크게 향상시키고, 복잡한 정보의 표시와 상호작용을 더욱 직관적이고 효율적으로 만들 수 있습니다.
만약 자신의 웹 프로젝트에 마인드맵 기능을 통합하고 싶다면, Mind Elixir를 활용하여 단 몇 줄의 코드만으로 웹사이트에 전문급 마인드맵 기능을 추가할 수 있습니다. 예를 들어 개인 블로그에서 마인드맵을 사용해 글의 지식 구조를 보여주거나 인터랙티브한 학습 노트를 만들 수 있으며, 이러한 기능은 콘텐츠를 더욱 생동감 있고 흥미롭게 만들 뿐만 아니라 블로그의 전문성을 크게 향상시킬 수 있습니다.

오픈소스 JavaScript 마인드맵 코어 Mind Elixir는 다음과 같은 핵심 특징을 가지고 있습니다:
- 부드러운 사용자 경험 매끄러운 상호작용과 자연스러운 피드백, 모바일 지원.
- 경량이면서 고성능 작은 용량으로 빠른 로딩과 렌더링을 제공하며, 복잡한 그래픽에서도 높은 프레임률을 유지.
- 프레임워크 독립적 React, Vue, Svelte는 물론 네이티브 프로젝트에서도 쉽게 통합하거나 독립적으로 실행 가능.
- 플러그인 아키텍처 유연한 플러그인 시스템으로 공식 확장이나 커스텀 플러그인을 지원하며, 기능 모듈을 자유롭게 조합 가능.
- PNG / HTML 내보내기 지원 마인드맵을 이미지나 HTML 페이지로 내보내어 공유와 임베딩이 편리.
- 노드 요약 / 노드 연결 노드 요약, 연결선, 태그 등 다양한 노드 스타일을 지원하여 다른 요구사항 충족.
- 실행취소 / 다시실행 지원 완전한 작업 히스토리 스택으로 모든 수정사항을 빠르게 되돌리거나 다시 실행 가능.
- 효율적인 단축키 지원 풍부한 키보드 단축키로 전문 사용자의 작업 효율성 향상.
- CSS 변수 스타일 커스터마이징 CSS 변수를 통해 노드 스타일과 전체 테마를 쉽게 제어하여 고도로 커스터마이징된 아름다운 레이아웃 구현.
이제 프로젝트에 Mind Elixir를 빠르게 통합하는 방법을 간단히 소개해보겠습니다! 🤗
초간단 연동
의존성 설치:
npm i mind-elixir -S
Mind Elixir 가져오기:
import MindElixir from "mind-elixir";import "mind-elixir/style"; // 5.0 이후 버전에서는 스타일을 직접 가져와야 함
물론 script
로 직접 가져올 수도 있습니다:
<script type="module" src="https://cdn.jsdelivr.net/npm/mind-elixir/dist/MindElixir.js"></script>
초기화하기 전에 마운트할 대상 요소의 스타일을 조정해야 합니다. 정확히 말하면 명확한 너비와 높이를 지정해야 하며, 특히 높이에 주의해야 합니다. CSS에 시달린 프론트엔드 개발자라면 누구나 알겠지만, 높이 100%는 다루기 까다로운 설정입니다. 아래에서는 높이 500px인 div를 예로 들겠습니다.
<div id="map"></div><style> #map { height: 500px; width: 100%; }</style>
이제 정식 초기화를 진행합니다. 다른 초기화 옵션을 고려하지 않는다면 마운트 요소만 전달하면 됩니다!
import MindElixir from "mind-elixir";import "mind-elixir/style";
let options = { el: "#map", // or HTMLDivElement};
let mei = new MindElixir(options);const data = MindElixir.new("new topic");mei.init(data);
여기까지 하면 가장 기본적인 연동이 완료됩니다!
TIP
Codepen에서 체험해보기: https://codepen.io/ssshooter/pen/vEOqWjE
이벤트 리스닝
프로젝트에서 마인드맵을 사용한 후 가장 일반적인 요구사항은 이벤트 리스닝입니다. 다양한 노드 작업을 모니터링하고 사용자 작업에 응답해야 합니다. 예를 들어 사용자가 새 노드를 생성했을 때 즉시 저장해야 합니다.
Mind Elixir는 bus
를 사용하여 이벤트를 디스패치합니다. 이 용어는 “버스”라는 개념에서 유래되었으며, 모든 이벤트는 이 “버스”에 의해 디스패치됩니다. 사용법은 브라우저의 addEventListener
와 유사하며, bus
에 접근할 수 있다면 어디서든 이벤트를 리스닝할 수 있습니다.
최신 버전 Mind Elixir 5.0까지 다음과 같은 이벤트가 있습니다:
type EventMap = { operation: (info: Operation) => void; selectNode: (nodeObj: NodeObj, e?: MouseEvent) => void; selectNewNode: (nodeObj: NodeObj) => void; selectNodes: (nodeObj: NodeObj[]) => void; unselectNodes: (nodeObj: NodeObj[]) => void; expandNode: (nodeObj: NodeObj) => void; linkDiv: () => void; scale: (scale: number) => void; move: (data: { dx: number; dy: number }) => void; updateArrowDelta: (arrow: Arrow) => void; showContextMenu: (e: MouseEvent) => void;};
이 중 대부분의 노드 작업 이벤트는 operation
이벤트로 분류됩니다. 말보다는 실행이죠! 프로젝트에 추가한 후 노드를 조작해보면 쉽게 이해할 수 있습니다:
mei.bus.addListener("operation", (operation) => { console.log(operation);});
때로는 이벤트 리스닝만으로는 충분하지 않습니다. 데이터베이스에 데이터를 삽입한 후에 화면에 표시되도록 하려면 작업 인터셉트를 사용해야 합니다. options
에 before
옵션을 추가하는데, 이는 객체이며 key는 인터셉트하려는 작업입니다. addChild
작업을 인터셉트하려면 다음과 같이 작성할 수 있습니다:
let mei = new MindElixir({ // ... before: { async addChild(el, obj) { try { await saveToDatabase(obj); return true; } catch (error) { console.error("Error adding child:", error); return false; } }, },});
이렇게 하면 saveToDatabase()
실행이 성공한 후에만 실제로 자식 노드가 추가되고, 실행이 실패하면 삽입 작업이 취소됩니다.
TIP
Codepen에서 체험해보기: https://codepen.io/ssshooter/pen/EajBbrM
마인드맵 다시 그리기
Mind Elixir 코어의 기본 동작을 사용하여 마인드맵을 업데이트하는 것 외에도, 노드 데이터를 직접 업데이트하여 마인드맵을 다시 그릴 수 있습니다.
완전한 노드 데이터 구조는 다음과 같습니다:
export interface NodeObj { topic: string; id: Uid; style?: { fontSize?: string; color?: string; background?: string; fontWeight?: string; }; children?: NodeObj[]; tags?: string[]; icons?: string[]; hyperLink?: string; expanded?: boolean; direction?: Left | Right; image?: { url: string; width: number; height: number; fit?: "fill" | "contain" | "cover"; }; branchColor?: string; dangerouslySetInnerHTML?: string; note?: string;}
노드 데이터를 수정하여 노드에 이미지, 태그, 하이퍼링크 등의 요소를 삽입할 수 있습니다. 예를 들어, Mind Elixir 인스턴스의 nodeData
를 읽어 현재 마인드맵 데이터를 가져온 다음 수정하고, 마지막으로 refresh
메서드를 호출하여 마인드맵을 다시 그릴 수 있습니다.
const data = mind.nodeData;console.log(data);data.topic = data.topic + "new Data";mind.refresh();
하지만 전체 마인드맵을 완전히 새로운 데이터로 업데이트하고 싶다면? 그것도 가능합니다! Mind Elixir 형식에 맞는 데이터를 refresh
메서드에 전달하면 즉시 전체 그래프를 업데이트할 수 있습니다.
import data from "https://esm.sh/mind-elixir/dist/example.js";mind.refresh(data);
TIP
Codepen에서 체험해보기: https://codepen.io/ssshooter/pen/vEOqpOX
재디자인의 자유
Mind Elixir는 마인드맵을 재디자인할 수 있는 자유를 제공합니다.
먼저 theme
과 cssVar
를 통해 전체 마인드맵의 스타일을 간단히 조정할 수 있습니다. 코어에는 밝은 테마와 어두운 테마인 MindElixir.DARK_THEME
과 MindElixir.THEME
이 내장되어 있으며, 커스텀 테마가 필요하다면 테마 형식에 따라 객체를 작성하여 Mind Elixir에 전달할 수 있습니다.
완전한 Mind Elixir 테마와 사용 방법은 다음과 같습니다:
const PROFESSIONAL_THEME = { name: "Professional", type: "light", palette: ["#2c2c2c", "#404040", "#555555", "#6a6a6a", "#7f7f7f", "#949494", "#a9a9a9"], cssVar: { "--node-gap-x": "32px", "--node-gap-y": "12px", "--main-gap-x": "68px", "--main-gap-y": "48px", "--root-radius": "8px", "--main-radius": "6px", "--root-color": "#ffffff", "--root-bgcolor": "#1a1a1a", "--root-border-color": "#333333", "--main-color": "#2c2c2c", "--main-bgcolor": "#ffffff", "--topic-padding": "4px", "--color": "#4a4a4a", "--bgcolor": "#fafafa", "--selected": "#666666", "--panel-color": "#2c2c2c", "--panel-bgcolor": "#ffffff", "--panel-border-color": "#e0e0e0", },};
let mind = new MindElixir({ el: "#map", theme: PROFESSIONAL_THEME,});
주의할 점은 data
자체도 theme
을 포함할 수 있으며, 이는 options
의 theme
을 덮어씁니다. 이는 각 마인드맵이 독립적인 테마를 가질 수 있도록 보장하기 위함입니다. 고정된 테마가 필요하다면 초기화 시 data
의 theme
을 undefined
로 설정하는 것을 잊지 마세요.
P.S. 초기화 후 테마를 다시 수정하고 싶다면 changeTheme
메서드를 사용할 수 있습니다.
cssVar
에서 자주 사용되는 main-gap은 주 노드 간의 간격을 조정할 수 있습니다:

node-gap은 노드 내부의 간격을 조정할 수 있습니다:

TIP
Codepen에서 체험해보기: https://codepen.io/ssshooter/pen/azOgVKX
CSS 변수로 조정할 수 없는 다른 매개변수들도 CSS 오버라이드를 통해 직접 미세 조정할 수 있습니다.
더 깊이 들어가면 generateMainBranch
와 generateSubBranch
를 통해 연결선의 스타일을 조정할 수 있습니다. (예제는 아래 codepen 링크 참조)
적절한 generateMainBranch
와 generateSubBranch
를 작성한 후, 확장/축소 버튼의 위치가 맞지 않는다면 CSS를 통해 미세 조정할 수 있습니다. 기본 스타일은 다음과 같습니다:
// 주 노드(루트 노드의 다음 레벨 노드)의 확장/축소 버튼 스타일me-main > me-wrapper > me-parent > me-epd { top: 50%; transform: translateY(-50%);}// 기타 자식 노드의 확장/축소 버튼 스타일me-epd { top: 100%; transform: translateY(-50%);}// 왼쪽 확장/축소 버튼 전용 조정.lhs { & > me-wrapper > me-parent > me-epd { left: -10px; } me-epd { left: 5px; }}// 오른쪽 확장/축소 버튼 전용 조정.rhs { & > me-wrapper > me-parent > me-epd { right: -10px; } me-epd { right: 5px; }}
TIP
Codepen에서 체험해보기: https://codepen.io/ssshooter/pen/WNmZMmq
노드 스타일
전체적인 스타일 외에도 노드에 대한 커스터마이징 요구사항이 있다면, 노드 자체에 style
을 설정할 수 있습니다:
//...{ fontSize?: string color?: string background?: string fontWeight?: string}// ...
노드에 대해 매우 강한 커스터마이징 요구사항이 있어서 이 정도의 style
설정으로는 전혀 부족하다고 느낀다면, 그것도 문제없습니다. 여전히 만족시킬 수 있습니다!
dangerouslySetInnerHTML
을 통해 더욱 다양하게 활용할 수 있습니다. 예를 들어:
const data = { nodeData: { id: "me-root", topic: "Mind Elixir", tags: ["Mind Map Core"], children: [ { topic: "Customized Div", id: "c00a2264f4532615", children: [ { topic: "", id: "c00a2264f4532614", dangerouslySetInnerHTML: '<div><style>.title{font-size:50px}</style><div class="title">Title</div><div style="color: red; font-size: 20px;">Hello world</div></div>', }, ], }, ], },};
TIP
Codepen에서 체험해보기: https://codepen.io/ssshooter/pen/MYwMrjZ
이미지 내보내기
modern-screenshot 덕분에 svg를 교묘하게 활용하여 div를 그대로 이미지로 변환할 수 있습니다. 기본적으로 너무 극단적인 dangerouslySetInnerHTML
(예: 비디오)을 사용하지 않는다면 정상적으로 이미지를 내보낼 수 있습니다. @ssshooter/modern-screenshot
은 추가로 padding
옵션을 제공하여 스크린샷의 여백을 조정할 수 있습니다.
import { domToPng } from "@ssshooter/modern-screenshot";
const download = async () => { const dataUrl = await domToPng(mind.nodes, { onCloneNode: (node) => { const n = node as HTMLDivElement; n.style.position = ""; n.style.top = ""; n.style.left = ""; n.style.bottom = ""; n.style.right = ""; }, padding: 300, quality: 1, }); const link = document.createElement("a"); link.download = "screenshot.png"; link.href = dataUrl; link.click();};
modern-screenshot을 직접 사용하거나 최근에 출시된 snapdom을 사용해도 됩니다. 스크린샷이 불완전한 경우(주로 요약과 연결선이 불완전한 경우)에는 cssVar
의 --map-padding
을 조정할 수 있습니다.
import { snapdom } from "@zumer/snapdom";
const dl2 = async () => { const result = await snapdom(mind.nodes); await result.download({ format: "jpg", filename: "my-capture" });};

여전히 이미지가 일부만 캡처되는 경우, scale이 1이 아닌 것이 원인일 수 있습니다. 스크린샷 전에 scale을 1로 설정하고 완료 후 원래 크기로 복원해 볼 수 있습니다.
TIP
Codepen에서 체험해보기: https://codepen.io/ssshooter/pen/NPqZXXB
서버 사이드 렌더링 프레임워크에서 사용
Next.js 등의 서버 사이드 렌더링 프레임워크에서 Mind Elixir를 사용할 때 window is not defined
같은 문제가 자주 발생합니다. 이는 Mind Elixir가 다양한 DOM 조작에 크게 의존하기 때문이며, SSR 환경에서는 정상적으로 작동할 수 없습니다.
이 문제를 해결하기 위해 useEffect
를 사용하여 클라이언트 사이드 렌더링 시 Mind Elixir를 로드할 수 있습니다. 다음은 간단한 예제입니다:
"use client";import { useEffect } from "react";import { mindMapExample } from "./mapExample";
export const MindMap = ({ className }: { className: string }) => { useEffect(() => { import("mind-elixir").then((MindElixir) => { const theme = MindElixir.default.DARK_THEME; theme.cssVar["--bgcolor"] = "rgba(0,0,0,0)"; const mei = new MindElixir.default({ el: "#map", direction: 2, theme, }); mei.init({ nodeData: mindMapExample, }); mei.toCenter(); window.addEventListener("resize", () => { mei.toCenter(); }); }); }, []); return ( <div id="wrapper" className={className}> <div id="map" className="pointer-events-none h-[50vh] w-screen" onScroll={(e) => e.preventDefault()} ></div> </div> );};
기타 Mind Elixir 옵션
Mind Elixir에는 설정할 수 있는 다른 많은 옵션들이 있습니다:
interface Options { // ... direction?: number; // 노드 배치 방향, 0 왼쪽 1 오른쪽 2 양쪽 locale?: Locale; // 언어 선택 contextMenu?: boolean | ContextMenuOption; // 우클릭 메뉴 활성화 여부, 옵션 추가 가능 toolBar?: boolean; // 내장 툴바 활성화 여부 keypress?: boolean | KeypressOptions; // 단축키 활성화 여부, 커스텀 단축키 추가 가능 mouseSelectionButton?: 0 | 2; // 드래그 버튼, 기본값은 우클릭 드래그 before?: Before; // 위에서 언급한 작업 인터셉트 newTopicName?: string; // 새 노드의 기본값 allowUndo?: boolean; // 실행취소/다시실행 활성화 여부 overflowHidden?: boolean; // 화면 이동 가능 여부, 마인드맵 카드 표시 시 사용 가능 alignment?: Alignment; // nodes로 설정 시 화면 중심이 마인드맵 중심, root로 설정 시 화면 중심이 루트 노드 중심, 기본값 root scaleSensitivity?: number; // 스크롤휠과 메뉴의 확대/축소 민감도 draggable?: boolean; // 노드 드래그 가능 여부 editable?: boolean; // 편집 가능 여부 // ...}
마무리
이 글의 소개를 통해 Mind Elixir의 기본 사용법부터 고급 커스터마이징까지 다양한 기법을 익히셨을 것입니다. 사용 과정에서 문제가 발생하거나 더 좋은 아이디어와 제안이 있으시면 댓글로 공유해 주시고, GitHub에서 토론에 참여하거나 PR을 제출해 주세요! 함께 Mind Elixir를 더욱 완벽하게 만들어 갑시다!