Awesome Anki는 **Anki 노트를 학습 효율이 높은 원자 카드(Atomic Card)**로 분할하고, 검증하고, 필요 시 복구(롤백)할 수 있는 로컬 우선 웹 애플리케이션입니다.
- 1. 프로젝트 소개
- 2. 핵심 기능
- 3. 기술 스택
- 4. 모노레포 구조
- 5. 빠른 시작
- 6. 실행 방법
- 7. 환경 변수
- 8. 개발 DX 도구
- 9. API 레퍼런스
- 10. 품질 검증
- 11. 운영/트러블슈팅 체크
- 12. 자가 호스팅 (Self-Host)
- 13. 참고 링크
- 14. 라이선스
기존 Anki 카드에 정보가 과도하게 밀집되면 복습 효율이 떨어질 수 있습니다. Awesome Anki는 다음 흐름으로 이 문제를 해결합니다.
- 카드 구조를 분석해 분할 가능 여부를 판단합니다.
- AI(Gemini/OpenAI)로 카드를 의미 단위로 분리합니다.
- 분할 전 백업을 생성하고, 적용 실패 시 자동 롤백합니다.
- 팩트/최신성/유사성/문맥 검증으로 결과 품질을 점검합니다.
- AI Split: Gemini 또는 OpenAI를 사용해 비정형 텍스트를 의미 단위로 분할
- 멀티 프로바이더: 웹 UI에서 프로바이더/모델 선택, 모델별 결과 비교 가능
- 비용 가시화: 분석 전 비용 추정, 분석 후 실측 비용 + 토큰 사용량 표시
- 예산 가드레일: 서버 사이드 예산 상한으로 과도한 비용 방지
- Preview / Apply 분리: 적용 전 미리보기로 결과 확인 가능
- 학습 데이터 복제: 분할 후 카드에 스케줄링 정보 복제 시도
- Fact Check: 카드 내용의 사실성 점검
- Freshness: 최신성 점검(기준 날짜 기반)
- Similarity: Jaccard + 임베딩 기반 유사 카드 탐지
- Context: nid 링크 기반 문맥 일관성 점검
- All-in-one 검증: 단일 요청으로 통합 검증 실행
- 분할 전 백업(
preBackup) 필수 생성 - 적용 실패 시 자동 롤백
- 손상 백업 파일 자동 격리(
.corrupt-*) - 원클릭 수동 롤백 API 제공
- 프롬프트 버전 생성/수정/활성화/삭제
- 시스템 프롬프트 원격 SoT 저장 (
awesomeAnki.prompts.system) expectedRevision기반 CAS 충돌 제어 (409반환)- systemPrompt 저장 성공 시 즉시 Anki
sync()실행 - 분할 히스토리 저장/조회
- 실패 패턴 분석
- A/B 실험(Experiment) 생성 및 완료 처리
/api/health를 제외한 API는ANKI_SPLITTER_API_KEY인증 필요
| 영역 | 기술 |
|---|---|
| 런타임 | Bun |
| 언어 | TypeScript |
| 서버 | Hono |
| 웹 | React 19 + Vite |
| 상태 관리 | TanStack Query |
| 스타일링 | Tailwind CSS v4 |
| 렌더링 | markdown-it + KaTeX |
| LLM | Google Gemini + OpenAI |
| 연동 | AnkiConnect |
awesome-anki/
├── src/ # 루트 CLI 엔트리
├── packages/
│ ├── core/ # 도메인 로직(분할/검증/백업)
│ ├── server/ # Hono REST API
│ └── web/ # React + Vite 웹 UI
├── output/ # 런타임 산출물(백업, 임베딩 캐시, 프롬프트 버전/legacy 기록)
├── docs/ # 아키텍처/기능/트러블슈팅 문서
└── templates/ # 카드 템플릿 리소스
git clone https://github.com/greenheadHQ/awesome-anki.git
cd awesome-anki
bun installcp .env.example .env실제 운영값은 이 저장소 기준으로 .envrc에서 export 관리하며, 비밀값은 secrets/*.age로 암호화 관리합니다.
# 서버 + 웹 동시 실행
bun run dev
# 서버만 실행 (기본: 3000)
bun run dev:server
# 웹만 실행 (기본: 5173)
bun run dev:web# 연결 상태 확인
bun run cli:status
# 덱 분할 미리보기
bun run cli:split
# 덱 분할 적용
bun run cli:split -- --apply
# 특정 노트 분할
bun run cli split --note <noteId>
# 분석 명령 (내부용)
bun run cli analyze <deckName> [noteId]
# 백업 목록/롤백
bun run cli backups
bun run cli rollback <backupId>| 변수 | 설명 |
|---|---|
GEMINI_API_KEY |
Gemini API 키 (split/validation에서 Gemini 사용 시 필요) |
OPENAI_API_KEY |
OpenAI API 키 (임베딩 필수, split/validation에서 OpenAI 사용 시 필요) |
ANKI_SPLITTER_API_KEY |
서버 API 인증 키 |
ANKI_CONNECT_URL |
AnkiConnect URL |
ANKI_CONNECT_VERSION |
AnkiConnect 버전(기본 6) |
TARGET_DECK |
기본 대상 덱 |
SPLIT_HISTORY_DB_PATH |
분할 이력 SQLite 파일 경로 (기본 data/split-history.db) |
HISTORY_SYNC_MODE |
히스토리 동기화 모드 (local / remote) |
ANKI_SPLITTER_DEFAULT_LLM_PROVIDER |
기본 LLM 프로바이더 (gemini / openai, 기본 gemini) |
ANKI_SPLITTER_DEFAULT_LLM_MODEL |
기본 LLM 모델 (기본 gemini-3-flash-preview) |
ANKI_SPLITTER_BUDGET_CAP_USD |
서버 사이드 예산 상한 USD (기본 1.0) |
| 변수 | 설명 |
|---|---|
VITE_API_URL |
웹에서 직접 호출할 API 베이스 URL |
VITE_API_PROXY_TARGET |
Vite dev 프록시 타깃(기본 http://localhost:3000) |
VITE_LOCATOR_TARGET |
Locator 에디터 타깃 (cursor/vscode) |
VITE_DISABLE_LOCATOR |
Locator 강제 비활성화 |
이 섹션은 LocatorJS를 실제로 어떻게 켜고 끄는지, 어떤 설정값이 영향을 주는지를 정리합니다.
- LocatorJS는
packages/web/src/main.tsx에서import.meta.env.DEV조건으로만 초기화됩니다. - 즉,
bun run dev일 때만 동작하고bun run build산출물에서는 실행되지 않습니다. - 비활성화는 아래 환경변수로 즉시 제어할 수 있습니다.
VITE_DISABLE_LOCATOR=true
- 런타임 패키지 설치
@locator/runtime
- Vite Babel 체인에 data-id 플러그인 연결
@locator/babel-jsx/dist- 개발 모드에서만 적용 (
mode === "development")
- 런타임 초기화
setupLocatorUI({ targets, showIntro: false })showIntro: false로 온보딩 팝업 비활성화
- 에디터 타깃 선택
- 기본:
VITE_LOCATOR_TARGET=cursor - 전환:
VITE_LOCATOR_TARGET=vscode
- 기본:
bun run dev실행- 브라우저에서 점프하려는 컴포넌트 위에 마우스를 올림
- macOS 기준
Option + Click실행 - 설정된 에디터(Cursor/VS Code)에서 해당 파일+라인으로 이동
- 점프가 안 되면 먼저
VITE_DISABLE_LOCATOR가true인지 확인 - 반드시 Vite 개발 서버(
bun run dev또는bun run dev:web)에서 테스트 - 소스 메타가 누락되면
packages/web/vite.config.ts의 Babel 플러그인 설정 확인
- 평소: Locator 활용 (기본 활성)
- 불필요 시:
VITE_DISABLE_LOCATOR=true로 비활성화
/api/health를 제외한 API는 인증 필요- 인증 방법:
X-API-Key: <ANKI_SPLITTER_API_KEY>Authorization: Bearer <ANKI_SPLITTER_API_KEY>
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/health |
서버 헬스 체크 |
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/decks |
덱 목록 |
| GET | /api/decks/:name/stats |
덱 통계 |
| GET | /api/cards/deck/:name |
덱 카드 목록 |
| GET | /api/cards/deck/:name/difficult |
어려운 카드 목록 |
| GET | /api/cards/:noteId |
단일 카드 상세 |
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/llm/models |
사용 가능 LLM 모델/가격 조회 |
| POST | /api/split/preview |
분할 미리보기 (provider/model/budgetUsdCap 선택 가능, 예산 초과 시 HTTP 402) |
| POST | /api/split/apply |
분할 적용 |
| POST | /api/split/reject |
분할 반려 처리 |
| GET | /api/history |
분할 이력 목록 조회 |
| GET | /api/history/:sessionId |
분할 이력 상세 조회 |
| GET | /api/history/sync/health |
히스토리 동기화 상태 |
| GET | /api/backup |
백업 목록 |
| GET | /api/backup/latest |
최신 백업 ID |
| POST | /api/backup/:id/rollback |
롤백 실행 |
| GET | /api/media/:filename |
Anki 미디어 프록시 |
| 메서드 | 경로 | 설명 |
|---|---|---|
| POST | /api/validate/fact-check |
팩트 체크 |
| POST | /api/validate/freshness |
최신성 검사 |
| POST | /api/validate/similarity |
유사성 검사 |
| POST | /api/validate/context |
문맥 검사 |
| POST | /api/validate/all |
통합 검증 |
| 메서드 | 경로 | 설명 |
|---|---|---|
| POST | /api/embedding/generate |
덱 임베딩 생성/갱신 |
| GET | /api/embedding/status/:deckName |
임베딩 캐시 상태 |
| DELETE | /api/embedding/cache/:deckName |
캐시 삭제 |
| POST | /api/embedding/single |
단일 텍스트 임베딩(디버그용) |
임베딩 API 응답은 공통 envelope를 사용합니다:
- 성공:
ok=true,schemaVersion,requestId,timestamp(ISO 8601),data - 실패:
ok=false,schemaVersion,requestId,timestamp(ISO 8601),error.code/message/retryable POST /api/embedding/generate는 부분 실패 시에도 HTTP 200을 유지하고data.status=completed_with_errors와data.failures[]로 상세를 반환합니다.
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/prompts/system |
원격 systemPrompt + revision 조회 |
| POST | /api/prompts/system |
CAS 기반 systemPrompt 저장 + sync |
| GET | /api/prompts/versions |
프롬프트 버전 목록 |
| GET | /api/prompts/versions/:id |
버전 상세 |
| POST | /api/prompts/versions |
버전 생성 |
| PUT | /api/prompts/versions/:id |
버전 수정 (systemPrompt 수정 불가) |
| DELETE | /api/prompts/versions/:id |
버전 삭제 |
| POST | /api/prompts/versions/:id/activate |
버전 활성화 |
| GET | /api/prompts/active |
현재 활성 버전 조회 |
| GET | /api/prompts/versions/:id/failure-patterns |
실패 패턴 분석 |
| GET | /api/prompts/experiments |
실험 목록 |
| GET | /api/prompts/experiments/:id |
실험 상세 |
| POST | /api/prompts/experiments |
실험 생성 |
| POST | /api/prompts/experiments/:id/complete |
실험 완료 |
추가 정책:
- systemPrompt SoT는 Git tracked file이 아닌 miniPC AnkiConnect config다.
- 로컬 파일 fallback 저장은 금지되며, sync 실패 시 저장 요청은 실패 처리된다.
루트 디렉터리에서 실행합니다.
# 빠른 검증 (PR 최소 기준)
bun run check:quick
# 전체 검증 (권장)
bun run check세부 검증:
bun run lint
bun run typecheck
bun run test
bun run buildCI에서는 .github/workflows/ci.yml의 quality-gate가 bun run check를 실행합니다.
문제 발생 시 아래 순서 권장:
bun run check:quickbun run checkANKI_SPLITTER_API_KEY및 API 헤더 확인ANKI_CONNECT_URL연결 확인
상세 트러블슈팅 문서: docs/TROUBLESHOOTING.md
# 호스트에 데이터 디렉토리 생성 및 권한 설정 (UID 1001 = 컨테이너 내부 anki 사용자)
sudo mkdir -p /srv/awesome-anki/{data,output}
sudo chown -R 1001:1001 /srv/awesome-ankipodman run -d --name awesome-anki \
--network=host \
-e GEMINI_API_KEY=<your-key> \
-e ANKI_CONNECT_URL=http://100.79.80.95:8765 \
-e ANKI_SPLITTER_REQUIRE_API_KEY=false \
-v /srv/awesome-anki/data:/app/data \
-v /srv/awesome-anki/output:/app/output \
ghcr.io/greenheadhq/awesome-anki:latest
ANKI_SPLITTER_REQUIRE_API_KEY=false: Tailscale/VPN 등 네트워크 격리 환경에서만 사용하세요. 공개 네트워크에 노출되는 경우 reverse proxy 단에서 인증(basic auth 등)을 반드시 설정해야 합니다.
# /etc/awesome-anki/env 예시
GEMINI_API_KEY=...
OPENAI_API_KEY=...
ANKI_CONNECT_URL=http://100.79.80.95:8765
ANKI_SPLITTER_REQUIRE_API_KEY=false
PORT=3100podman run -d --name awesome-anki \
--network=host \
--env-file /etc/awesome-anki/env \
-v /srv/awesome-anki/data:/app/data \
-v /srv/awesome-anki/output:/app/output \
ghcr.io/greenheadhq/awesome-anki:latestCaddy 사이트블록 예시 (NixOS 등에서 관리):
anki.greenhead.dev {
reverse_proxy localhost:3100
}상세 인프라 구성은 greenheadHQ/nixos-config#75를 참고하세요.
- 아키텍처:
docs/ARCHITECTURE.md - 기능 상세:
docs/FEATURES.md - 트러블슈팅:
docs/TROUBLESHOOTING.md - 작업 로드맵:
docs/TODO.md
- Bun: https://bun.sh/
- Hono: https://hono.dev/
- React: https://react.dev/
- Vite: https://vite.dev/
- Tailwind CSS: https://tailwindcss.com/
- TanStack Query: https://tanstack.com/query
- Google Gemini API: https://ai.google.dev/
- OpenAI API: https://platform.openai.com/
- Anki: https://apps.ankiweb.net/
- AnkiConnect: https://ankiweb.net/shared/info/2055492159
- LocatorJS: https://github.com/infi-pc/locatorjs
- LocatorJS React data-id 설치 가이드: https://www.locatorjs.com/install/react-data-id?stack=Vite
MIT