1 제품 개요
한 줄 정의, 해결하려는 문제, 타깃 사용자, 핵심 가치 제안
한 줄 정의
Tempo는 칸반 보드로 업무를 관리하면서, 일과 시간 동안 주기적으로 "지금 무엇에 집중하고 있는지"를 가볍게 기록하게 하여, 작업별 실제 투입 시간(focus)과 위임/대기 시간(백그라운드)을 자동으로 정량화해 주는 데스크톱 앱이다.
해결하려는 문제
- 주관적 시간 인식의 오차 — 사람은 자신이 무엇에 얼마나 시간을 썼는지 정확히 기억하지 못한다. 사후 타임시트 작성은 부정확하고 귀찮다.
- 컨텍스트 스위칭의 비가시성 — 하루에 작업을 몇 번 바꿨는지, 한 작업에 얼마나 몰입했는지가 보이지 않는다.
- AI 에이전트 위임 시간의 누락 — Claude Code 같은 에이전트가 백그라운드로 돌리는 동안의 "진척"은 기존 타이머 앱에서 측정 단위로 잡히지 않는다.
- 회고 데이터 부재 — 하루를 마칠 때 "오늘 뭐 했지?"에 답할 구조화된 타임라인이 없다.
타깃 사용자
| 세그먼트 | 특징 | 핵심 니즈 |
|---|---|---|
| 1차: 지식 노동자 / 개발자 | 여러 작업을 병렬로 진행, AI 에이전트 활용, 셀프 시간관리 욕구 | 몰입 시간 가시화, 에이전트 위임 작업 추적 |
| 2차: 프리랜서 / 컨설턴트 | 클라이언트·프로젝트별 시간 청구 필요 | 작업별 정확한 시간 집계, 회고/리포트 |
| 3차: 자기계발형 직장인 | 딥워크 습관 형성, 산만함 줄이기 | 주기적 자기 점검, 집중 패턴 분석 |
핵심 가치 제안
2 핵심 컨셉 정의
작업 상태 체계 · focus 단일성 규칙 · 시간 측정 로직
2.1 작업 상태 체계
작업(Task)은 항상 아래 다섯 상태 중 정확히 하나를 가진다. 보드의 칸(컬럼)은 이 상태와 1:1 매핑된다. 단, 진행중·focus와 진행중·백그라운드는 같은 "진행중" 컬럼 안에서 배지로 구분한다(컬럼 4개, 상태 5개).
| 상태 | 의미 | 시간 측정 | focus 점유 |
|---|---|---|---|
| 해야할일 TODO | 착수 전 백로그 | 측정 안 함 | 불가 |
| 펜딩 PENDING | 외부 의존·블로커로 대기(리뷰 대기, 답변 대기 등). 일시적으로 진행 불가 | 측정 안 함 | 불가 |
| 진행중·focus | 지금 내가 직접 집중하는 단 하나의 작업 | focus 시간 누적 | 점유(동시 1개) |
| 진행중·백그라운드 | 진행 중이나 내 직접 집중은 아님(에이전트 위임, 빌드/렌더 대기 등) | 백그라운드 시간 누적 | 비점유(동시 다수 허용) |
| 완료 DONE | 종료된 작업 | 측정 중단·확정 | 불가 |
상태 전이 다이어그램
stateDiagram-v2
[*] --> TODO: 작업 생성
TODO --> FOCUS: 집중 시작 (focus 점유)
TODO --> BG: 백그라운드 착수
TODO --> DONE: 즉시 완료
FOCUS --> BG: 위임·자리비움 (focus 해제)
BG --> FOCUS: 다시 직접 집중 (focus 점유)
FOCUS --> PENDING: 블로커 발생
BG --> PENDING: 블로커 발생
PENDING --> FOCUS: 블로커 해소·집중
PENDING --> BG: 블로커 해소·백그라운드
FOCUS --> DONE: 완료
BG --> DONE: 완료
PENDING --> DONE: 완료/취소
DONE --> TODO: 재오픈(되돌리기)
note right of FOCUS
focus는 전체에서 동시에 1개만 존재한다.
새 작업이 FOCUS로 진입하면 기존 FOCUS는
자동으로 BG 또는 직전 상태로 강등된다 (2.2).
end note
그림 1. 작업 상태 전이도 — 전환 조건 포함
2.2 'focus는 단 하나' 규칙과 전환 동작
시스템 전체에서 진행중·focus 작업은 항상 0개 또는 1개다. 사용자가 작업 B를 focus로 지정하면 다음이 원자적으로(atomic) 일어난다:
- 현재 focus였던 작업 A의 진행 중 focus 세션을 종료(타이머 stop, 종료시각 기록).
- 작업 A를 어디로 보낼지 강등 규칙에 따라 결정:
- A가 여전히 "진행 중"이어야 하면 → 백그라운드로 강등하고 백그라운드 세션 시작.
- 사용자가 명시적으로 "A는 잠시 멈춤"을 선택하면 → 펜딩 또는 TODO로 이동, 어떤 타이머도 돌지 않음.
- 작업 B의 focus 세션 시작(타이머 start). B가 백그라운드였다면 백그라운드 세션을 종료하고 focus 세션으로 승격.
2.3 focus 시간 vs 백그라운드 시간
| 구분 | focus 시간 | 백그라운드 시간 |
|---|---|---|
| 정의 | 사용자가 직접 그 작업에 몰입한 시간 | 작업이 진행되지만 사용자 집중 밖에서 흐른 시간(에이전트 실행, 빌드 등) |
| 동시성 | 전체 합산 시 겹치지 않음(항상 1개) | 여러 작업이 동시 누적 가능 |
| 누적 시작 | 작업이 focus로 진입한 순간 | 작업이 백그라운드로 진입한 순간 |
| 누적 중단 | 다른 작업 focus 전환 / 일과 종료 / 유휴(idle) 임계 초과 / 펜딩·완료 이동 | 작업이 펜딩·완료·TODO로 이동 / 일과 종료 |
| 대표 지표 | 몰입도·실투입 | 위임/병렬 효율 |
t에서 focus 상태 작업 수 ∈ {0,1}. 따라서 하루 focus 시간 합 ≤ 일과 총 길이. 백그라운드 시간 합은 일과 길이를 초과할 수 있다(병렬).2.4 AI 에이전트 파이프라인 (노트 트래킹)
사용자는 자유형 마크다운 노트만 작성한다. 구조에 맞출 필요 없다. 앱은 15초마다 노트 스냅샷을 찍어 before/after diff를 추출하고, 3개의 AI 에이전트가 순차적으로 처리해 내부 task graph를 유지한다. 노트는 사용자 영역, task graph는 AI 내부 영역(사용자가 직접 편집 불가).
flowchart TD
U["사용자 — 자유형 마크다운 노트 작성/편집"]
U -->|15s 스냅샷| C["Collection: before/after diff 추출"]
C --> A1["Agent 1: Diff Parser\n변경 항목 추출 (들여쓰기 레벨 포함)"]
A1 --> A2["Agent 2: Task Mapper\n기존 task 매핑 or 신규 생성\nparent-child 관계 설정"]
A2 --> A3["Agent 3: Pattern Classifier\n변경 패턴 분류"]
A3 --> TG["Task Graph Store"]
TG --> K["Kanban · Mainboard"]
TG --> RS["Right Sidebar 이벤트 스트림"]
TG --> R["Report 세션 분석"]
그림 2-A. AI 에이전트 파이프라인 — 노트 diff → task graph
| 에이전트 | 입력 | 출력 | 핵심 규칙 |
|---|---|---|---|
| Agent 1 Diff Parser |
before / after 전체 마크다운 | ParsedChange[] — 추가/삭제/수정, 들여쓰기 레벨 | 체크박스 상태 변경은 별도 추출; 공백·줄바꿈 무시; 50자 미만 diff는 전후 3줄 컨텍스트 포함 |
| Agent 2 Task Mapper |
ParsedChange[] + 현재 task graph | TaskGraphCommand[] — CreateTask / UpdateTask / AddMemo | 기존 task 매핑 우선; 들여쓰기 상위 = parent_id; 신뢰도 < 0.7 시 신규 생성 |
| Agent 3 Pattern Classifier |
TaskGraphCommand[] + 변경 전후 상태 | PatternEvent[] — 이벤트 타입 라벨 | 같은 레벨 신규 항목 = EXPANSION; 들여쓰기 증가 = DECOMPOSITION; 체크박스 전환 = STATUS_CHANGE |
시간 측정 로직 흐름도
flowchart TD
A([틱: 1초/이벤트 발생]) --> B{일과 진행 중?}
B -- 아니오 --> Z[모든 타이머 정지]
B -- 예 --> C{유휴 임계 초과?}
C -- 예 --> I["focus 세션 일시정지
유휴로 기록"] --> D
C -- 아니오 --> R{focus 세션 복귀?}
R -- 예 --> RS[focus 세션 재개]
R -- 아니오 --> D
RS --> D
D{현재 focus 작업 존재?}
D -- 예 --> E[focus 작업 += 경과]
D -- 아니오 --> F[focus 누적 없음]
E --> G
F --> G
G[[백그라운드 작업 집합 순회]] --> H{각 BG 작업 진행중?}
H -- 예 --> J[해당 작업 백그라운드 += 경과]
H -- 아니오 --> K[건너뜀]
J --> L([세션 테이블에 반영])
K --> L
그림 2. 시간 측정 누적/중단 로직 — focus와 백그라운드는 독립 누적
3 사용자 시나리오 (User Story)
하루 흐름: 출근 → 작업 전환 → 에이전트 위임 → 퇴근 → 회고
US-1 · 출근, 일과 시작
개발자 지민은 09:00에 Tempo를 연다. 어제 설정한 일과(09:00–18:00)가 자동 시작되며 첫 Spotlight 팝업이 뜬다. "오늘 첫 focus는?"에 보드의 결제 모듈 리팩터링을 선택 → 즉시 focus 타이머가 돈다.
가치: 하루의 시작점과 첫 집중 대상이 1초 만에 기록된다.
US-2 · 작업 전환 (컨텍스트 스위칭)
10:20, 동료가 코드리뷰를 요청한다. 지민은 보드에서 PR #482 리뷰 카드를 focus로 드래그한다. 시스템은 결제 모듈 focus 세션을 종료하고 자동으로 백그라운드로 강등(혹은 펜딩 선택). 5분 뒤 Spotlight가 "여전히 PR 리뷰 중?"을 물으면 "맞음 — 코멘트 3개 작성 중"이라고 한 줄 남긴다.
가치: 전환 횟수·각 작업 점유 시간이 자동으로 기록된다.
US-3 · 에이전트에게 위임 (백그라운드)
11:00, 지민은 Claude Code에게 테스트 커버리지 90% 작업을 맡긴다. 이 카드를 진행중·백그라운드로 지정하고, 본인은 결제 모듈을 다시 focus로 가져온다. 이제 두 타이머가 동시에 흐른다 — 결제 모듈은 focus, 테스트 작업은 백그라운드.
가치: 내가 직접 한 시간과 에이전트가 대신 진행한 시간이 분리 집계된다.
US-4 · 점심·자리비움 (유휴 처리)
12:30, 지민이 점심으로 자리를 비운다. 키보드/마우스 입력이 없어 5분 후 유휴 임계를 넘기면 focus 타이머가 자동 일시정지되고 복귀 시 "12:30–13:25 자리 비움이었나요? 이 시간을 focus에서 제외할까요?"를 묻는다.
가치: 부풀려진 집중 시간이 자동 보정된다.
US-5 · 퇴근, 회고
18:00, 지민이 "일과 종료"를 누른다. 모든 타이머가 멈추고 일과 리포트가 뜬다 — 작업별 focus/백그라운드 시간, 전환 횟수, 그리고 각 Spotlight 때 남긴 한 줄 기록이 시간순 타임라인으로 정리된다. 지민은 "결제 모듈 3h12m, 전환 7회"를 보며 내일 우선순위를 정한다.
가치: 사후 작성 없이 구조화된 하루 회고가 완성된다.
4 기능 명세
기능별 동작 조건 · 입력/출력 · 예외 처리
| ID / 기능 | 동작 조건 | 입력 → 출력 | 예외 처리 |
|---|---|---|---|
| F1 칸반 보드 & 드래그앤드롭 | 앱 실행 시 항상 | 입력: 카드 드래그(컬럼 간 이동)·생성·편집 → 출력: 상태 변경, 타이머 start/stop 트리거 | focus 컬럼에 이미 1개 존재 시 §2.2 전환 로직 실행; 동일 위치 드롭은 no-op |
| F2 focus 지정 (단일성 보장) | TODO/PENDING/BG 카드를 focus로 지정 | 입력: focus 지정 액션 → 출력: 이전 focus 자동 강등, 새 focus 세션 시작 | 이미 focus면 무시; 강등 대상 처리 실패 시 트랜잭션 롤백 |
| F3 백그라운드 진행 (다중) | 진행 중이나 비집중 작업 | 입력: BG 지정 → 출력: 백그라운드 세션 시작, 다중 동시 허용 | BG 작업 수 상한(기본 20) 초과 시 경고; focus와 동일 작업 중복 불가 |
| F4 시간 측정 엔진 | 일과 진행 중 & 작업이 진행 상태 | 입력: 시스템 틱(1s)/상태 이벤트 → 출력: 세션별 누적초, 보드 카드 실시간 타이머 | 시스템 시계 점프/슬립 복귀 시 wall-clock 재동기화; 음수 경과 방지 |
| F5 Spotlight 체크인 팝업 | 일과 중, 설정 주기(기본 5분) 경과 | 입력: 작업 선택 + 한 줄 상태 메모 → 출력: PopupLog 1건, focus 갱신 | 무응답 시 §7-EC2; 입력 중 다음 주기 도래 시 큐잉(중복 표시 안 함) |
| F6 즉석 작업 생성 | 팝업/보드에서 "새 작업" | 입력: 제목(필수)·상태 → 출력: Task 신규 생성 후 즉시 focus 가능 | 빈 제목 거부; 중복 제목 허용(별 ID) |
| F7 일과(출퇴근) 관리 | 사용자 설정 또는 수동 | 입력: 시작/종료 시각, 자동시작 on/off → 출력: Workday 레코드, 팝업 스케줄러 가동/정지 | 종료 미클릭 시 §7-EC4 자동 마감; 시작<종료 검증 |
| F8 유휴(idle) 감지 | 입력 무활동이 임계(기본 5분) 초과 | 입력: OS 입력 이벤트 부재 → 출력: focus 세션 일시정지 + 복귀 시 확인 다이얼로그 | 권한 미허용 OS는 수동 모드로 폴백; 미디어 재생 중 예외 옵션 |
| F9 일과 종료 리포트 | 일과 종료 시점 | 입력: 당일 세션·팝업 로그 → 출력: 작업별 통계 + 타임라인 뷰 | 데이터 0건이면 빈 상태 안내; 진행 중 세션 강제 마감 후 집계 |
| F10 과거 기록 조회 | 언제든 | 입력: 날짜/기간 선택 → 출력: 해당 일과 리포트 재구성, 주/월 합계 | 해당일 데이터 없음 안내; 대용량은 페이지네이션 |
| F11 설정 | 언제든 | 입력: 팝업 주기·유휴 임계·일과 기본값·테마·알림음 → 출력: 즉시 반영 | 범위 밖 값(예: 주기<1분) 거부, 기본값으로 복원 |
| F12 마크다운 노트 에디터 | 앱 실행 시 항상 (Work 탭) | 입력: 자유형 마크다운 텍스트 → 출력: NoteSnapshot(15s), 실시간 렌더링 | 빈 노트 허용; 에디터 비활성 중에도 스냅샷 수집; WYSIWYG 목표(MVP는 plain textarea) |
| F13 AI 에이전트 파이프라인 | NoteSnapshot diff 발생 시 | 입력: before/after diff → 출력: TaskGraphCommand[], PatternEvent[], task graph 업데이트 | AI API 오류 시 재시도 3회 후 스킵; 신뢰도 낮은 매핑은 보류(다음 스냅샷 재처리) |
| F14 Task Graph 칸반 | AI 파이프라인 업데이트 시 | 입력: task graph → 출력: 칸반 카드(최상위 task) + 체크리스트(subtask), 실시간 갱신 | 칸반 직접 편집 불가(AI 내부 영역); 드래그앤드롭은 status 변경 전용 |
| F15 Right Sidebar 이벤트 스트림 | PatternEvent 발생 시 | 입력: PatternEvent → 출력: "10:32 — 'X' 아래 subtask 3개 분해됨" 형태 로그 | 이벤트 최대 200건 유지; 오래된 이벤트는 리포트로 이동 |
5 데이터 모델
주요 엔티티 · 속성 · 관계
erDiagram
WORKDAY ||--o{ TIME_SESSION : "포함"
WORKDAY ||--o{ POPUP_LOG : "포함"
TASK ||--o{ TIME_SESSION : "측정됨"
TASK ||--o{ POPUP_LOG : "참조됨"
TASK ||--o{ TIMELINE_MEMO: "이벤트 로그"
TASK }o--o| TASK : "parent(자기참조)"
NOTE_SNAPSHOT ||--o{ TASK : "원천"
PROJECT ||--o{ TASK : "그룹핑(선택)"
PROJECT {
uuid id PK
string name
string color
}
TASK {
uuid id PK
string label
string status "TODO|PENDING|FOCUS|BG|DONE"
uuid parent_id FK "nullable — 업무분해 계층"
string source_text "원본 노트 텍스트"
int cumulative_ms
uuid project_id FK "nullable"
datetime created_at
datetime completed_at "nullable"
}
TIMELINE_MEMO {
uuid id PK
uuid task_id FK
string event_type "EXPANSION|DECOMPOSITION|STATUS_CHANGE|NEW_TASK"
string description "AI 생성 설명"
datetime timestamp
}
NOTE_SNAPSHOT {
uuid id PK
datetime captured_at
text content "전체 마크다운"
}
TIME_SESSION {
uuid id PK
uuid task_id FK
uuid workday_id FK
string kind "FOCUS|BACKGROUND"
datetime started_at
datetime ended_at "nullable=진행중"
int idle_sec "유휴 제외분"
}
POPUP_LOG {
uuid id PK
uuid workday_id FK
uuid task_id FK "nullable=무응답"
datetime shown_at
datetime responded_at "nullable"
string note "한 줄 상태"
string outcome "ANSWERED|SNOOZED|MISSED|NEW_TASK"
}
WORKDAY {
uuid id PK
date date
datetime start_at
datetime end_at "nullable=진행중"
bool auto_started
}
SETTINGS {
int popup_interval_min "기본 5"
int idle_threshold_min "기본 5"
time default_start
time default_end
string theme
}
그림 3. 데이터 모델 관계도(ER) — AI 파이프라인 엔티티 포함
엔티티 설명
- Task — AI가 노트에서 추출한 작업 단위.
parent_id로 분해 계층 표현.source_text는 원본 노트 텍스트.cumulative_ms는 누적 작업 시간(TimeSession 합). - TimelineMemo — task에 발생한 이벤트 로그. Agent 3가 생성.
event_type으로 확장(EXPANSION)·분해(DECOMPOSITION)·상태변경·신규 구분. 리포트 타임라인의 원재료. - NoteSnapshot — 15s마다 수집된 전체 마크다운. Diff Parser의 입력. 원천 데이터(source of truth).
- TimeSession — 시간 측정 원천. 하나의 (작업 × 종류 × 연속 구간).
ended_at=null이면 진행 중. focus 세션은 전체에서 동시 1개만 진행(불변식). - PopupLog — Spotlight 체크인 노출/응답 1건.
outcome으로 무응답·스누즈·신규 구분. - Workday — 출퇴근 1일 단위. 팝업 스케줄과 리포트 집계의 경계.
- Project(선택) — 작업 그룹핑·색상. MVP에서는 단순 태그 수준.
- Settings — 단일 레코드(앱 전역 설정).
6 화면 구성 (와이어프레임)
메인 보드 · Spotlight 팝업 · 일과 종료 리포트 · 과거 기록
6.1 메인 보드
해야할 일
펜딩
진행 중
완료
상단 바: 일과 상태·경과·현재 focus 표시 + [일과 종료] 버튼. 4개 컬럼, 진행 중 컬럼 내 카드는 focus(⏺)/백그라운드(🤖) 배지와 실시간 타이머. 드래그앤드롭으로 상태 전환. 카드 클릭 → 상세(제목·메모·세션 히스토리).
6.2 Spotlight 체크인 팝업
지금 무엇에 집중하고 있나요?
상태 한 줄 기록
화면 중앙 모달, 키보드 우선(↑↓ 선택·Enter 저장·Esc 스누즈). 현재 focus는 최상단에 강조. 새 작업은 인라인 생성. 한 줄 메모는 선택 입력(비워도 저장 가능).
6.3 일과 종료 리포트
09:05 결제 모듈 시작
10:20 PR 리뷰로 전환
11:00 테스트 위임(bg)
6.4 과거 기록 조회
화면 흐름도
flowchart LR
L[앱 실행] --> B[메인 보드]
B -->|일과 시작/자동| B
B -. 주기 경과 .-> P[Spotlight 팝업]
P -->|저장/스누즈| B
B -->|새 작업| N[작업 생성 모달]
N --> B
B -->|카드 클릭| D[작업 상세]
D --> B
B -->|일과 종료| R[일과 리포트]
R -->|닫기| B
B -->|기록 메뉴| H[과거 기록 조회]
H -->|날짜 선택| R2[과거 리포트]
R2 --> H
B -->|설정| S[설정]
S --> B
그림 4. 화면 흐름도(Screen Flow)
Spotlight 팝업 인터랙션 플로우
sequenceDiagram
participant Sch as 스케줄러
participant UI as Spotlight 팝업
participant U as 사용자
participant E as 시간엔진
Sch->>UI: 주기 도래 → 팝업 표시(현재 focus 강조)
UI->>U: "지금 집중 대상?" + 메모 입력
alt 기존 작업 유지/선택
U->>UI: 작업 선택 + 한 줄 메모
UI->>E: focus 갱신(필요 시 전환 로직)
UI->>UI: PopupLog(ANSWERED) 저장
else 새 작업 생성
U->>UI: 제목 입력
UI->>E: 신규 Task 생성 후 focus 지정
UI->>UI: PopupLog(NEW_TASK) 저장
else 스누즈(Esc)
U->>UI: 나중에
UI->>Sch: N분 뒤 재노출
UI->>UI: PopupLog(SNOOZED) 저장
else 무응답(타임아웃)
UI->>UI: PopupLog(MISSED) 저장, focus 유지
end
UI-->>Sch: 다음 주기 예약
그림 5. Spotlight 팝업 인터랙션 플로우
7 엣지 케이스
정상 흐름에서 벗어나는 상황과 처리 규칙
| ID | 상황 | 처리 규칙 |
|---|---|---|
| EC1 | 일과 시간 외에 작업 | 일과 종료 후엔 팝업이 뜨지 않고 신규 측정도 기본 비활성. 사용자가 작업을 focus로 옮기면 "지금 일과를 다시 열까요?(연장/임시 일과)"를 제안. 임시 일과로 측정하면 리포트에 '일과 외' 구간으로 별도 표기. |
| EC2 | 팝업 무응답 | 표시 후 타임아웃(기본 60초) 시 자동 닫힘 + PopupLog(MISSED). focus는 직전 상태 유지(시간 계속 누적). 연속 N회(기본 3) 무응답 시 "자리 비움?"으로 간주해 다음 팝업 주기를 늘리고 유휴 검사 강화. |
| EC3 | 자정 넘김 | 진행 중 세션을 23:59:59에서 split하여 당일/익일 2개 세션으로 저장(집계 경계 보존). 일과가 자정을 넘기게 설정된 경우(야근/야간 근무) Workday는 시작일 기준 1건으로 유지하되 날짜 경계 마커를 둠. |
| EC4 | 일과 종료 미클릭(잊고 자리 뜸) | 설정 종료시각 + 유예(기본 2h) 경과 & 무활동이면 자동 마감. 마감 시각은 '마지막 활동 시각'으로 보정(빈 시간 부풀림 방지). 다음 실행 시 "어제 일과를 OO시에 마감했어요" 안내. |
| EC5 | 동시 백그라운드 다수 | 허용(설계 의도). 단 카드별 독립 누적이라 총 백그라운드 합 > 실제 경과가 정상. 리포트에서는 'wall-clock(실경과)'과 '백그라운드 누적합'을 분리 표기해 오해 방지. 상한 초과 시 경고. |
| EC6 | focus 작업을 직접 완료/펜딩 이동 | focus 세션 종료 후 focus 공석(0개). 다음 Spotlight 또는 즉시 프롬프트로 새 focus 유도. 공석 상태에서는 focus 시간 누적 없음. |
| EC7 | 시스템 슬립/절전 복귀 | monotonic이 아닌 wall-clock 기준 재계산. 슬립 구간은 focus에서 제외(유휴로 처리), 단 BG가 '에이전트 실행'이면 사용자 선택으로 포함 가능. |
| EC8 | 시계 변경/타임존 이동 | 저장은 UTC. 사용자 시계 역행 감지 시 음수 경과를 0으로 클램프하고 이상 로그 기록. |
| EC9 | 앱 강제 종료 중 진행 세션 | 세션 시작은 영속 저장되어 있으므로 재실행 시 미마감 세션을 감지 → '마지막 하트비트 시각'까지로 복구 마감 후 사용자에게 보정 제안. |
| EC10 | 유휴 권한 거부(특히 Linux/Wayland) | OS 유휴 API 불가 시 수동 모드로 폴백: 팝업 응답·명시적 일시정지에만 의존. 설정에서 상태 안내. |
8 MVP 범위 vs 향후 확장
먼저 만들 것과 나중에 더할 것
MVP (v1.0) — 핵심 루프 검증
- 마크다운 노트 에디터 — plain textarea(F12)
- 15s NoteSnapshot 수집 (F12)
- Agent 1 + Agent 2 — Diff Parser + Task Mapper (F13)
- Task Graph 저장 (parent_id, cumulative_ms, timeline_memos)
- Task Graph 기반 칸반 (F14) — status 드래그앤드롭
- focus 단일성 + 백그라운드 다중 (F2·F3)
- 시간 측정 엔진 + 실시간 타이머 (F4)
- Spotlight 체크인 팝업 (F5·F6)
- 일과 시작/종료 + 스케줄러 (F7)
- 일과 종료 리포트 + 타임라인 (F9)
- 로컬 저장(SQLite) · 단일 사용자
향후 확장 (v1.1+)
- Agent 3 — Pattern Classifier (EXPANSION·DECOMPOSITION 감지) (F13)
- Right Sidebar 이벤트 스트림 (F15)
- 유휴 자동 일시정지 (F8, 가능한 OS)
- WYSIWYG 마크다운 에디터 (F12 upgrade)
- 주/월/프로젝트별 분석 대시보드
- AI 신뢰도 낮은 매핑 사용자 확인 UI
- 클라우드 동기화·멀티 디바이스
- 캘린더 연동, Slack/Jira 연동
- 자연어 회고 요약(AI), 모바일 컴패니언
9 기술 스택 제안
크로스플랫폼 구현 방식 · 선택 이유 · 데이터 저장
9.1 크로스플랫폼 프레임워크
| 후보 | 장점 | 단점 | 본 제품 적합성 |
|---|---|---|---|
| Tauri (Rust + WebView) 권장 | 경량(수~십 MB), 메모리 적음, OS 네이티브 API·트레이·전역 단축키·유휴 감지에 강함, 보안 모델 우수 | Rust 학습 곡선, 일부 WebView 렌더 편차 | 높음 — 상주형 트레이 앱 + 빈번한 OS 이벤트(유휴/입력)와 궁합 |
| Electron (Chromium + Node) | 생태계·자료 풍부, 단일 렌더 엔진으로 일관성, 빠른 개발 | 무겁고(100MB+) 메모리 큼 — 종일 상주 앱엔 부담 | 중 — 빠른 프로토타입엔 좋으나 상주 부담 |
| Flutter Desktop | 단일 코드·고성능 렌더, 일관 UI | 데스크톱 OS 통합(트레이/유휴) 성숙도 낮음 | 중하 — OS 통합 의존도 높은 본 제품엔 리스크 |
9.2 아키텍처 & 데이터 저장
- 프론트엔드(WebView): TypeScript + React, 보드는
dnd-kit/@hello-pangea/dnd, 차트는 경량 라이브러리. 상태관리 Zustand. - 코어(Rust): 시간 측정 엔진·스케줄러·유휴 감지·트레이를 네이티브에 둠(UI가 닫혀도 정확한 측정 보장). 1초 틱 대신 이벤트 기반 + wall-clock 차감으로 절전.
- 데이터 저장: 로컬 SQLite(local-first). 마이그레이션 관리, 트랜잭션으로 focus 전환 원자성 보장. 파일은 OS 앱 데이터 디렉터리에 저장, 사용자 내보내기(JSON/CSV) 제공.
- 동기화(확장): 초기엔 미포함. 확장 시 SQLite → 동기 가능 백엔드(예: 서버 + CRDT/이벤트 로그)로 점진 이행.
- 시간 정합성: 모든 시각 UTC 저장, monotonic 보조 + wall-clock 기준 재동기화(슬립/시계변경 대비, §7).
9.3 핵심 기술 리스크
| 리스크 | 완화책 |
|---|---|
| OS별 유휴 감지 편차(특히 Linux/Wayland) | 플랫폼별 어댑터 + 미지원 시 수동 모드 폴백(EC10) |
| 장시간 상주 시 측정 드리프트 | 틱 누적이 아닌 세션 start/end의 wall-clock 차이로 산출 |
| focus 전환 동시성 버그 | 상태 전환을 단일 트랜잭션으로 직렬화, 불변식 검사 테스트 |
| 팝업 방해감 | 주기·스누즈·무응답 학습으로 적응형 빈도, 키보드 1초 응답 |