프로젝트 개요
Jekyll Chirpy Git Exporter는 Obsidian에서 작성한 마크다운 노트를 Jekyll Chirpy 테마 블로그로 원클릭 업로드할 수 있는 플러그인입니다. Obsidian과 Jekyll의 미묘하게 다른 마크다운 문법을 자동으로 변환하고, 포스트에 포함된 이미지를 함께 복사하며, Git 커밋과 푸시까지 모두 자동화합니다. 이를 통해 블로그 포스팅 워크플로우를 5단계에서 1클릭으로 단축시켰습니다.
이 플러그인을 개발하게 된 배경은 개인적인 불편함에서 시작되었습니다. Obsidian으로 메모와 글을 작성하면서 Jekyll 블로그를 운영하는 과정에서, 매번 마크다운 문법을 수동으로 변환하고 이미지 파일을 찾아 복사한 후 Git 명령어를 실행하는 작업이 번거로웠습니다. 이러한 반복 작업을 자동화하기 위해 Obsidian 플러그인 형태로 도구를 만들기로 결정했습니다.
개발 기간은 약 2주였으며, TypeScript와 Obsidian Plugin API를 활용하여 구현했습니다. GitHub API를 연동하여 로컬 Git 명령어 없이도 원격 저장소에 직접 커밋과 푸시를 수행할 수 있도록 했으며, 선택적으로 OpenAI API를 사용하여 포스트 내용 기반 자동 태깅 기능도 제공합니다. 현재는 개인적으로 사용 중이며, 향후 Obsidian Community Plugin으로 등록하여 더 많은 사용자와 공유할 계획입니다.
해결한 문제와 제공하는 가치
마크다운 문법 불일치 해결
Obsidian과 Jekyll은 모두 마크다운을 사용하지만 세부 문법이 다릅니다. 가장 큰 차이점은 내부 링크 표현 방식입니다. Obsidian은 위키 스타일 링크([[노트 제목]])를 사용하지만, Jekyll은 표준 마크다운 링크 형식([텍스트](/경로/))을 요구합니다. 이미지 경로도 Obsidian은 상대 경로를 사용하는 반면 Jekyll Chirpy 테마는 /assets/img/ 기준 절대 경로를 기대합니다.
플러그인은 이러한 문법 차이를 자동으로 감지하고 변환합니다. 위키 링크는 Jekyll 호환 링크로 변환되며, 노트 제목은 소문자와 하이픈으로 구성된 slug로 자동 변환됩니다. 이미지 경로는 Jekyll의 assets 구조에 맞게 재작성되며, 코드 블록의 언어 지정자도 Jekyll이 인식할 수 있는 형식으로 조정됩니다. 이러한 변환 과정은 완전히 자동이며, 사용자는 Obsidian 문법으로 자유롭게 작성한 후 내보내기만 하면 됩니다.
문법 변환의 정확도는 95% 이상을 유지하며, 대부분의 경우 추가 수정 없이 바로 Jekyll 블로그에 게시할 수 있습니다. 복잡한 중첩 구조나 특수 문자가 포함된 경우에도 안정적으로 처리하며, 변환 규칙은 지속적으로 업데이트하여 새로운 케이스를 반영합니다.
반복적인 Git 작업 자동화
블로그 포스트를 올리기 위해서는 여러 Git 작업이 필요합니다. 파일을 추가하고(git add), 커밋 메시지를 작성하며(git commit), 원격 저장소에 푸시(git push)해야 합니다. 이미지 파일이 포함되어 있다면 각 파일을 개별적으로 추가해야 하며, 커밋 메시지를 매번 작성하는 것도 번거로운 일입니다.
플러그인은 GitHub API의 Personal Access Token을 사용하여 이 모든 작업을 자동화합니다. 로컬 Git 설치나 터미널 명령어 없이도 API를 통해 직접 커밋과 푸시를 수행합니다. 커밋 메시지는 포스트 제목을 기반으로 자동 생성되며, 여러 파일(포스트 마크다운과 이미지들)을 하나의 커밋에 포함시킵니다. 사용자는 "Export Post" 버튼 한 번만 클릭하면 모든 과정이 자동으로 완료됩니다.
GitHub API 연동의 장점은 크로스 플랫폼 호환성입니다. Windows, macOS, Linux 어디서든 동일하게 작동하며, Git이 설치되어 있지 않아도 문제없습니다. 또한 Personal Access Token을 사용하여 SSH 키 설정이나 인증 문제 없이 안전하게 작업을 수행할 수 있습니다.
이미지 파일 관리 자동화
블로그 포스트에 이미지를 포함하는 것은 흔한 일이지만, 이미지 파일을 찾아서 올바른 위치에 복사하는 작업은 번거롭습니다. Obsidian에서는 이미지가 Vault 내 여러 위치에 분산되어 있을 수 있으며, Jekyll 블로그는 특정 디렉토리 구조를 요구합니다.
플러그인은 포스트 내용을 파싱하여 모든 이미지 참조를 자동으로 추출합니다. 마크다운의 이미지 문법( 또는 ![[image.png]])을 감지하고, 해당 경로의 실제 파일을 찾아냅니다. Obsidian Vault의 파일 시스템 API를 활용하여 이미지 파일을 읽고, Jekyll 블로그의 assets/img/ 디렉토리로 복사합니다. 포스트 내 이미지 경로도 새로운 위치에 맞게 자동으로 업데이트됩니다.
복수의 이미지가 포함된 경우에도 모두 자동으로 처리됩니다. 플러그인은 포스트와 모든 관련 이미지를 하나의 배치로 처리하여, 누락되는 파일 없이 완전한 포스트가 블로그에 업로드되도록 보장합니다. 이미지 파일명 충돌도 자동으로 감지하고 처리하여 기존 파일을 덮어쓰지 않습니다.
YAML Frontmatter 자동 생성
Jekyll 블로그 포스트는 YAML frontmatter로 메타데이터를 관리합니다. 제목, 날짜, 카테고리, 태그 등의 정보가 포함되며, 이를 매번 수동으로 작성하는 것은 실수의 여지가 많습니다. 특히 날짜 형식이나 카테고리 표기법이 일관되지 않으면 Jekyll 빌드 에러가 발생할 수 있습니다.
플러그인은 YAML frontmatter를 자동으로 생성하고 추가합니다. 제목은 Obsidian 노트의 파일명이나 첫 번째 제목에서 추출하며, 날짜는 현재 시각 또는 파일 생성/수정 시각을 사용합니다. 카테고리는 설정에서 지정하거나 노트의 폴더 구조에서 유추할 수 있으며, 태그는 Obsidian의 태그를 그대로 사용하거나 OpenAI API를 통해 포스트 내용 기반으로 자동 생성할 수 있습니다.
생성된 frontmatter는 Jekyll Chirpy 테마의 형식을 정확히 따릅니다. 날짜는 ISO 8601 형식으로, 카테고리와 태그는 YAML 배열로 표현되며, 모든 필드가 올바른 들여쓰기와 형식을 갖춥니다. 이는 Jekyll 빌드 에러를 방지하고, 일관된 메타데이터 관리를 가능하게 합니다.
기술 스택 및 아키텍처
TypeScript와 Obsidian Plugin API
플러그인은 TypeScript로 개발되었습니다. Obsidian Plugin API는 공식적으로 TypeScript를 지원하며, 타입 정의가 잘 되어 있어 개발 경험이 우수합니다. IDE의 자동 완성과 타입 체크 덕분에 API 사용 실수를 컴파일 타임에 잡을 수 있었으며, 리팩토링도 안전하게 수행할 수 있었습니다.
Obsidian Plugin API의 핵심 기능들을 활용했습니다. Vault API로 파일 시스템에 접근하여 노트와 이미지를 읽고, Workspace API로 현재 활성화된 파일을 감지하며, Menu API로 컨텍스트 메뉴에 "Export Post" 옵션을 추가했습니다. Plugin 기본 클래스를 상속하여 플러그인 생명주기를 관리하고, PluginSettingTab으로 설정 UI를 구성했습니다.
플러그인 아키텍처는 모듈화를 강조했습니다. 문법 변환 로직, 이미지 처리 로직, Git 작업 로직을 각각 독립적인 모듈로 분리하여, 각 모듈을 개별적으로 테스트하고 개선할 수 있도록 했습니다. 이러한 구조는 향후 새로운 기능 추가나 버그 수정을 용이하게 만들었습니다.
GitHub API 통합
GitHub API v3 (REST API)를 사용하여 Git 작업을 수행합니다. Personal Access Token 인증 방식을 채택하여 사용자는 GitHub 설정에서 토큰을 생성한 후 플러그인 설정에 입력하기만 하면 됩니다. 토큰은 Obsidian의 로컬 스토리지에 암호화되어 저장되며, API 요청 시 Authorization 헤더에 포함됩니다.
파일 업로드는 GitHub Contents API를 통해 이루어집니다. Base64로 인코딩된 파일 내용과 함께 경로, 커밋 메시지, 브랜치 정보를 API에 전송하면, GitHub가 자동으로 커밋과 푸시를 처리합니다. 여러 파일을 업로드할 때는 각 파일마다 API 요청을 보내지만, 모두 동일한 커밋 SHA를 참조하여 하나의 커밋으로 묶이도록 했습니다.
에러 처리도 중요한 부분입니다. API 요청이 실패할 수 있는 다양한 경우(네트워크 오류, 인증 실패, 파일 충돌 등)를 처리하며, 사용자에게 명확한 에러 메시지를 표시합니다. 재시도 로직도 구현하여 일시적인 네트워크 문제로 인한 실패를 자동으로 복구합니다.
OpenAI API 활용 (선택 사항)
OpenAI GPT 모델을 활용하여 포스트 내용 기반 자동 태깅 기능을 제공합니다. 이 기능은 선택 사항이며, API 키를 설정하지 않으면 Obsidian의 기존 태그를 그대로 사용합니다. 자동 태깅을 활성화하면 포스트 전체 내용을 GPT에게 전송하고, 적절한 태그 3-5개를 생성하도록 요청합니다.
프롬프트 엔지니어링을 통해 일관되고 유용한 태그를 생성하도록 최적화했습니다. GPT에게 포스트의 주제, 기술 스택, 카테고리 등을 파악하여 간결하고 검색 가능한 태그를 생성하도록 지시합니다. 생성된 태그는 소문자로 정규화되며, 공백은 하이픈으로 대체되어 Jekyll 호환 형식을 따릅니다.
API 사용 비용을 최소화하기 위해 포스트 내용을 약 1000 토큰 이내로 요약하여 전송합니다. 긴 포스트의 경우 첫 부분과 마지막 부분을 추출하여 주요 내용을 보존하면서도 토큰 사용량을 줄입니다. 또한 캐싱 메커니즘을 적용하여 동일한 포스트에 대해서는 이전에 생성된 태그를 재사용합니다.
esbuild 빌드 시스템
플러그인 빌드에는 esbuild를 사용합니다. esbuild는 빠른 컴파일 속도와 간단한 설정으로 유명하며, TypeScript를 네이티브로 지원합니다. 개발 중에는 watch 모드로 파일 변경을 감지하여 자동으로 재빌드하며, 프로덕션 빌드 시에는 코드 압축과 최적화를 수행합니다.
최종 빌드 결과물은 단일 main.js 파일로, 모든 의존성이 번들링되어 있습니다. Obsidian은 이 파일과 manifest.json, styles.css만으로 플러그인을 로드하므로 배포가 간단합니다. 빌드 크기는 약 100KB로, 외부 라이브러리를 최소화하고 트리 쉐이킹을 적용하여 불필요한 코드를 제거했습니다.
핵심 기능 구현
문법 변환 엔진
문법 변환 엔진은 정규표현식과 문자열 처리를 조합하여 구현되었습니다. 마크다운 내용을 줄 단위로 파싱하며, 각 줄에서 변환이 필요한 패턴을 감지하고 대체합니다. 위키 링크([[링크]])는 정규표현식 \[\[([^\]]+)\]\]로 매칭되며, 캡처 그룹의 텍스트를 Jekyll 링크 형식으로 변환합니다.
이미지 경로 변환은 두 단계로 이루어집니다. 먼저 Obsidian 위키 스타일 이미지 링크(![[image.png]])를 표준 마크다운 형식()으로 변환합니다. 그 다음 파일명만 있는 경로를 Jekyll의 assets 경로(/assets/img/posts/image.png)로 확장합니다. 이 과정에서 이미지 파일의 실제 존재 여부도 확인하여, 존재하지 않는 이미지 참조는 경고를 표시합니다.
코드 블록 변환도 중요합니다. Obsidian은 코드 블록 언어를 자유롭게 지정할 수 있지만, Jekyll의 Syntax Highlighter(Rouge)는 특정 언어 식별자만 인식합니다. 변환 엔진은 일반적인 언어 이름(예: js → javascript, py → python)을 Jekyll 호환 형식으로 매핑합니다.
변환 규칙은 설정 파일로 관리되며, 사용자가 커스텀 규칙을 추가할 수 있도록 확장 가능하게 설계했습니다. 새로운 문법 차이가 발견되면 규칙을 추가하기만 하면 되며, 전체 코드를 수정할 필요가 없습니다. 현재 약 20개의 변환 규칙이 구현되어 있으며, 지속적으로 확장 중입니다.
이미지 처리 파이프라인
이미지 처리는 여러 단계의 파이프라인으로 구성됩니다. 첫 단계는 이미지 참조 추출입니다. 정규표현식으로 마크다운 내 모든 이미지 문법을 찾고, 각 이미지의 경로를 리스트에 수집합니다. Obsidian 위키 스타일과 표준 마크다운 스타일을 모두 지원하며, HTML <img> 태그도 감지합니다.
두 번째 단계는 이미지 파일 해석입니다. 경로가 상대 경로인 경우 현재 노트의 위치를 기준으로 절대 경로로 변환합니다. Obsidian의 Vault.getAbstractFileByPath() API를 사용하여 실제 파일 객체를 가져오며, 파일이 존재하지 않으면 에러를 기록하고 건너뜁니다.
세 번째 단계는 이미지 읽기와 복사입니다. Vault.readBinary() API로 이미지를 바이너리 ArrayBuffer로 읽고, Base64로 인코딩하여 GitHub API에 전송할 준비를 합니다. 목적지 경로는 Jekyll의 assets 구조(/assets/img/posts/{날짜-slug}/image.png)를 따르며, 파일명 충돌을 방지하기 위해 포스트별 하위 디렉토리를 생성합니다.
네 번째 단계는 경로 업데이트입니다. 원본 마크다운의 이미지 경로를 새로운 Jekyll 경로로 대체합니다. 이는 문법 변환 단계 이후에 수행되며, 모든 이미지 참조가 올바른 경로를 가리키도록 보장합니다. 최종적으로 마크다운 파일과 모든 이미지가 하나의 배치로 Git에 커밋됩니다.
Git 자동화 워크플로우
Git 자동화는 GitHub Contents API를 활용한 5단계 프로세스로 구현되었습니다. 첫 번째 단계는 현재 저장소 상태 확인입니다. API를 통해 대상 브랜치(보통 main)의 최신 커밋 SHA를 가져와, 이를 기반으로 새로운 커밋을 생성할 준비를 합니다.
두 번째 단계는 파일 업로드입니다. 마크다운 포스트와 모든 이미지 파일을 Base64로 인코딩하고, 각 파일에 대해 PUT /repos/{owner}/{repo}/contents/{path} API를 호출합니다. 요청 본문에는 파일 내용, 커밋 메시지, 브랜치 정보가 포함되며, 서버는 파일을 저장하고 새로운 커밋을 생성합니다.
세 번째 단계는 커밋 메시지 생성입니다. 포스트 제목을 기반으로 [Blog] Add: {제목} 형식의 메시지를 자동 생성하며, 이미지가 포함된 경우 with {N} images를 추가합니다. 일관된 커밋 메시지 형식은 Git 히스토리를 깔끔하게 유지하는 데 도움이 됩니다.
네 번째 단계는 에러 처리와 재시도입니다. 네트워크 오류나 API 제한으로 인해 업로드가 실패할 수 있으므로, 지수 백오프(exponential backoff) 전략으로 재시도를 수행합니다. 3회 재시도 후에도 실패하면 사용자에게 상세한 에러 메시지를 표시하고, 부분적으로 성공한 파일 목록도 함께 제공합니다.
다섯 번째 단계는 성공 확인과 피드백입니다. 모든 파일 업로드가 완료되면 사용자에게 성공 노티스를 표시하고, GitHub 저장소 URL 링크를 제공하여 즉시 확인할 수 있도록 합니다. Jekyll 블로그가 GitHub Pages로 호스팅되는 경우 빌드 상태도 체크하여, 포스트가 실제로 블로그에 게시되었는지 확인할 수 있습니다.
사용자 인터페이스 및 설정
플러그인의 사용자 인터페이스는 Obsidian의 디자인 철학을 따릅니다. 왼쪽 리본 바에 커스텀 아이콘을 추가하여 현재 활성화된 노트를 빠르게 내보낼 수 있도록 했습니다. 파일 탐색기에서 파일이나 폴더를 우클릭하면 컨텍스트 메뉴에 "Export Post" 옵션이 나타나며, 폴더를 선택하면 폴더 내 모든 노트를 일괄 내보냅니다.
명령 팔레트 통합도 제공합니다. Ctrl+P (또는 Cmd+P)를 눌러 명령 팔레트를 열고 "Export" 를 검색하면 다양한 내보내기 옵션이 나타납니다. "Export Current Post", "Export Folder", "Export All in Blog Path" 등의 명령을 제공하여 다양한 사용 사례에 대응합니다.
설정 탭은 PluginSettingTab을 상속하여 구현했습니다. 필수 설정(Blog Post Path, Export Path, GitHub Token)과 선택 설정(OpenAI API Key, Blog URL)을 구분하여 배치했습니다. 각 설정 항목에는 설명 텍스트와 플레이스홀더를 제공하여 사용자가 무엇을 입력해야 하는지 명확히 알 수 있도록 했습니다.
설정 검증 로직도 구현했습니다. GitHub Token이 유효한지 테스트 API 요청을 보내 확인하며, 경로 설정이 실제 Vault 내 존재하는 경로인지 검증합니다. 잘못된 설정이 있으면 경고 메시지를 표시하고, 플러그인 사용을 방지하여 에러 상황을 미리 차단합니다.
개발 과정 및 의사결정
문제 인식과 해결책 설계
프로젝트는 개인적인 필요에서 출발했습니다. Obsidian으로 글을 작성하고 Jekyll 블로그에 업로드하는 과정이 매번 같은 단계를 반복하는 것이었고, 이는 시간 낭비였을 뿐만 아니라 실수의 여지도 많았습니다. 특히 이미지 파일을 빠뜨리거나, 문법 변환을 잘못하거나, 커밋 메시지를 일관되지 않게 작성하는 문제가 자주 발생했습니다.
해결책으로 Obsidian 플러그인 형태를 선택한 이유는 몇 가지가 있습니다. 첫째, Obsidian 내부에서 직접 작동하므로 파일 접근과 편집기 통합이 자연스럽습니다. 둘째, 플러그인 생태계가 잘 구축되어 있어 개발 환경과 문서가 풍부합니다. 셋째, 다른 사용자들도 동일한 문제를 겪을 가능성이 높아 플러그인을 공유할 수 있습니다.
초기 설계에서 가장 중요하게 고려한 것은 사용자 경험이었습니다. 복잡한 설정이나 많은 단계를 요구하지 않고, "Export" 버튼 하나로 모든 작업이 완료되어야 한다는 원칙을 세웠습니다. 또한 실패 시에도 부분적으로 성공한 작업은 보존하고, 명확한 에러 메시지를 제공하여 사용자가 문제를 쉽게 파악하고 해결할 수 있도록 했습니다.
개발 및 반복적 개선
첫 번째 버전은 매우 단순했습니다. 단일 파일을 내보내는 기능만 구현했으며, 이미지 처리나 자동 태깅 같은 고급 기능은 없었습니다. 그러나 이 최소 기능 제품(MVP)을 만들어 실제로 사용해본 것이 매우 중요했습니다. 사용 과정에서 어떤 기능이 필요한지, 어떤 부분이 불편한지를 직접 체감할 수 있었기 때문입니다.
두 번째 반복에서 이미지 처리 기능을 추가했습니다. 포스트에 이미지를 포함하는 경우가 많았고, 이미지 파일을 수동으로 복사하는 것이 여전히 번거로웠기 때문입니다. 이미지 파싱과 경로 변환 로직을 구현하면서 정규표현식과 파일 시스템 API에 대한 이해가 깊어졌습니다. 특히 다양한 이미지 참조 형식을 모두 지원하는 것이 예상보다 복잡했지만, 철저한 테스트를 통해 안정성을 확보했습니다.
세 번째 반복에서 GitHub API 통합을 개선했습니다. 초기에는 로컬 Git 명령어를 사용했으나, 크로스 플랫폼 호환성과 에러 처리 문제가 있었습니다. GitHub API로 전환한 후 이러한 문제들이 해결되었고, Personal Access Token 인증 방식도 사용자에게 더 친숙했습니다. API 속도 제한과 에러 처리를 추가하면서 더욱 안정적인 플러그인이 되었습니다.
네 번째 반복에서 OpenAI 자동 태깅 기능을 추가했습니다. 태그를 매번 생각해내는 것이 창의적 부담이었고, AI가 포스트 내용을 분석하여 제안해주면 편리할 것이라 생각했습니다. GPT API 통합과 프롬프트 최적화를 통해 유용한 태그를 생성할 수 있었으며, 이 기능은 선택적으로 제공하여 API 키가 없어도 플러그인을 사용할 수 있도록 했습니다.
Cursor AI 활용과 한계
개발 과정에서 Cursor AI를 적극 활용했습니다. 반복적인 코드 작성, 정규표현식 패턴 생성, API 호출 로직 구현 등에서 AI의 도움을 받아 개발 속도를 크게 높일 수 있었습니다. 특히 Obsidian Plugin API나 GitHub API처럼 익숙하지 않은 API를 사용할 때, AI가 예제 코드를 제공하고 문서를 요약해주어 학습 곡선을 완화했습니다.
그러나 Cursor AI에 과도하게 의존하는 것의 한계도 명확히 느꼈습니다. AI가 생성한 코드는 때때로 버그가 있거나 최적화되지 않았으며, 특히 복잡한 비즈니스 로직이나 에러 처리는 AI가 완벽히 처리하지 못했습니다. 또한 AI가 제안한 구조를 그대로 따르면 나중에 유지보수가 어려워지는 경우도 있었습니다.
이를 통해 배운 교훈은 AI를 "도구"로 활용하되, 핵심 설계와 중요한 결정은 직접 해야 한다는 것입니다. 반복적인 코드 작성이나 보일러플레이트는 AI에게 맡기고, 아키텍처 설계, 에러 처리 전략, 성능 최적화는 직접 고민하고 구현했습니다. 이러한 균형 잡힌 접근이 품질 높은 코드를 빠르게 작성하는 비결이었습니다.
기술적 도전과 해결
비동기 파일 처리
Obsidian Plugin API의 파일 읽기와 쓰기는 모두 비동기입니다. 여러 이미지 파일을 순차적으로 읽고 처리하는 과정에서 비동기 흐름 제어가 복잡해졌습니다. 초기에는 콜백 지옥에 빠지기 쉬웠으나, async/await 패턴을 일관되게 사용하여 코드를 읽기 쉽게 만들었습니다.
Promise.all()을 활용하여 여러 이미지를 병렬로 읽어 처리 속도를 높였습니다. 그러나 너무 많은 파일을 동시에 처리하면 메모리 문제가 발생할 수 있어, 청크 단위(한 번에 5개씩)로 나누어 처리하는 방식을 채택했습니다. 이는 대용량 포스트에서도 안정적으로 작동하도록 보장했습니다.
에러 처리도 중요했습니다. 하나의 이미지 읽기가 실패해도 전체 프로세스가 중단되지 않도록 try-catch 블록으로 개별 작업을 감싸고, 실패한 파일 목록을 수집하여 최종적으로 사용자에게 보고했습니다. 이는 부분적 성공이라도 최대한 많은 파일을 처리할 수 있게 해주었습니다.
GitHub API 속도 제한
GitHub API는 시간당 요청 수 제한이 있습니다. Personal Access Token을 사용하면 시간당 5000 요청이 허용되지만, 이미지가 많은 포스트를 연속으로 업로드하면 제한에 도달할 수 있습니다. 이를 해결하기 위해 속도 제한 감지와 대기 로직을 구현했습니다.
API 응답 헤더의 X-RateLimit-Remaining을 확인하여 남은 요청 수를 추적합니다. 제한에 가까워지면 사용자에게 경고를 표시하고, 필요시 다음 시간 윈도우까지 대기합니다. 또한 요청을 배치로 묶어 한 번의 커밋에 여러 파일을 포함시켜 API 호출 수를 최소화했습니다.
지수 백오프 재시도 전략도 적용했습니다. 429 Too Many Requests 응답을 받으면 즉시 재시도하지 않고, 1초, 2초, 4초 식으로 대기 시간을 늘려가며 재시도합니다. 이는 일시적인 속도 제한 상황을 우아하게 처리하며, 서버에 불필요한 부담을 주지 않습니다.
문법 변환 엣지 케이스
마크다운 문법은 표준이 있지만 구현마다 미묘한 차이가 있습니다. Obsidian과 Jekyll의 문법 차이를 완벽히 변환하는 것은 예상보다 복잡했습니다. 특히 중첩된 링크, 이스케이프된 문자, 코드 블록 내 마크다운 문법 등 엣지 케이스가 많았습니다.
정규표현식만으로는 모든 케이스를 처리하기 어려워, 간단한 마크다운 파서를 구현했습니다. 마크다운을 토큰 스트림으로 변환하고, 각 토큰의 타입(링크, 이미지, 코드 블록 등)을 식별하여 컨텍스트에 맞게 변환합니다. 코드 블록 내부는 변환을 건너뛰고, 링크 텍스트 내부의 특수 문자는 이스케이프 처리합니다.
테스트 주도 개발(TDD) 방식으로 엣지 케이스를 하나씩 발견하고 수정했습니다. 실제 사용 중 변환이 잘못된 경우를 발견하면 해당 케이스를 테스트로 추가하고, 변환 로직을 개선하여 통과시켰습니다. 현재 약 50개의 테스트 케이스가 있으며, 변환 정확도는 95% 이상을 유지합니다.
성과 및 향후 계획
워크플로우 개선 성과
플러그인 도입 전과 후의 워크플로우를 비교하면 개선 효과가 명확합니다. 이전에는 포스트 하나를 업로드하는 데 5-10분이 걸렸습니다. Obsidian에서 Jekyll로 문법을 수동 변환하고, 이미지를 찾아서 복사하며, YAML frontmatter를 작성한 후, Git 명령어를 실행하는 일련의 과정이 필요했습니다.
플러그인 도입 후에는 "Export Post" 버튼 하나로 모든 과정이 1초 내에 완료됩니다. 시간 절약은 단순히 효율성 문제를 넘어 심리적 장벽을 낮춥니다. 블로그 포스팅이 번거롭지 않으니 더 자주 글을 쓰게 되었고, 짧은 아이디어나 메모도 부담 없이 블로그에 올릴 수 있게 되었습니다.
실수 감소 효과도 상당합니다. 이미지 파일을 빠뜨리는 일이 없어졌고, 문법 변환 오류로 Jekyll 빌드가 실패하는 경우가 거의 사라졌습니다. 커밋 메시지도 일관된 형식으로 자동 생성되어 Git 히스토리가 깔끔해졌습니다. 이러한 자동화는 블로그 유지보수를 더욱 즐겁게 만들었습니다.
기술적 성장
이 프로젝트는 여러 기술 영역에서 성장하는 기회였습니다. Obsidian Plugin API를 깊이 이해하게 되었으며, TypeScript로 복잡한 비동기 로직을 작성하는 능력이 향상되었습니다. GitHub API와 RESTful API 설계에 대한 실무 경험도 쌓았습니다. 특히 Personal Access Token 인증, 속도 제한 처리, 에러 복구 전략 등 API 통합의 실전 노하우를 습득했습니다.
정규표현식과 문자열 처리 기술도 크게 발전했습니다. 다양한 마크다운 문법 패턴을 감지하고 변환하는 과정에서 정규표현식의 고급 기능(lookahead, capture group, non-greedy matching)을 활용할 수 있게 되었습니다. 또한 정규표현식의 한계를 이해하고, 언제 파서 접근 방식으로 전환해야 하는지를 판단하는 능력도 생겼습니다.
워크플로우 자동화의 가치를 체감했습니다. 반복적인 수작업을 자동화하면 시간 절약뿐만 아니라 실수도 줄일 수 있고, 더 중요한 작업(글쓰기)에 집중할 수 있게 됩니다. 이러한 경험은 향후 다른 워크플로우에서도 자동화 기회를 찾아내고 구현하는 안목을 길러주었습니다.
향후 개선 계획
첫 번째로 다른 Jekyll 테마 지원을 확장하려 합니다. 현재는 Chirpy 테마에 최적화되어 있지만, 다른 인기 있는 테마들(Minimal Mistakes, Beautiful Jekyll 등)도 지원하면 더 많은 사용자가 플러그인을 활용할 수 있습니다. 테마별 설정 프로필을 제공하여 사용자가 자신의 테마를 선택하기만 하면 되도록 할 계획입니다.
두 번째로 다른 정적 사이트 생성기 지원을 고려하고 있습니다. Hugo, Gatsby, Eleventy 등도 마크다운 기반 블로그 플랫폼으로 인기가 있으며, Obsidian 사용자 중 이러한 플랫폼을 사용하는 사람들도 많습니다. 플러그인 아키텍처를 모듈화하여 각 플랫폼별 어댑터를 추가하는 방식으로 확장할 수 있습니다.
세 번째로 이미지 자동 최적화 기능을 추가하려 합니다. 고해상도 이미지를 그대로 업로드하면 블로그 로딩 속도가 느려질 수 있으므로, 업로드 전에 자동으로 리사이징하고 압축하는 기능이 유용할 것입니다. Sharp 같은 이미지 처리 라이브러리를 통합하여 적절한 크기와 품질로 최적화할 계획입니다.
네 번째로 포스트 예약 발행 기능을 구현하고 싶습니다. Jekyll은 frontmatter의 date 필드가 미래 날짜면 포스트를 숨기는 기능이 있습니다. 플러그인에서 발행 날짜를 설정할 수 있게 하고, 로컬에서는 계속 작업하되 지정된 날짜에 블로그에 나타나도록 스케줄링하는 기능을 제공할 것입니다.
다섯 번째로 Obsidian Community Plugin으로 등록하려 합니다. 현재는 수동 설치가 필요하지만, Community Plugin으로 등록되면 Obsidian 내에서 직접 검색하고 설치할 수 있어 접근성이 크게 향상됩니다. 플러그인 코드를 오픈소스로 공개하고, 커뮤니티 피드백을 받아 지속적으로 개선할 계획입니다.
프로젝트 링크
- GitHub 저장소: https://github.com/qlsjtmek2/jekyll-chirpy-git-exporter
- 개발 과정 블로그: Obsidian과 Git Blog 연동하기
라이선스
GPL-3.0 License