skip to content
usubeni fantasy logo Usubeni Fantasy

웹페이지에 전문적인 마인드맵 기능을 쉽게 통합하기 - Mind Elixir

/ 20 min read

This Post is Available In: CN EN ES JA KO RU

정보 폭발의 시대에 우리는 매일 대량의 복잡한 정보와 아이디어를 처리해야 합니다. 전통적인 선형 노트는 사고의 도약성과 연관성을 표현하기 어려운 반면, 마인드맵은 반자동 노트 도구로서 우리가 사고를 더 잘 정리하고, 지식 구조를 체계화하며, 창의적 영감을 자극하는 데 도움을 주면서도 화이트보드나 그림 소프트웨어보다 더 편리한 조작을 제공합니다.

지식 관리 플랫폼, 온라인 교육 웹사이트, 협업 도구 구축 등 어떤 경우든 마인드맵 기능을 통합하면 사용자 경험을 크게 향상시키고, 복잡한 정보의 표시와 상호작용을 더욱 직관적이고 효율적으로 만들 수 있습니다.

만약 자신의 웹 프로젝트에 마인드맵 기능을 통합하고 싶다면, Mind Elixir를 활용하여 단 몇 줄의 코드만으로 웹사이트에 전문급 마인드맵 기능을 추가할 수 있습니다. 예를 들어 개인 블로그에서 마인드맵을 사용해 글의 지식 구조를 보여주거나 인터랙티브한 학습 노트를 만들 수 있으며, 이러한 기능은 콘텐츠를 더욱 생동감 있고 흥미롭게 만들 뿐만 아니라 블로그의 전문성을 크게 향상시킬 수 있습니다.

Mind Elixir 内核

오픈소스 JavaScript 마인드맵 코어 Mind Elixir는 다음과 같은 핵심 특징을 가지고 있습니다:

  • 부드러운 사용자 경험 매끄러운 상호작용과 자연스러운 피드백, 모바일 지원.
  • 경량이면서 고성능 작은 용량으로 빠른 로딩과 렌더링을 제공하며, 복잡한 그래픽에서도 높은 프레임률을 유지.
  • 프레임워크 독립적 React, Vue, Svelte는 물론 네이티브 프로젝트에서도 쉽게 통합하거나 독립적으로 실행 가능.
  • 플러그인 아키텍처 유연한 플러그인 시스템으로 공식 확장이나 커스텀 플러그인을 지원하며, 기능 모듈을 자유롭게 조합 가능.
  • PNG / HTML 내보내기 지원 마인드맵을 이미지나 HTML 페이지로 내보내어 공유와 임베딩이 편리.
  • 노드 요약 / 노드 연결 노드 요약, 연결선, 태그 등 다양한 노드 스타일을 지원하여 다른 요구사항 충족.
  • 실행취소 / 다시실행 지원 완전한 작업 히스토리 스택으로 모든 수정사항을 빠르게 되돌리거나 다시 실행 가능.
  • 효율적인 단축키 지원 풍부한 키보드 단축키로 전문 사용자의 작업 효율성 향상.
  • CSS 변수 스타일 커스터마이징 CSS 변수를 통해 노드 스타일과 전체 테마를 쉽게 제어하여 고도로 커스터마이징된 아름다운 레이아웃 구현.

이제 프로젝트에 Mind Elixir를 빠르게 통합하는 방법을 간단히 소개해보겠습니다! 🤗

초간단 연동

의존성 설치:

Terminal window
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);
});

때로는 이벤트 리스닝만으로는 충분하지 않습니다. 데이터베이스에 데이터를 삽입한 후에 화면에 표시되도록 하려면 작업 인터셉트를 사용해야 합니다. optionsbefore 옵션을 추가하는데, 이는 객체이며 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는 마인드맵을 재디자인할 수 있는 자유를 제공합니다.

먼저 themecssVar를 통해 전체 마인드맵의 스타일을 간단히 조정할 수 있습니다. 코어에는 밝은 테마와 어두운 테마인 MindElixir.DARK_THEMEMindElixir.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을 포함할 수 있으며, 이는 optionstheme을 덮어씁니다. 이는 각 마인드맵이 독립적인 테마를 가질 수 있도록 보장하기 위함입니다. 고정된 테마가 필요하다면 초기화 시 datathemeundefined로 설정하는 것을 잊지 마세요.

P.S. 초기화 후 테마를 다시 수정하고 싶다면 changeTheme 메서드를 사용할 수 있습니다.

cssVar에서 자주 사용되는 main-gap은 주 노드 간의 간격을 조정할 수 있습니다:

Mind Elixir main-gap

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

Mind Elixir node-gap

TIP

Codepen에서 체험해보기: https://codepen.io/ssshooter/pen/azOgVKX

CSS 변수로 조정할 수 없는 다른 매개변수들도 CSS 오버라이드를 통해 직접 미세 조정할 수 있습니다.

더 깊이 들어가면 generateMainBranchgenerateSubBranch를 통해 연결선의 스타일을 조정할 수 있습니다. (예제는 아래 codepen 링크 참조)

적절한 generateMainBranchgenerateSubBranch를 작성한 후, 확장/축소 버튼의 위치가 맞지 않는다면 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" });
};
Mind Elixir map-padding

여전히 이미지가 일부만 캡처되는 경우, 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를 더욱 완벽하게 만들어 갑시다!

评论组件加载中……