diff --git a/app/api/documents.py b/app/api/documents.py
index e645065..8407168 100644
--- a/app/api/documents.py
+++ b/app/api/documents.py
@@ -315,6 +315,60 @@ async def list_library_documents(
)
+# ─── Section 2: 카테고리 집계 (Sidebar / Dashboard) ───
+#
+# documents.category (§1 에서 추가) 가 1차 진입점. 이 엔드포인트는 Sidebar 배지 및
+# /dashboard 카테고리 카드 용. ai_suggestion.proposed_category='library' 인
+# 승인 대기 건수는 /library 의 pending 배지로 별도 표시.
+
+
+@router.get("/stats/category-counts")
+async def get_category_counts(
+ user: Annotated[User, Depends(get_current_user)],
+ session: Annotated[AsyncSession, Depends(get_session)],
+):
+ """카테고리별 문서 건수 + 승인 대기 (library 제안) 건수.
+
+ Response:
+ {
+ "counts": { "document": 640, "library": 12, "news": 311, ... },
+ "library_pending_suggestions": 17
+ }
+
+ - 전제: §1 의 documents.category enum + ai_suggestion JSONB 가 이미 적용됨
+ - category IS NULL 인 문서는 counts 에서 제외 (§1 백필 전 드문 상태)
+ """
+ from sqlalchemy import text as sql_text
+
+ count_rows = await session.execute(
+ sql_text("""
+ SELECT category::text AS category, COUNT(*) AS cnt
+ FROM documents
+ WHERE deleted_at IS NULL
+ AND category IS NOT NULL
+ GROUP BY category
+ """)
+ )
+ counts: dict[str, int] = {row.category: row.cnt for row in count_rows}
+
+ pending_scalar = (
+ await session.execute(
+ sql_text("""
+ SELECT COUNT(*)
+ FROM documents
+ WHERE deleted_at IS NULL
+ AND ai_suggestion IS NOT NULL
+ AND ai_suggestion->>'proposed_category' = 'library'
+ """)
+ )
+ ).scalar()
+
+ return {
+ "counts": counts,
+ "library_pending_suggestions": int(pending_scalar or 0),
+ }
+
+
@router.get("/", response_model=DocumentListResponse)
async def list_documents(
user: Annotated[User, Depends(get_current_user)],
diff --git a/frontend/src/lib/components/Sidebar.svelte b/frontend/src/lib/components/Sidebar.svelte
index 0742713..e37ea69 100644
--- a/frontend/src/lib/components/Sidebar.svelte
+++ b/frontend/src/lib/components/Sidebar.svelte
@@ -1,14 +1,40 @@
+
+자동 분류 제안
+ {#if !loading}
+
+ {total}건
+
+ {/if}
+
+ {#each docs as doc (doc.id)}
+ {@const busy = rowBusy[doc.id]}
+ {@const isSel = selected.has(doc.id)}
+
+ {/if}
+