한 줄 요약
StepanKropachev/obsidian-pm는 Obsidian vault 안의 Markdown 파일을 데이터베이스로 삼아 프로젝트·작업을 관리하는 Project Manager 커뮤니티 플러그인이다.[1] Table, Gantt, Kanban 뷰를 같은 작업 데이터 위에 제공하며 외부 서비스·클라우드 계정·별도 동기화 구독 없이 동작하도록 설계됐다.[3][4]
핵심은 “Obsidian 안에 별도 SaaS를 임베드”하는 것이 아니라, 사람이 읽고 Git으로 버전 관리할 수 있는 .md와 YAML frontmatter를 정본으로 유지하면서 프로젝트 관리 UI를 얹는 것이다.[3][6][17]
조사 기준과 저장소 현황
- 저장소:
StepanKropachev/obsidian-pm.[1] - 고정 조사 커밋:
b42b5bfe0e8329fcbd18a1118ed86476f7711baa—quiet the task editor section labels and align the subtask rows (#193).[2] - 커밋 시각:
2026-08-03T02:33:08+02:00; 커밋 작성자는 Jason Hyde다.[2] - manifest 버전:
1.8.0; 플러그인 IDproject-manager; 최소 Obsidian 버전1.13.0;isDesktopOnly: false라 데스크톱과 모바일을 모두 대상으로 한다.[4] - GitHub 조회 시점의 저장소 신호: 별 560개, fork 60개, open issue 70개, TypeScript, MIT, 비아카이브 상태.[1]
- 공식 릴리스 최신 태그는
1.8.0이며 2026-07-03에 게시됐다.[3] 고정 HEAD는 그 릴리스 이후의main커밋이다.[2][3] - 저장소 홈페이지는
https://dotpm.pm이며 README는 별도의 제품 웹사이트와 Obsidian Community Plugin 설치 링크도 제공한다.[1][3]
조사 시각은 2026-08-10 14:55 KST다. raw 캡처는 이동하는 main이 아니라 위 커밋의 GitHub snapshot을 기준으로 만들었고, 선택 원문 본문 SHA-256은 raw 문서 frontmatter에 기록했다.[2]
제품 표면
세 가지 뷰
| 뷰 | 역할 | 코드·문서에서 확인한 동작 |
|---|---|---|
| Table | 작업 그리드 | 정렬·필터·inline edit·saved view·다중 선택 bulk action을 제공한다.[3][23] |
| Gantt | 일정·의존성 타임라인 | 작업 막대 drag/resize, dependency arrow, milestone diamond, today line, day/week/month/quarter zoom을 제공한다.[3][24] |
| Kanban | 상태별 보드 | 상태 컬럼에 카드를 배치하고 drag-and-drop으로 작업 상태를 바꾼다.[3][25] |
모든 뷰는 동일한 Project.tasks 트리와 TaskIndex를 읽는다. 따라서 뷰 전환은 데이터를 복사하거나 별도 저장소로 옮기는 작업이 아니라 같은 vault 파일을 다른 투영으로 보는 방식이다.[6][7][23]
작업 관리
작업은 일반 task, milestone, subtask 타입을 가지며, 임의 깊이의 하위 작업·blocking dependency·진행률·완료일·보관 상태를 지원한다.[6][18]
기본 속성은 제목, 본문 설명, status, priority, start/due 날짜, progress 0–100, assignees, tags, subtasks, dependencies다. 선택 속성으로 recurrence, time estimate, 날짜별 time log, 프로젝트별 custom fields를 저장한다.[6][17]
사용자는 status와 priority의 라벨·색·아이콘을 전역 설정에서 편집할 수 있고, 프로젝트별로 별도 palette를 정의할 수 있다. 프로젝트 override에 없는 값이라도 실제 작업이 사용 중이면 fallback 항목으로 추가해 보드나 picker에서 사라지지 않게 한다.[3][6][19]
프로젝트별 설정은 기본 view, auto-schedule, 완료가 빠른 작업의 dependent pull-forward, Kanban subtasks 표시, 설명 preview 표시까지 override할 수 있다.[3][6][19]
시간·알림·bulk action
시간 추적은 예상 시간과 실제 로그를 분리하고, 각 로그에 날짜·시간·메모를 보존한다. 작업 UI는 logged/estimated 진행 상태를 표시하며 초과 기록도 표현한다.[3][6]
due date reminder는 전역 on/off와 며칠 전 알림을 설정할 수 있다. Table의 bulk action은 status·priority·assignee·tag·due date·progress 변경, parent 지정·해제, archive/unarchive, 삭제를 다룬다.[3][5][23]
파일 우선 저장 모델
디렉터리 구조
Projects/
├── Project Alpha.md
└── Project Alpha_tasks/
├── design-login.md
├── implement-api.md
└── Archive/
└── shipped-v1.md프로젝트 파일에는 pm-project: true, 프로젝트 ID·제목·설명·색·아이콘·taskIds·custom fields·team members·saved views·project config가 들어간다. 개별 작업은 pm-task: true와 project ID, parent ID, task ID, 작업 속성, subtaskIds, dependencies를 frontmatter에 기록한다.[6][16][17]
프로젝트 파일은 작업 목록의 top-level 순서와 링크를 보존하고, 작업 파일은 본문 설명과 parent/project wikilink 및 ## Subtasks 자동 생성 목록을 보존한다. 저장 시 자동 생성 영역을 먼저 제거하므로 반복 저장으로 링크나 Subtasks 목록이 중복되지 않는다.[17]
각 작업은 하나의 Markdown 파일이므로 Obsidian 검색·Git diff·외부 편집기·Obsidian Sync/iCloud/Dropbox/Syncthing과 함께 사용할 수 있다.[3] 다만 README도 실시간 다중 사용자 편집은 제공하지 않으며 동일 파일 동시 수정은 일반 Markdown sync conflict처럼 처리된다고 명시한다.[3]
읽기·쓰기·동기화
ProjectStore는 한 경로에 대해 하나의 live Project 인스턴스를 유지하고, 동시에 들어온 load 요청을 하나의 promise로 합친다. vault create/modify/delete/rename 이벤트를 등록하고, 외부 변경은 300ms debounce 후 live project에 adopt한다.[7]
저장 큐는 프로젝트 경로별로 직렬화된다. frontmatter만 바꾸는 변경과 본문까지 다시 써야 하는 변경을 fm/full로 나누고, dirty task만 저장해 대형 프로젝트에서 불필요한 전체 재작성과 스크롤·선택 상태 손실을 줄인다.[7][12]
작업 제목을 바꾸면 파일명을 slug로 다시 계산하고, 기존 파일·첨부 폴더를 새 경로로 옮긴다.[7][21] 이름 충돌은 generic vault error가 아니라 TaskFileNameConflictError로 표면화해 사용자가 inline 오류를 볼 수 있게 한다.[7]
작업 본문은 처음부터 모두 읽지 않고 metadata cache에서 frontmatter를 먼저 로드한 뒤, Kanban 설명 preview나 task editor가 필요할 때 body를 lazy hydrate한다. 이는 큰 vault에서 초기 로딩 비용을 줄이는 선택이다.[7][25]
트리·복제·보관
TaskIndex는 task ID→task와 parent ID를 매핑한다. 로드 직후에는 전체 트리를 재구축하고, insert/move/delete/reparent 시에는 subtree 단위로 index를 갱신해 ID 조회와 parent 확인을 빠르게 한다.[6][18][7]
subtask는 부모 파일의 subtaskIds와 자식 파일의 parentId 양쪽으로 기록된다. 둘이 어긋나면 로더가 orphan의 parentId를 보고 부모 아래로 다시 붙이는 self-healing 경로를 사용하지만, 복구된 구조는 다음 저장에서 정규화된다.[7][17]
작업 복제는 새 ID·timestamp·파일 경로를 만들고, subtree 내부 dependency는 새 ID로 remap한다. 외부 task를 가리키는 dependency는 원래 ID를 유지하며, 동일 제목 충돌을 피하기 위해 (copy), (copy 2) 같은 이름을 예약한다.[18][7]
archive는 task와 모든 descendant의 Markdown 파일을 프로젝트 task 폴더 아래 Archive/로 이동하고, task별 첨부 폴더도 함께 이동한다. unarchive는 반대로 원래 task 폴더로 되돌리며, 파일 위치가 runtime archived 상태의 근거가 된다.[20][21]
의존성 기반 smart scheduling
Scheduler는 task를 dependency 그래프로 보고 Kahn 위상 정렬을 수행한다. 정렬되지 않은 잔여 노드는 cycle 후보로 반환하고, 작업 편집 UI도 새 dependency가 cycle을 만들면 선택지에서 제외한다.[8]
변경 task를 지정하면 해당 task에서 downstream dependent로 도달 가능한 범위만 계산할 수 있다. 각 predecessor의 due를 기준으로 dependent의 earliest start를 다음 날로 맞추고, start/due가 모두 있으면 작업 기간을 보존한다.[8]
terminal status의 작업은 자동으로 이동하지 않는다. pullForwardOnEarlyFinish가 켜져 있으면 예정 due보다 일찍 완료된 predecessor의 completion date를 실제 종료점으로 사용해 dependent를 앞당기되, 기존 slack과 predecessor 이후 시작 제약을 보존한다.[8]
이 기능은 범용 일정 최적화기가 아니라 날짜 필드와 dependency ID를 이용한 보수적 자동 조정이다. 동적 외부 캘린더, 휴일·근무일 캘린더, 리소스 용량, 팀 간 병렬성은 저장소의 핵심 모델에 포함되지 않는다.[6][8]
Import와 TaskNotes 연동
일반 Markdown note import
Command Palette의 Import notes as tasks는 기존 vault note를 task로 바꾸고, move 또는 copy를 선택하게 한다. move는 note를 프로젝트 task 폴더로 옮기고, copy는 원본을 남긴 채 새 task 파일을 만든다.[3][7]
pm-project: true를 기존 note에 추가하면 Open current file as project 명령으로 그 note를 프로젝트 파일처럼 열 수 있다. 선택한 텍스트에서 task를 만드는 command와 editor context-menu도 제공한다.[3][22]
TaskNotes 4.10+
TaskNotes가 설치되어 있고 API v1과 catalog.read capability를 제공할 때만 연동 페이지를 활성화한다. Project Manager는 TaskNotes의 status·priority palette를 읽어 자신의 palette에 upsert하고, 기존에 모르는 항목은 보존한다.[3][9]
TaskNotes task import는 scheduled/due 날짜, status·priority, tags, completion date, recurrence의 단순 FREQ/INTERVAL, time estimate, archive 상태를 매핑한다. TaskNotes의 project link는 parent/subtask 관계로, blockedBy는 dependency ID로 변환하며, import 집합 밖의 참조와 cycle은 제외·해제한다.[9][10]
README는 TaskNotes 쪽에서도 pm-task: true property와 scheduled → start mapping을 설정하면 Project Manager task를 변환 없이 읽고 편집할 수 있다고 설명한다. 단, 양쪽의 hierarchy/dependency 표현이 달라 TaskNotes 측에서 Project Manager의 전체 트리 semantics가 그대로 해석되지는 않는다.[3][9]
마이그레이션과 호환성
초기 버전은 한 프로젝트 파일 frontmatter 안에 tasks 배열을 embedded 저장했지만, 현재 loader는 taskIds와 개별 task files를 새 포맷으로 인식한다. layout-ready 시 old-format 프로젝트를 찾아 ProjectStore.saveProject()로 one-file-per-task 구조로 마이그레이션한다.[7][11][17]
versions.json은 각 플러그인 버전과 요구 Obsidian 버전을 기록한다. 이번 snapshot의 manifest는 1.13.0을 요구하는 반면 versions.json의 1.8.0 행은 1.7.2로 남아 있어, 배포·설치 호환성 판단에서는 manifest·실제 changelog·versions.json을 함께 확인해야 한다.[4][5][12]
CHANGELOG의 Unreleased 항목은 TaskNotes import, per-project statuses/priorities, early-finish pull-forward, progress, 설정 검색, view synchronization bug fixes를 다음 릴리스 후보로 나열한다. 1.8.0에는 pinned Gantt header와 선택 텍스트→task 생성이 기록되어 있다.[12]
설치와 개발
권장 설치는 Obsidian Community Plugins에서 Project Manager를 검색해 설치·활성화하는 방식이다. beta는 BRAT, 수동 설치는 release의 main.js, manifest.json, styles.css 세 파일을 .obsidian/plugins/project-manager/에 복사하는 방식이다.[3][4]
개발 환경은 Node 24와 pnpm 11.5.3을 기준으로 하며, pnpm check는 oxlint·oxfmt·TypeScript·submission ESLint를 실행한다. pnpm test는 Vitest, pnpm build는 TypeScript bundle과 CSS 빌드를 실행한다.[5][13][14]
release workflow는 tag가 manifest version과 versions.json에 존재하는지 확인하고, build 후 main.js, manifest.json, styles.css에 GitHub build-provenance attestation을 붙여 GitHub release를 만든다.[15]
이번 조사에서 수행한 검증
- 고정 clone의 작업 트리는 clean이며
main이 조사 커밋b42b5bf를 가리켰다.[2] corepack pnpm install --frozen-lockfile이 lockfile 기준 설치 상태를 확인해Already up to date로 종료했다.[5]corepack pnpm check가 type check·oxlint·oxfmt·submission ESLint를 모두 통과했다. 125개 source 파일에 formatting 오류가 없고 lint warning/error가 0이었다.[5]corepack pnpm test가 13개 테스트 파일, 232개 테스트를 모두 통과했다.[5]corepack pnpm build가 productionmain.js약 248KB와styles.css를 생성하며 성공했다.[5]- GitHub Actions에서도 같은 commit의 Test·Build·CodeQL workflow가 성공 상태였다.[13][14]
이는 플러그인 단위의 정적·단위·번들 검증이다. 실제 Obsidian desktop/mobile UI, 대형 vault, Obsidian Sync conflict, TaskNotes 설치 환경, 파일 시스템별 case-insensitive 동작은 이번 Linux 호스트에서 end-to-end로 실행하지 않았다.[3][13][14]
평가와 활용 판단
강점
- Markdown/YAML을 정본으로 유지해 Obsidian 검색, Git versioning, 다른 sync backend와 결합하기 쉽다.[3][17]
- Table·Gantt·Kanban이 같은 task tree를 공유하고, dependency cycle 감지·auto-schedule·undo/redo·bulk action까지 제공해 단순 checkbox 플러그인보다 프로젝트 관리 모델이 깊다.[3][8][22]
- one-file-per-task 저장, lazy body hydration, dirty-kind 저장, path별 queue는 vault 규모가 커질 때의 편집 충돌과 불필요한 I/O를 줄이려는 설계다.[7]
- TaskNotes API v1과 import forest를 통해 기존 Obsidian task workflow를 버리지 않고 단계적으로 옮길 수 있다.[9][10]
- 테스트·타입·린트·build·CodeQL과 release attestation을 모두 갖추어 배포 품질 신호가 비교적 명확하다.[5][13][15]
트레이드오프와 주의점
- 프로젝트 파일과 작업 파일이 여러 개로 나뉘므로 vault 이동·renaming·sync conflict 시 단일 문서보다 관리해야 할 경로가 많다.[3][7]
- 실시간 multi-user editing이 아니며, 같은 task 파일을 동시에 수정하면 일반 Markdown conflict를 직접 해결해야 한다.[3]
- auto-schedule은 dependency와 날짜 중심이므로 휴일, 근무시간, 리소스 capacity, 외부 캘린더를 계산하는 전문 PM 시스템의 대체재는 아니다.[6][8]
- 작업 삭제, archive 이동, 파일명 변경은 vault 파일과 첨부 폴더에 직접 side effect를 만들므로 Git 또는 Obsidian Sync backup을 먼저 두는 편이 안전하다.[7][20][21]
- TaskNotes 양방향 호환은 frontmatter를 비파괴적으로 유지하지만 hierarchy와 dependency 표현이 다르므로 양쪽을 동시에 정본으로 삼기보다 어느 플러그인이 구조의 source of truth인지 정해야 한다.[3][9][10]
- 이번 snapshot의
manifest.json과versions.json요구 버전 값이 다르므로, 특정 Obsidian 버전 지원을 배포 전에 실제 manifest와 release contract로 확인해야 한다.[4][5][15]
결론
obsidian-pm는 Obsidian을 떠나지 않고 개인·소규모 팀의 Markdown 프로젝트 데이터를 Table·Gantt·Kanban 작업면으로 승격시키는 로컬 우선 프로젝트 관리 플러그인이다. 특히 vault를 Git으로 관리하고, 작업 설명·링크·첨부까지 일반 파일로 보존하고 싶은 사용자에게 적합하다.[1][3]
선택 기준은 명확하다. Obsidian vault가 이미 업무의 정본이고 dependency·time tracking·saved view가 필요하면 도입 가치가 높다. 반대로 실시간 협업·리소스 계획·외부 캘린더·서버형 감사 로그가 핵심이면 이 플러그인을 전문 SaaS PM의 대체재로 보지 말고 Markdown 기반 개인 작업면으로 한정하는 것이 맞다.[3][6][8]
관련 링크
- moc-productivity — Markdown 기반 개인 프로젝트·업무 인터페이스를 연결한다.
- moc-dev-tools — Obsidian 플러그인·개발 도구 관점의 연결 지점이다.
- moc-ai-coding — 코드·에이전트 작업과 프로젝트 관리 도구를 비교할 때의 상위 MOC다.
- moc-llm-wiki — 파일 우선 지식 베이스와 Markdown 정본성의 공통 패턴을 연결한다.
Sources
[1] https://github.com/StepanKropachev/obsidian-pm [2] https://github.com/StepanKropachev/obsidian-pm/commit/b42b5bfe0e8329fcbd18a1118ed86476f7711baa [3] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/README.md [4] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/manifest.json [5] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/package.json [6] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/types.ts [7] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/store/ProjectStore.ts [8] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/store/Scheduler.ts [9] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/integrations/tasknotes.ts [10] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/integrations/tasknotesImport.ts [11] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/migration.ts [12] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/CHANGELOG.md [13] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/.github/workflows/test.yml [14] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/.github/workflows/build.yml [15] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/.github/workflows/release.yml [16] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/store/YamlParser.ts [17] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/store/YamlSerializer.ts [18] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/store/TaskTreeOps.ts [19] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/store/ProjectConfig.ts [20] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/store/ArchiveOps.ts [21] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/store/vaultFs.ts [22] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/main.ts [23] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/views/table/TableView.ts [24] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/views/gantt/GanttView.ts [25] https://raw.githubusercontent.com/StepanKropachev/obsidian-pm/b42b5bfe0e8329fcbd18a1118ed86476f7711baa/src/views/KanbanView.ts