프론트엔드 개발자의 옵시디언 플러그인 개발기!
왜 만들었나
하루를 계획하고 나서 막상 저녁이 되면 "오늘 뭘 했지?"라는 생각이 드는 날이 많았다
특히 회사에 8시간이나 있는데 뭔가 한게 없는 것 같이 지나가는 시간이 너무 아까웠다.
그래서 어차피 옵시디언으로 일지를 쓰고 있으니까 그대로 활용하면 좋겠다는 생각을 했고,
마음에 드는 타임라인 플러그인이 없어서 직접 만들어보기로 했다!
그렇게 Time Manager를 만들었다.
Daily Note에 적어둔 태스크를 시각적인 타임라인으로 보여주고,
카테고리별로 완료 현황을 추적하는 Obsidian 플러그인이다.
어떻게 동작하나
사용법은 단순하다. Daily Note에 이런 식으로 태스크를 적으면 된다.
## Timeline
- [ ] 09:00 팀 스탠드업 @work 30m
- [x] 10:00 기능 개발 @work 2h
- [ ] 13:00 운동 @personal 1h
- [ ] 14:30 알고리즘 공부 @study 1h30m

또는 +task 버튼으로 태스크를 추가할 수 있다.

플러그인이 이 섹션을 파싱해서 오른쪽 사이드바에 타임라인으로 그려준다.
체크박스를 [x]로 바꾸면 완료로 집계된다.

카테고리는 @work, @study, @personal, @hobby 네 가지를 지원하고,
하단에 카테고리별 막대 그래프로 오늘 얼마나 시간을 쓰고 목표를 이루었는지 한눈에 볼 수 있다.

개발하면서 고민했던 것들
1. 마크다운 파싱
Obsidian은 파일을 직접 읽고 쓰는 방식이라 파싱을 직접 구현해야 했다.
한 줄을 파싱하면 이런 정보들이 나온다.
- [x] 10:00 기능 개발 @work 2h
│ │ │ │ └─ 소요 시간 (2h)
│ │ │ └─ 카테고리 (@work)
│ │ └─ 태스크 이름
│ └─ 시작 시간 (HH:MM)
└─ 완료 여부 ([x] or [ ])
실제 파서는 정규식 세 개가 순서대로 작동한다.
// 1단계: 체크박스 + 시간 + 나머지 텍스트 분리
const lineMatch = /^- \[( |x)\] (\d{2}:\d{2}) (.*)$/.exec(raw.trim());
// 2단계: @카테고리 태그 추출
const HASHTAG_CAT_RE = /@(work|personal|hobby|study)\b/i;
// 3단계: 끝에 붙은 소요 시간 추출 (단위 필수)
const TRAILING_TIME_RE = /\s+(?:(\d+)h(\d+)m|(\d+)h|(\d+)m)\s*$/;
1h30m, 1h, 30m 세 가지 포맷을 전부 처리하고, 각각 분으로 변환한다.
if (timeMatch[1] && timeMatch[2])
dur = parseInt(timeMatch[1], 10) * 60 + parseInt(timeMatch[2], 10); // 1h30m → 90분
else if (timeMatch[3])
dur = parseInt(timeMatch[3], 10) * 60; // 1h → 60분
else if (timeMatch[4])
dur = parseInt(timeMatch[4], 10); // 30m → 30분
## Timeline 헤딩이 나오면 파싱을 시작하고, 다른 ## 헤딩이 나오면 멈춘다. 노트 전체를 파싱하는 게 아니라 플래너 섹션만 읽는다.
for (const line of lines) {
if (/^## /.test(line)) {
inPlannerSection = labelPattern.test(line); // "Timeline" 섹션인지 확인
continue;
}
if (!inPlannerSection) continue;
// 여기서부터 태스크 파싱
}
처음엔 숫자만 입력하면 분으로 처리하는 방식이었는데,
단위 없이 숫자만 쓰면 태스크 이름의 일부인지 소요 시간인지 애매해지는 경우가 생겼다.
예를 들어 헤드 퍼스트 디자인 패턴 챕터 3이 '헤드 퍼스트 디자인 패턴 챕터 3'을 공부하겠다는 것인지
'헤드 퍼스트 디자인 패턴 챕터'를 3분 공부하겠다는 것인지 확실하게 구분을 할 수 없었다.
그래서 단위를 필수로 바꿨다.
2. 타임라인 렌더링
CSS pixel 기준으로 분당 2px를 배치하는 방식으로 구현했다.
const PX_PER_MIN = 2;
const DAY_START_HOUR = 7; // 오전 7시부터 시작
// 태스크 블록의 위치와 높이 계산
const top = (startMin - dayStartMin) * PX_PER_MIN;
const height = Math.max(durMin * PX_PER_MIN, 28); // 최소 28px
태스크 블록의 높이는 소요 시간 기준이고,
소요 시간이 명시되지 않은 경우 다음 태스크 시작 시간까지의 간격으로 자동 계산된다.
if (i + 1 < this.tasks.length) {
const nextMin = parseMinutes(this.tasks[i + 1].startTime);
const gap = nextMin - startMin;
if (!task.dur && gap > 0) durMin = gap; // 소요 시간 미입력 시 자동 계산
}
현재 시각을 나타내는 빨간 선은 1분마다 위치를 갱신한다.
// onOpen()에서 인터벌 등록
this.nowLineInterval = window.setInterval(() => this.updateNowLine(), 60 * 1000);
// onClose()에서 반드시 정리
if (this.nowLineInterval !== null) window.clearInterval(this.nowLineInterval);
두 개의 타임라인이 겹치는 경우는 아직 대응하지 않았다.
왜냐하면 나는 멀티태스킹을 좋아하는데 그렇게 욕심을 내는 것이
업무의 집중도나 능률에는 도움이 되지 않아 멀티태스킹을 최소화하는 것을 지향하기 때문이다!
3. 최소 기능 구현과 필드명 결정
처음엔 예상 시간과 실제 시간을 모두 작성할 수 있고, 타이머로 실제 시간을 계산하는 기능까지 있었다.
- [ ] 10:00 기능 개발 [est:: 120] [act:: 95] ← 처음 설계
- [ ] 10:00 기능 개발 2h ← 최종
그런데 만들다보니까 회사에서 인터럽트가 많이 걸린다는 것을 깨닫고
실제 시간을 타이머로 표시하는 것이 과한 기능이라는 생각이 들어 삭제했다.
그래서 처음에는 [est:: 60], [act:: 60]으로 예상 시간과 실제 시간을 모두 표기할 수 있도록 설계했는데
act가 빠지면서 est가 "estimate(추정)"의 줄임말이라 좀 어색해졌다.
실제로 쓰는 건 "소요 시간"이니까 dur(duration)로 바꾸는 게 더 맞다고 판단해서 전부 수정했다.
코드 내부 변수명까지 estMinutes → durMinutes로 통일했다.
커뮤니티 플러그인 등록 과정
만들고 나서 Obsidian 커뮤니티 플러그인으로 등록해보기로 했다.
절차가 생각보다 까다로웠다.
필수 조건
- manifest.json에 정확한 메타데이터 (id, description, authorUrl 등)
- GitHub Release에 main.js, manifest.json, styles.css 첨부
- LICENSE 파일 포함
- 영어로 된 README
manifest.json은 이렇게 생겼다.
{
"id": "time-manager",
"name": "Time Manager",
"version": "0.1.0",
"minAppVersion": "1.4.0",
"description": "Organize Daily Note tasks into a visual timeline and track time by category.",
"author": "Najeong Kim",
"authorUrl": "https://github.com/Najeong-Kim",
"isDesktopOnly": false
}
등록 방법
https://github.com/obsidianmd/obsidian-releases 저장소를 포크해서
community-plugins.json에 항목을 추가하는 PR을 올리면 된다.
PR 제목은 반드시 Add plugin: 플러그인명 형식이어야 하고, 공식 체크리스트 템플릿을 그대로 써야 한다.

처음에 PR 설명을 내 마음대로 작성했다가 봇한테 바로 에러를 받았다 ㅠ.ㅠ
공식 템플릿을 제대로 안 따르고 LICENSE 파일도 없다고..!
둘 다 수정하고 PR을 닫았다 다시 열어서 봇을 재실행했다
현재 리뷰 대기 중이고 보통 2~8주 걸린다고 한다~
앞으로
일단 리뷰 통과가 목표이고 통과되면 추가하고 싶은 기능들이 있다
- 타임블록 드래그로 시간 조정
- 주간 통계 뷰
- 할일 목록 추가
버그도 좀 있어서 수정이 필요하다.. ㅎㅎ
AI로 세상이 발전하면서 아이디어가 살아남는 시대가 온 것 같다.
나는 구현도 즐기지만 아이디어로 이것저것 만들어보는 것을 좋아하는데
지금이 나에게는 너무 좋은 시대라고 생각을 한다!!
이번 프로젝트도 내가 전혀 해보지 않아서 지레 겁먹고 시도해보지않았던 플러그인 만들기에 도전해보는데
생각보다 어렵지 않았고 앞으로 하나하나 새로운 시도를 해보려고 한다!
프론트엔드 엔지니어라는 명함도 언젠가는 떼야 할 수도 있을 것 같다
GitHub: https://github.com/Najeong-Kim/time-manager-plugin
GitHub - Najeong-Kim/time-manager-plugin: Daily Note 태스크를 시각적 타임라인으로 정리하는 Obsidian 플러그
Daily Note 태스크를 시각적 타임라인으로 정리하는 Obsidian 플러그인. Contribute to Najeong-Kim/time-manager-plugin development by creating an account on GitHub.
github.com
'코딩 > 공부' 카테고리의 다른 글
| [코드트리] 코딩테스트 문제 풀면서 깃허브 잔디 심기 (0) | 2026.05.22 |
|---|---|
| [코드트리] DP(동적계획법) 알고리즘 약점 학습 후기 (0) | 2026.05.17 |
| [코드트리] 코딩테스트 준비, gap-check로 내 약점 찾기: 갭체크 후기 (0) | 2026.05.10 |
| 10 Years of Let’Swift 행사 참가 후기 (0) | 2026.04.02 |
| Frontend Fundamentals 모의고사 2회차 후기 (0) | 2026.03.29 |