+
+ saveNote(strokes)}
+ />
+
+
+ {/if}
diff --git a/migrations/177_document_notes.sql b/migrations/177_document_notes.sql
new file mode 100644
index 0000000..614e2f9
--- /dev/null
+++ b/migrations/177_document_notes.sql
@@ -0,0 +1,22 @@
+-- 177_document_notes.sql
+-- 자료별 손글씨 노트 — 자료 학습 시 옆에 띄워놓고 필기.
+-- plan: ~/.claude/plans/scalable-chasing-stonebraker.md (PR-D)
+--
+-- 모델: 사용자×자료 1:1. UNIQUE (user_id, document_id) 로 강제.
+-- strokes_json 은 perfect-freehand input points + style.
+-- canvas_width/height 는 마지막 저장 시점의 캔버스 표시 크기 (참고용, 렌더는 자체 비율 유지).
+--
+-- 회독 (document_reads) 와 별개 — append-only log 가 아닌 upsert 방식.
+
+CREATE TABLE IF NOT EXISTS document_notes (
+ id BIGSERIAL PRIMARY KEY,
+ user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+ document_id BIGINT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
+ strokes_json JSONB,
+ canvas_width INTEGER,
+ canvas_height INTEGER,
+ schema_version INTEGER NOT NULL DEFAULT 1,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ UNIQUE (user_id, document_id)
+);
diff --git a/migrations/178_document_notes_idx.sql b/migrations/178_document_notes_idx.sql
new file mode 100644
index 0000000..9eba5d3
--- /dev/null
+++ b/migrations/178_document_notes_idx.sql
@@ -0,0 +1,5 @@
+-- 178_document_notes_idx.sql (2/2)
+-- (user_id, document_id) UNIQUE 제약이 자체 인덱스 생성하지만 명시 추가 — 조회 단순화.
+
+CREATE INDEX IF NOT EXISTS idx_document_notes_user_doc
+ ON document_notes (user_id, document_id);