옵시디언 플러그인 Boardline

화이트보드가 필요해

개발하다 보면 글보다 그림으로 정리하는 게 빠를 때가 있다.
서버 구조를 잡거나, 기능 흐름을 정리하거나, 회의 전에 생각을 펼쳐 놓을 때 플로차트나 마인드맵을 자주 그린다.

나는 이럴 때 웹 화이트보드 서비스를 즐겨 썼다.
도형을 놓으면 알아서 정렬선에 붙고, 연결선은 도형을 피해 깔끔하게 꺾이고, 마인드맵은 키보드만으로 가지를 늘릴 수 있었다.
손에 익은 도구였다.

하지만 블로그 개설 이야기에서도 말했듯이, 내 데이터가 언젠가 서비스를 종료할 수도 있는 플랫폼에 묶여 있는 게 마음이 편하지 않았다.
지식 관리는 이미 옵시디언으로 옮겼는데, 그림만 남의 서버에 남아 있는 셈이었다.

옵시디언에도 기본적으로 캔버스 기능이 있긴 하지만 카드를 연결하는 방식이 맘에 들지 않았다.

그래서 화이트보드 플러그인을 직접 만들어 보기로 했다.
그 결과물이 이번 글의 주제인 옵시디언 플러그인 Boardline이다.

GitHub - kjs104901/obsidian-boardline

9_boardline

이번에도 바이브 코딩

블로그를 만들 때는 클로드가 짜 준 코드를 내가 검토하고 붙여 넣는 방식이었다면, 이번에는 Claude Code가 저장소 안에서 직접 코드를 고치고, 테스트하고, 커밋까지 했다.
나는 기능을 정하고, 만들어진 걸 써 보면서 이상한 동작을 알려 주는 역할에 가까웠다.

덕분에 아이디어부터 커뮤니티 플러그인 등록까지 이틀 정도밖에 걸리지 않았다.

그렇다고 말만 하면 다 되는 건 아니었다.
기능은 금방 생기는데, 막상 써 보면 연결선이 엉뚱하게 꺾이거나 복제한 도형의 높이가 줄어드는 식으로 세세한 동작이 어긋났다.
화이트보드는 이런 손맛이 중요해서, 시간 대부분은 써 보고 지적하고 고치는 반복에 들어갔다.

처음에는 데스크탑 앱으로 시작했다가 중간에 옵시디언 플러그인으로 방향을 바꿨다.
어차피 노트는 전부 옵시디언에 있으니, 보드도 같은 보관함에 있으면 노트를 보드에 올리고 보드를 노트에 넣을 수 있다.
보드 엔진은 처음부터 React나 옵시디언을 모르도록 나눠 두었기 때문에 옮기는 작업은 어렵지 않았다.

Boardline

보드 하나는 .board 파일 하나다.
첫 줄에 보드 정보, 그 아래에 객체를 한 줄에 하나씩 쓰는 JSON이라 사람이 읽을 수 있고, 객체 하나를 고치면 한 줄만 바뀌어서 diff나 동기화 충돌을 보기 편하다.

옵시디언 안에서 쓰는 도구인 만큼 옵시디언과 잘 어울리도록 만들었다.

  • 기본 크기와 그리드는 옵시디언 캔버스에 맞추고, 라이트·다크 테마와 강조색을 따라간다.
  • 노트를 보드에 끌어다 놓으면 마크다운을 그린 노트 카드가 되고, 노트에 ![[보드 이름.board]]를 쓰면 보드 그림이 들어간다.
  • 보드에 올린 파일을 옮기거나 이름을 바꾸면 보드 안의 링크도 따라간다.
  • Mermaid를 붙여 넣으면 보드 객체로 바뀌고, 보드를 노트나 캔버스로 내보낼 수도 있다.

9_boardline_flowcharts

9_boardline_notes

시행착오

tldraw 라이선스

처음에는 tldraw 같은 오픈소스 화이트보드 라이브러리를 쓰려고 했다.
그런데 tldraw는 라이선스 키 없이는 개발 환경에서만 쓸 수 있고, 키 없이 앱으로 빌드하면 편집기가 사라진다. 혼자 쓰는 앱도 예외가 없었다.
tldraw/LICENSE.md at main · tldraw/tldraw

서비스에 의존하던 게 라이브러리 키에 의존하는 걸로 바뀔 뿐이라, 결국 Canvas 2D로 엔진을 직접 만들었다.
내가 좋아했던 것도 노드가 붙고 정렬되는 세세한 감각이라, 라이브러리를 썼어도 어차피 그 부분은 갈아엎었을 것 같다.

React 19의 script 태그

옵시디언 플러그인은 원격 코드를 불러오면 안 된다.
그런데 React 19의 react-dom 안에는 리소스 미리 불러오기용 createElement("script") 코드가 들어 있다.
쓰지 않는 기능이지만 심사에서 걸릴 수 있어서 React를 18.3으로 내렸다.

createEl 경고

심사는 통과했지만 document.createElement 대신 옵시디언의 createEl을 쓰라는 경고를 받았다.
문제의 코드는 옵시디언 없이도 테스트가 돌아야 하는 보드 엔진에 있었다.

그래서 엔진은 @boardline/dom 모듈만 부르게 하고, 빌드할 때 이 모듈을 바꿔 끼웠다.
플러그인 빌드에서는 createEl을, 테스트에서는 일반 createElement를 쓴다.

// esbuild.config.mjs (플러그인)
alias: { '@boardline/dom': here('src/dom.ts') }      // createEl
// vitest.config.ts (테스트)
alias: { '@boardline/dom': here('scripts/dom.ts') }  // document.createElement

사라진 플러그인

등록 다음 날 커뮤니티 플러그인 검색에서 Boardline이 갑자기 사라졌다.
1.0.3 릴리스를 지웠다가 다시 만들면서 파일을 첨부하지 않아 설치할 수 없는 상태가 되었고, 목록을 갱신하는 봇이 그사이 뺀 것으로 보인다.
1.0.4를 올리자 다시 돌아왔다. 한 번 올린 릴리스는 건드리지 않는 게 좋을 것 같아보인다.

큰 보드 성능

객체 1만 개짜리 보드를 만들어 보니 화면을 한 번 그리는 데 350ms 가까이 걸렸다.
화면에 보이는 것만 그리고, 너무 작은 글자는 생략하고, 연결선 경로 같은 계산 결과를 캐시해서 2ms 정도로 줄였다.

마치며

블로그를 만들 때만 해도 AI는 코드를 대신 짜 주는 도구였는데, 이번에는 저장소를 통째로 맡기고 나는 써 보면서 지적하는 쪽에 가까웠다.
그래도 무엇을 만들지, 어떤 동작이 이상한지 판단하는 건 여전히 내 몫이었다.

지금은 1.0.6까지 나왔고, 옵시디언 커뮤니티 플러그인에서 Boardline을 검색하면 설치할 수 있다.
써 보고 불편한 점이 있다면 GitHub 이슈로 알려 주면 좋겠다.