12 KiB
뇌사료 ↔ 옵시디언 플러그인 연동 구현 계획
작성일: 2026-04-16
작업 경로:c:\project\my_util\memo_server
서버 주소:your-server-ip:5093
원격 경로:/home/your-username/Script/memo_server
1. 제품 컨셉 (확정)
뇌사료 단독: 웹 기반 빠른 캡처 메모 + AI 태깅/요약
뇌사료 + 옵시디언: 뇌사료(INPUT/캡처) → 옵시디언(PROCESS/정리/아카이브/그래프)
포지셔닝 핵심:
- 뇌사료는 옵시디언의 "웹 프론트엔드 + AI 레이어" 역할
- 옵시디언 사용자의 고질적 불편(로컬 파일 기반 → 모바일/웹 접근 어려움)을 해결
- 뇌사료 → 옵시디언은 단방향 동기화 (뇌사료가 Single Source of Truth)
- 옵시디언은 읽기/정리 전용 뷰로 사용
2. 현재 아키텍처 현황
2-1. 서버 구조 (Flask + Blueprint)
app/
├── __init__.py # create_app(), Blueprint 등록, 보안 미들웨어
├── constants.py # GROUP_DEFAULT="default", GROUP_FILES="files", GROUP_DONE="done"
├── database.py # SQLite, DB_PATH=data/memos.db
├── auth.py # @login_required 데코레이터 (세션 기반)
├── security.py # Fernet 암호화/복호화 (PBKDF2 + ENCRYPTION_SEED)
├── ai.py # Gemini AI 태깅/요약
└── routes/
├── __init__.py # register_blueprints(app) ← 여기에 새 Blueprint 추가
├── memo.py # /api/memos CRUD
├── file.py # /api/files 업로드/다운로드
├── ai.py # /api/ai
├── auth.py # /login /logout
├── settings.py # /api/settings
└── main.py # / 메인 페이지
2-2. DB 스키마 (SQLite: data/memos.db)
memos(
id, title, content, summary, color,
is_pinned, status, -- status: 'active' | 'done'
group_name, -- 'default' | 'files' | 'done' | 사용자 정의
is_encrypted, -- 0 | 1
created_at, updated_at
)
tags(id, memo_id, name, source) -- source: 'user' | 'ai'
attachments(id, memo_id, filename, original_name, file_type, size, created_at)
memo_links(id, source_id, target_id) -- 백링크/전방링크
2-3. 현재 인증 방식
- 세션 기반 (
session['logged_in']) — 브라우저 전용 - API Token 인증 없음 → Phase 1에서 추가 필요
2-4. 암호화 방식
# app/security.py
# PBKDF2(password + ENCRYPTION_SEED) → Fernet 키 → 본문 암호화
# .env의 ENCRYPTION_SEED 필수
encrypt_content(content, password)
decrypt_content(encrypted_data, password) # 실패 시 None 반환
암호화 메모는 is_encrypted=1, 복호화는 /api/memos/{id}/decrypt POST로 처리.
3. 구현 계획 (3단계)
Phase 1. API Token 인증 추가 (뇌사료 서버)
목적: 세션 없이 외부에서 API를 호출할 수 있도록 Token 인증 추가
원칙: 기존 @login_required 데코레이터를 건드리지 않고 새 데코레이터 추가
1-1. .env에 토큰 추가
# .env (기존 항목 유지, 아래 추가)
OBSIDIAN_API_TOKEN=your_secret_token_here
1-2. app/auth.py에 토큰 인증 데코레이터 추가
def token_required(view):
"""API Token 기반 인증 데코레이터 (Obsidian 플러그인 전용)"""
@functools.wraps(view)
def wrapped_view(**kwargs):
token = request.headers.get('X-API-Token') or request.args.get('token')
expected = os.getenv('OBSIDIAN_API_TOKEN', '')
if not expected or token != expected:
return jsonify({'error': 'Unauthorized'}), 401
return view(**kwargs)
return wrapped_view
1-3. 새 Blueprint 파일 생성: app/routes/sync.py
# app/routes/sync.py
# 옵시디언 동기화 전용 Blueprint
sync_bp = Blueprint('sync', __name__)
# GET /api/sync/export
# 전체 메모 목록 반환 (암호화 메모는 플레이스홀더 처리)
# 쿼리파라미터: since=2024-01-01T00:00:00 (증분 동기화용)
# POST /api/sync/decrypt/<id>
# 단일 암호화 메모 복호화 반환
# 헤더: X-Memo-Password: 비밀번호
# GET /api/sync/groups
# 그룹 목록 반환
1-4. app/routes/__init__.py에 Blueprint 등록
from .sync import sync_bp
app.register_blueprint(sync_bp)
완료 기준: curl -H "X-API-Token: xxx" http://서버/api/sync/export 가 JSON 반환
Phase 2. Python 동기화 스크립트 (로컬 실행)
목적: 뇌사료 API를 호출해서 옵시디언 Vault에 .md 파일로 저장
실행 방식: Windows Task Scheduler 또는 cron으로 주기 실행 (5~10분)
위치: 프로젝트 루트의 obsidian_sync/ 폴더
2-1. 파일 구조
obsidian_sync/
├── obsidian_sync.py # 메인 동기화 스크립트
├── config.json # 설정 파일
└── last_sync.txt # 마지막 동기화 시각 저장 (증분 동기화용)
2-2. obsidian_sync/config.json
{
"server_url": "http://your-server-ip:5093",
"api_token": "your_secret_token_here",
"vault_path": "C:/Users/your-username/Documents/ObsidianVault/뇌사료",
"sync_interval_minutes": 10,
"encrypted_memo_handling": "placeholder",
"frontmatter": true,
"group_to_folder": {
"default": "inbox",
"files": "files",
"done": "archive"
}
}
2-3. 변환 규칙 (뇌사료 → .md)
| 뇌사료 필드 | 옵시디언 변환 |
|---|---|
title |
파일명 + H1 헤더 |
content |
본문 (HTML → Markdown 변환) |
tags |
frontmatter tags: + 본문 #태그 |
group_name |
하위 폴더 분류 |
backlinks |
본문 하단 [[링크제목]] |
is_encrypted=1 |
[🔒 암호화된 메모 — 뇌사료에서 확인] |
created_at |
frontmatter date: |
updated_at |
frontmatter updated: |
summary |
frontmatter summary: |
2-4. 생성될 .md 예시
---
id: 42
date: 2026-04-15
updated: 2026-04-16
tags: [python, flask, ai]
source: 뇌사료
group: default
---
# 메모 제목
본문 내용...
---
**Tags:** #python #flask #ai
**Links:** [[관련 메모 제목]]
**Source:** [뇌사료에서 열기](http://your-server-ip:5093)
완료 기준: 스크립트 실행 후 Vault 폴더에 .md 파일 생성 확인
Phase 3. TypeScript 옵시디언 플러그인 (선택적 고도화)
목적: Phase 2가 안정화된 후, 옵시디언 내에서 직접 UI 제공
전제조건: Phase 1, 2 완료 후 진행
개발 언어: TypeScript (옵시디언 플러그인 필수)
3-1. 플러그인 저장소 구조
obsidian-brainsryo-plugin/ # 별도 Git 저장소 권장
├── src/
│ ├── main.ts # 플러그인 진입점
│ ├── api.ts # 뇌사료 API 클라이언트
│ ├── converter.ts # 뇌사료 JSON → Obsidian .md 변환
│ ├── settings.ts # 플러그인 설정 UI
│ └── modal.ts # 암호화 메모 비밀번호 입력 모달
├── manifest.json
├── package.json
└── tsconfig.json
3-2. 플러그인 설정 항목 (UI)
뇌사료 서버 URL: [http://your-server-ip:5093]
API Token: [***************]
저장 폴더: [뇌사료/]
동기화 주기: [10분]
암호화 메모 처리: [플레이스홀더 ▼] ← 또는 "비밀번호 입력"
자동 동기화: [ON/OFF]
3-3. 암호화 메모 처리 흐름 (플러그인 버전)
옵시디언에서 암호화 메모 클릭
→ 비밀번호 입력 모달 표시 (Obsidian Modal API)
→ POST /api/sync/decrypt/{id} (헤더: X-Memo-Password)
→ 복호화 성공 시 임시 .md 생성 후 표시
→ 닫으면 임시 파일 삭제 (디스크에 평문 저장 안 함)
3-4. 개발 우선순위
- 단방향 동기화 (뇌사료 → 옵시디언) 기본 버전
- 설정 UI
- 증분 동기화 (마지막 동기화 이후 변경분만)
- 암호화 메모 지원 (비밀번호 모달)
- (미래) 옵시디언 → 뇌사료 import 기능
4. 구현 순서 체크리스트
Phase 1 — API Token (뇌사료 서버 수정)
.env에OBSIDIAN_API_TOKEN추가.env.example에도 항목 추가 (값 없이)app/auth.py에token_required데코레이터 추가app/routes/sync.py파일 생성GET /api/sync/export엔드포인트 (since파라미터 지원)POST /api/sync/decrypt/<id>엔드포인트GET /api/sync/groups엔드포인트
app/routes/__init__.py에sync_bp등록- 서버 배포 (
python deploy.py— 사용자 승인 필수)
Phase 2 — Python 동기화 스크립트
obsidian_sync/폴더 생성obsidian_sync/config.json작성obsidian_sync/obsidian_sync.py작성- API 호출 (Token 인증)
- HTML → Markdown 변환 (
markdownify라이브러리) - frontmatter 생성
- 태그/백링크 변환
- 그룹 → 폴더 분류
- 증분 동기화 (
last_sync.txt) - 암호화 메모 플레이스홀더 처리
- 스크립트 테스트 (실제 Vault에 파일 생성 확인)
- Windows Task Scheduler 등록
Phase 3 — TypeScript 옵시디언 플러그인 (나중에)
- Node.js 개발 환경 세팅
- obsidian-sample-plugin 템플릿 클론
- API 클라이언트 구현
- 설정 UI 구현
- 동기화 로직 구현
- 암호화 메모 모달 구현
- 로컬 설치 테스트 (
.obsidian/plugins/복사)
5. API 스펙 (Phase 1에서 구현할 엔드포인트)
GET /api/sync/export
Headers: X-API-Token: {OBSIDIAN_API_TOKEN}
Params:
- since (optional): ISO 8601 datetime, 이 시각 이후 수정된 메모만 반환
- limit (optional): 기본 1000
Response:
[
{
"id": 42,
"title": "메모 제목",
"content": "<p>HTML 본문</p>",
"summary": "AI 요약",
"tags": [{"name": "python", "source": "ai"}, ...],
"group_name": "default",
"is_encrypted": false,
"is_pinned": false,
"backlinks": [{"source_id": 10, "title": "다른 메모"}],
"links": [{"target_id": 20, "title": "링크된 메모"}],
"created_at": "2026-04-15T10:00:00",
"updated_at": "2026-04-16T08:00:00"
},
// is_encrypted=true인 경우:
{
"id": 99,
"title": "암호화된 메모",
"content": null,
"is_encrypted": true,
...
}
]
POST /api/sync/decrypt/<id>
Headers:
X-API-Token: {OBSIDIAN_API_TOKEN}
X-Memo-Password: {메모_비밀번호}
Response (성공): {"content": "복호화된 본문"}
Response (실패): {"error": "Invalid password"}, 403
GET /api/sync/groups
Headers: X-API-Token: {OBSIDIAN_API_TOKEN}
Response: {"groups": ["default", "files", "done", "custom_group1", ...]}
6. 주의사항 및 설계 원칙
단방향 원칙: 뇌사료 → 옵시디언 방향만 동기화. 옵시디언에서 .md를 수정해도 뇌사료 DB에는 반영되지 않음. 양방향 동기화는 충돌 처리 복잡도가 높아 Phase 3 이후 별도 검토.
암호화 메모 보안: 복호화된 본문은 절대 디스크에 저장하지 않음. Phase 2 스크립트는 기본적으로
encrypted_memo_handling: "placeholder"유지.
파일명 규칙: 옵시디언 파일명 특수문자 금지 (
/ \ : * ? " < > |) 뇌사료 제목의 해당 문자는_로 치환. 중복 시제목_id42.md형식.
7. 작업 이어받기 안내 (다른 AI 인스턴스용)
-
먼저 읽을 파일들:
app/auth.py— 현재 인증 구조 확인app/routes/__init__.py— Blueprint 등록 방식 확인app/routes/memo.py— 기존 API 패턴 참고app/security.py— 암호화/복호화 함수 확인.env—OBSIDIAN_API_TOKEN추가 여부 확인
-
시작점: Phase 1 체크리스트 항목 순서대로 진행
-
배포 방법: 수정 완료 후
python deploy.py실행 (반드시 사용자 승인 후 실행 — 사용자 규칙) -
테스트:
curl -H "X-API-Token: {토큰}" http://your-server-ip:5093/api/sync/export -
추가 의존성:
pip install markdownify(Phase 2 스크립트에서 필요)