개요
Yanki는 Markdown 노트 폴더를 Anki와 동기화합니다.
이 접근 방식의 주요 참신함은 Markdown이 Anki 노트로 어떻게 번역되는지, 그리고 폴더가 Anki 덱으로 어떻게 번역되는지에 있습니다:
-
하나의 Markdown 파일은 하나의 Anki 노트에 매핑됩니다.
-
Markdown 노트의 구조가 그것이 되는 Anki 노트의 유형을 결정하므로, 추가적인 구문이나 Anki 전용 마크업이 필요하지 않습니다. 순수한 Markdown만 사용하면 됩니다.
-
Markdown 노트의 상위 폴더는 필요한 모든 중간 계층 구조와 함께 Anki에서 해당 노트를 포함하는 덱 이름을 결정합니다.
이 패키지는 독립형 CLI 도구와 다른 프로젝트에 쉽게 통합할 수 있는 TypeScript 라이브러리를 모두 제공합니다.
이 라이브러리는 yanki-obsidian Obsidian 플러그인의 구동 엔진입니다. Anki에 대한 더 낮은 수준의 접근이 필요하면, yanki-connect TypeScript 라이브러리를 확인해 보세요.
Yanki의 'Y' 접두사는 'Yet another'라는 명명 전통에 따른 것으로, Anki의 강력하고 때로는 중복적인 서드파티 도구 생태계에 대한 경의를 표한 것입니다. (또한 적절하게도, Yankī는 무단결석 학생의 한 종류입니다.)
빠른 시작
아직 설치하지 않았다면 Node.js를 설치하세요. (Yanki는 Node 22.18 이상을 필요로 합니다.)
Markdown 노트 파일 폴더가 있다고 가정하고, Anki 앱이 열려 있고 AnkiConnect 애드온이 설치되어 있다고 가정합니다:
npx yanki ./folder-of-markdown
이렇게 하면 폴더의 Markdown 파일들이 Anki 노트로 변환되어 Anki 데이터베이스로 전송됩니다.
기능
하나의 Markdown 파일 = 하나의 Anki 노트
다중 노트 및 단일 노트 구문을 혼합하고 매칭하는 복잡성을 피하세요. 로컬 Markdown 파일 하나는 항상 하나의 Anki 노트를 생성합니다.
로컬 폴더 계층 구조 = Anki 덱 계층 구조
Yanki는 소스 Markdown 파일의 부모 디렉터리 이름을 덱 이름으로 사용합니다. 복잡한 폴더 계층 구조도 지원하며, 로컬 파일 시스템의 구조와 일치하도록 필요한 경우 Anki 덱을 생성하고 중첩합니다.
Anki 기본 노트 유형 수용
노트 유형이 많으면 문제가 많아집니다.
Yanki는 Anki 앱에서 기본으로 제공되는 Basic, Basic (및 역방향 카드), Basic (답변에 입력하기), 그리고 Cloze 노트 유형으로만 Markdown을 변환하는 것을 지원합니다.
Markdown 구조에서 Anki 노트 유형 추론
지원되는 노트 유형의 수가 적기 때문에, 주어진 문서에서 생성할 Anki 노트의 유형은 Markdown 구조에 대한 몇 가지 간단한 규칙으로부터 추론될 수 있습니다.
예를 들어, Basic 노트는 카드 앞면과 뒷면을 분리하는 --- 가로줄이 있는 모든 Markdown 파일입니다:
그게 전부이며, 추가 메타데이터나 Anki 전용 마크업은 필요하지 않습니다. 노트 스타일링을 위해 원하는 추가 Markdown 구문을 추가할 수 있습니다.
지원되는 네 가지 노트 유형의 구조적 단서(structural cues)는 본 문서의 여기에서 설명합니다.
선택적으로, Markdown 파일의 프론트매터에 tags 배열을 추가하여 Anki 데이터베이스와 자동으로 동기화되도록 할 수 있습니다. 프론트매터는 초기 동기화 후 Anki 노트의 ID를 저장하는 데도 사용됩니다.
중첩된 태그(Nested tags)가 지원되며, Obsidian과 같은 도구와의 호환성을 위해 Markdown 프론트매터에서 구분자(delimiter)로 :: 외에 /를 사용할 수 있습니다.
태그 배열의 내용은 반드시 원시 문자열 값(primitive string values)이어야 합니다. 비문자열 값은 모두 문자열로 강제 변환됩니다.
지능형 동기화(Intelligent synchronization)
사용자의 로컬 Markdown 파일이 Anki에 들어갈 내용의 유일한 진실 공급원(single point of truth)이지만, Yanki는 사용자가 가지고 있는 다른 Anki 노트들은 건드리지 않도록 합니다.
로컬 Markdown 노트를 편집할 때, Yanki는 진행 상황을 보존하기 위해 Anki 데이터베이스에서 노트를 새로 생성하기보다는 업데이트하는 데 최선을 다합니다.
하지만 무언가를 삭제하고 싶다면, 파일 시스템에서 로컬 Markdown 노트를 삭제하고 yanki sync를 실행하여 Anki 데이터베이스에서도 제거하면 됩니다. Yanki가 처음에 생성하지 않은 Anki 노트는 삭제되지 않도록 보호 장치가 마련되어 있습니다.
만약 AnkiWeb을 사용하여 노트를 클라우드에 동기화한다면, Yanki는 이 다음 단계도 트리거하여 Markdown → Anki → AnkiWeb의 흐름을 한 번에 자동화합니다.
로컬 Markdown 노트에서 연관된 Anki noteId가 포함된 프론트매터를 실수로 삭제하더라도, Yanki는 프론트매터를 복구하고 Markdown 노트를 기존 Yanki 노트와 다시 연결하는 데 최선을 다할 것입니다.
기존 노트는 건드리지 않음(Existing notes are untouched)
Yanki는 Anki 노트 모델의 숨겨진 필드를 사용하여 자신이 관리하는 노트를 추적하므로, 사용자의 기존 Anki 노트에는 절대 손대지 않습니다.
고급 Markdown(Fancy Markdown)
확장된 Markdown 구문 팔레트가 기본적으로 제공됩니다:
사용자 정의 스타일 (Custom styles)
Yanki는 기본적으로 Anki의 내장 CSS 스타일시트를 사용하여 카드를 꾸미지만, Anki 사용자 인터페이스에서 클릭 미션을 수행할 필요 없이 Yanki로 관리하는 모든 노트에 대해 사용자 지정 스타일시트를 설정하기 쉽게 만듭니다.
파일명 관리 (Filename management)
하나의 마크다운 파일이 하나의 Anki 노트가 될 수 있어 개별 파일이 많이 생길 수 있고, 콘텐츠가 수정될 때마다 파일을 생각하고 이름을 바꾸는 것은 지루할 수 있습니다. 따라서 Yanki를 사용하면 내용에 기반하여 노트 파일의 이름을 관리할 수 있습니다.
Yanki는 각 노트를 살펴보고 '프롬프트'(대부분의 경우 카드 앞면) 텍스트 또는 '응답'(대부분의 경우 카드 뒷면) 텍스트 중 하나를 추출하여 파일 이름으로 사용합니다. 자르기(Truncation), 중복 제거(deduplication), 그리고 정제(sanitization)는 모두 처리됩니다.
엣지 케이스(Edge cases)는 신중하게 관리되어 항상 어떤 종류의 최선의 노력으로 의미 있는 파일 이름이 할당되도록 보장합니다.
미디어 에셋 동기화
Yanki는 노트에 포함된 이미지, 비디오 및 오디오 파일을 Anki의 미디어 에셋 관리 시스템과 동기화할 수 있습니다. 사용자의 선택에 따라 로컬 에셋만, URL을 통해 연결된 에셋만, 둘 다, 또는 아무것도 동기화하지 않을 수 있습니다.
Yanki는 노트가 삭제되거나 특정 에셋이 노트에서 제거될 때 동기화된 미디어 에셋의 정리(clean-up)를 자동으로 관리합니다.
위키 스타일의 ![[something.png]]와  에셋 임베딩 구문 모두 지원됩니다.
마크다운 노트 유형
Yanki는 사용자의 마크다운 파일에서 특정 요소의 존재 여부나 부재 여부를 기반으로 Anki에 생성하려는 노트의 _유형_을 자동으로 추론합니다.
이 규칙들은 마크다운의 의미적(semantic) 및 시각적(visual) 특성을 염두에 두고 설계되었습니다.
서로 다른 노트 유형을 '트리거'하는 가장 최소한의 예시는 아래에 나와 있지만, 구현은 추가적인 이상함(weirdness)도 처리할 수 있으며 일반적으로 충돌하는 노트 유형을 나타낼 수 있는 요소를 만나더라도 올바르게 작동합니다.
노트 파일의 앞면과 뒷면을 스타일링하고 구조화하기 위해 마크다운을 자유롭게 사용할 수 있습니다.
주의할 점은 Anki의 기본 구문(예: {{c1::... 클로즈 마크업)과 Yanki 마크다운을 섞어 사용할 수는 없다는 것입니다.
기본 (Basic)
기본(basic) 카드는 ---가 있는 모든 파일에서 생성됩니다:
This is the front of the card
---
...
기본 (및 역순 카드)
---를 두 번 사용하면 해당 노트를 가역적(reversible) 노트로 식별하며 (Anki에서 두 개의 카드가 생성됩니다).
기억술: 카드 개수가 두 배가 되려면 ---도 두 배로 사용하세요.
Yanki는 또한 양쪽 생성된 카드 모두에 나타날 "추가(extra)" 콘텐츠를 추가하는 것도 지원합니다:
기본 (답변 입력 유형)
Markdown 파일의 마지막 문장이 _이렇게 강조된 경우_이면, 이는 Anki에서 답변 입력 텍스트로 사용됩니다.
<em>기억술: 이 구문은 _채워야 할 빈칸_과 유사합니다.</em>
재즈는 죽지 않았다.
_단지 이상한 냄새가 날 뿐이다_
클로즈 (Cloze)
~~취소선 친~~ 형식으로 된 다소 난해한 이중 물결표 구문은 결과 클로즈(cloze) 카드에서 숨겨집니다:
기억술: ~~취소선 친~~은 삭제를 의미합니다.
여러 클로즈가 지원되며, 이는 추가 카드를 생성합니다. 또한 ---를 추가하여 카드 뒷면 정보도 포함할 수 있습니다. 힌트(Hints)도 지원하며, 클로즈 취소선 끝에 힌트 텍스트를 _강조_ 형태로 제공하여 표시됩니다:
~~모든~~ 것은 ~~드러날 _여기에 힌트가 있어요_~~ 것이다.
---
...
기본적으로, 카드에 클로즈가 하나 이상 있을 경우 순차적으로 번호가 매겨집니다. 예를 들어 다음 Markdown이 사용됩니다:
~~모든~~ 것은 ~~드러날 _여기에 힌트가 있어요_~~ 것이다.
이는 내부적으로 다음과 같은 Anki 구문으로 변환됩니다:
{{c1::모든}}은 {{c2::드러날::<em>여기에 힌트가 있어요</em>}}이다.
드물지만, 클로즈 번호 매기기를 직접 제어하고 싶을 수 있습니다. 예를 들어 여러 개의 클로즈를 단일 카드에서 동시에 표시하거나, 특히 클로즈가 많은 노트에 특정 클로즈들을 그룹화하고 싶을 수 있습니다.
이를 지원하기 위해 Yanki는 몇 가지 선택적 추가 구문을 제공합니다. 클로즈된 콘텐츠 앞에 붙은 한두 자리 숫자는 클로즈 번호로 해석됩니다:
예를 들어:
1 All은 {{c1::All}}이(가) 될 것이며, {{c1::revealed::<em>here's a hint</em>}}에서 공개될 것입니다.
차이는 미묘하지만, 일치하는 {{c1을 주목하세요. 이 마크업은 'All'과 'revealed' 모두 동시에 공개되는 단일 카드를 생성합니다.
참고: 이미지, 수학 방정식 및 기타 인라인 스타일 문법으로 감쌀 수는 있지만, 여러 줄 또는 블록 요소를 클로징하는 것은 현재 지원되지 않습니다.