Skip to content
게으른 엔지니어의 기술 블로그
Go back

Obsidian LiveSync 쓸 때 조심해야 할 것 — 노트가 조용히 사라진 사연

Obsidian을 켤 때마다 이상한 메시지가 뜨기 시작했다.

DELETE DATABASE did not delete Temporary/2025년 여행.md;
keeping last-seen record to avoid resurrecting the file

한두 개가 아니라 여러 개였다. LLM Wiki.md, 2025년 여행.md, First Principles Thinking.md… 그리고 실제로 Obsidian에서 그 노트들이 사라져 있었다. 내가 지운 적 없는 노트들이었다.

왜 하필 이 플러그인이었나

원래는 Dropbox에 vault를 두고 동기화하는 플러그인을 쓰고 있었는데, 충돌이 잦았다. 기기 두 개에서 거의 동시에 저장하면 같은 노트가 두 벌로 갈라지거나, 어느 한쪽 내용이 그냥 사라지는 일이 종종 있었다. 그래서 좀 더 안정적인 동기화 메커니즘을 가진 쪽을 찾다가 CouchDB 기반의 Obsidian LiveSync로 옮겼다.

Obsidian 공식 Sync를 안 쓴 이유는, 나 혼자 쓰는 거면 상관없는데 주변 사람들도 같이 쓰게 해야 해서였다. 마침 VPS도 있겠다, 셀프 호스팅으로 직접 CouchDB를 띄워서 여러 사람이 계정을 나눠 쓸 수 있게 만들었다. “더 안정적인” 걸 찾아서 옮긴 셈인데, 이번 일을 겪고 보니 여기도 여기 나름의 삽질이 필요했다는 걸 알게 됐다.

서버부터 확인했다

이 vault는 CouchDB로 셀프 호스팅한 Obsidian LiveSync로 여러 기기(PC, 아이폰)에 동기화해서 쓰고 있다. 다행히 이 서버는 내가 직접 관리하고 있어서, CouchDB에 바로 들어가서 확인할 수 있었다.

curl -s -u "$USER:$PASS" "http://127.0.0.1:5984/my_sync_vault" | python3 -m json.tool
# "doc_del_count": 0

doc_del_count가 0이었다. 즉 서버에는 지금까지 실제로 삭제된 문서가 단 하나도 없었다. 데이터 자체는 안전하다는 뜻이라 일단 마음이 놓였는데, 그럼 저 “DELETE DATABASE did not delete”라는 메시지는 대체 뭘 하려다가 실패한 걸까.

메시지의 정체

플러그인 소스코드(vrtmrz/livesync-commonlib)를 직접 뒤져서 찾았다. 이 메시지는 LiveSync의 Offline Scanner라는 기능에서 나온다. Obsidian을 켤 때마다(끄고 켜는 거의 모든 순간) 로컬 vault 파일과 로컬 동기화 DB를 자동으로 비교하는데, 어떤 파일이 “DB에는 있는데 로컬 디스크에는 없는” 상태고 + 예전에 기록해둔 “마지막으로 본 시각”이 DB 쪽보다 같거나 최신이면, “사용자가 Obsidian 밖에서 직접 지운 거구나”라고 추측해서 DB에서도 지우려고 시도한다.

문제는 이 추측이 늘 맞지는 않는다는 거다. 파일이 로컬에서 “일시적으로” 안 보인 것뿐이어도 똑같이 삭제로 오판할 수 있다. 그리고 삭제 시도가 소스코드 상 두 단계로 이뤄지는데, ①”삭제됨” 표시를 문서에 남기는 것(이건 잘 성공하고, 다른 기기로도 정상 전파된다)과 ②그 표시를 완전히 확정 짓는 마지막 처리(코드 안에서는 “Commit File Deletion”이라고 부른다)인데, 지금 내가 쓰는 버전에서는 이 마지막 단계 기능 자체가 UI에서 비활성화돼 있었다. 그러니 매번 켤 때마다 “지우려 했는데 못 지웠다”는 이 메시지가 반복해서 뜨는 거였다.

역설적으로 이 실패 덕분에 데이터는 안전했다 — 완전히 삭제되지 않고 리비전 히스토리에 남아있었으니까. 최근 버전(0.25.79, 릴리스 노트 확인) 변경 로그에도 정확히 이 얘기가 있었다.

Fixed an issue where a file deleted from storage could be resurrected by the offline scanner because the database tombstone was not written when the storage file was already gone.

예전엔 실패한 삭제를 성공한 것처럼 처리해서 파일이 유령처럼 부활하는 버그가 있었는데, 그걸 고치면서 지금은 “삭제 실패 시 계속 재시도하며 알림”으로 바뀐 것.

복구는 됐는데, 일괄로는 안 됐다

플러그인 자체에 있는 “Reset Synchronisation on This Device” (설정 → Maintenance) 기능으로 전체를 한 번에 복구해보려 했는데 안 됐다. 이 기능은 “이미 삭제됨으로 표시된 파일”은 의도적으로 건드리지 않도록 설계돼 있어서, 지금 같은 상황엔 그냥 건너뛴다.

결국 통한 방법은 파일 하나하나를 명령 팔레트(Ctrl/Cmd+P)의 **“Pick a file to show history”**로 찾아서, 삭제 표시가 붙기 전의 리비전을 골라 복원하는 거였다. 번거롭지만 확실했다.

가장 유력한 원인 — Obsidian이 켜진 채로 탐색기에서 드래그 앤 드롭

여기까지는 “왜 복구가 안 됐는지”였고, 진짜 궁금한 건 “애초에 왜 로컬에서 파일이 순간 사라졌는가”였다. iCloud 저장 공간 최적화(자주 안 쓰는 파일이 자동으로 클라우드 전용으로 바뀌는 기능)를 먼저 의심했는데, 아니었다.

로그가 남아있는 게 아니라서 100% 확정은 못 하지만, 기억을 더듬어보니 문제가 생긴 시점에 탐색기(파일 뷰어)에서 파일을 Obsidian 파일 뷰로 드래그 앤 드롭한 적이 있었다. 가장 유력한 원인으로 보이는 게 이거다. Obsidian이 켜진 상태로, 특히 막 켜서 자동 스캔이 도는 바로 그 타이밍에 파일을 밖에서 밀어 넣으면, 스캐너가 “이 파일이 방금 로컬에 없었다가 생겼네”를 “삭제됐다가 다시 생겼다(resurrect)“로 오해할 여지가 생긴다. Obsidian 자체의 파일 조작(새 노트 생성, 이름 변경 등)과 OS 탐색기의 파일 조작은 서로 다른 경로라, 이런 타이밍 문제가 생길 수 있는 거다.

그냥 확 다시 만들기로 했다

파일별로 하나씩 히스토리에서 복원하는 건 확실하긴 한데, 언제 또 이 스캐너가 다른 파일을 오판할지 모른다는 찜찜함이 남았다. 그래서 필요한 노트는 전부 복원해둔 다음, 서버 쪽 DB를 통째로 지우고 지금 로컬(가장 완전한 상태인 PC)을 기준으로 새로 만들기로 했다.

curl -X DELETE ".../my_sync_vault"
curl -X PUT ".../my_sync_vault"
curl -X PUT ".../my_sync_vault/_security" -d '{...동일한 권한 설정...}'

접속 정보(URI/계정/DB 이름)는 그대로 두고 내용물만 비웠다 — 이러면 다른 기기들은 설정을 다시 할 필요 없이 그대로 연결돼 있다가, 다음 동기화 때 새로 채워진 서버 내용을 받게 된다.

여기서 작은 삽질이 하나 더 있었다. PC 정리가 끝나기 전에 다른 기기가 먼저 열려서 빈 서버랑 동기화되면 꼬일 것 같아서, _security의 members를 빈 배열로 바꿔서 “막았다”고 생각했는데 — 착각이었다. CouchDB는 admins/members가 둘 다 비어있으면 오히려 “제한 없음”으로 동작한다. 그러니까 나는 막은 게 아니라 반대로 더 열어둔 셈이었다. 다행히 그사이 별일은 없었지만, 접근을 진짜로 막고 싶으면 _security를 만지작거릴 게 아니라 CouchDB 자체를 잠깐 멈추는 게 맞는 방법이었다.

아이폰에서 vault를 지웠는데, 왜 또 똑같은 메시지가

PC는 문제없이 정리됐다. 아이폰은 새로 연결하기 전에 기존 vault를 통째로 지웠다 — 그런데 다시 연결하니까 처음이랑 똑같은 “DELETE DATABASE did not delete …md” 메시지가 이번엔 파일 수십 개에 걸쳐 한꺼번에 쏟아졌다. 그리고 결정적으로, 데이터가 하나도 안 내려왔다.

곰곰이 생각해보니 이유가 명확했다. “vault를 지운다”는 건 vault 폴더 안의 파일들을 지운 거지, 플러그인 자체의 로컬 캐시 DB까지 지운 게 아니었다. 이 캐시는 vault 파일과 별개로 저장되는데, 여기에 “이 파일들을 예전에 봤었다”는 기록이 그대로 남아 있었던 거다. 그러니 새로 연결한 아이폰이 서버에서 파일을 받아와야 하는 상황에서도, 스캐너 입장에서는 “이 파일 전에 봤었는데 지금 로컬(방금 지운 vault)엔 없네 → 지운 거네”로 또 오판해서, 받아오는 대신 지우려고 시도한 거였다. 처음 사건이랑 원인은 같은데, 이번엔 대상이 훨씬 많아서 증상이 더 크게 터진 셈이다.

해결책은 설정 → **Maintenance → “Delete local database to reset or uninstall Self-hosted LiveSync”**였다. 이게 파일이 아니라 플러그인의 로컬 캐시 자체를 완전히 지우는 기능이다. 이걸 먼저 실행하고 나서 PC에서 “Copy setup URI”로 다시 연결하니, 이번엔 진짜 빈 기억 상태로 처음부터 정상적으로 받아왔다.

그 다음은 진짜 정상적인 충돌

아이폰이 다 받아온 다음, 이번엔 “Some files have been left conflicted!”라는 알림이 떴다. 근데 이건 완전히 다른 종류였다 — 지금까지 겪은 “조용히 사라지는” 버그가 아니라, Obsidian이 정상적으로 두 버전 중 뭘 쓸지 물어보는 기능이었다. PC에서 만든 파일을 Obsidian 내부에서 다른 폴더로 옮긴 시점이 마침 아이폰이 새로 받아오던 타이밍이랑 겹쳐서, 같은 문서의 리비전이 두 갈래로 갈린 것 같았다.

명령 팔레트의 “Pick a file to resolve conflict”를 실행하면 좌우 비교 화면이 뜨고, 아래에 “Use 로컬”/“Use 원격”/“Concat both” 버튼이 있다. 이건 데이터가 안 날아가는 안전한 절차라, PC 쪽(원격)을 기준으로 고르면 끝이었다.

앞으로 조심할 것

이번 일로 정리된 원칙은 세 가지다.

  1. 탐색기에서 파일을 vault에 넣을 땐 Obsidian을 끄고 한다. 볼트 폴더에 직접 복사해 넣은 다음 Obsidian을 켜면, 스캔이 도는 시점과 파일이 나타나는 시점이 겹칠 일이 없다. 드래그 앤 드롭 자체가 문제가 아니라, Obsidian이 켜진 채로 하는 게 문제다.
  2. vault를 iCloud/구글드라이브/OneDrive 같은 클라우드 동기화 폴더 안에 두지 않는다. 이번 사건의 직접 원인은 아니었지만, 같은 부류의 문제(파일이 로컬에서 일시적으로 사라진 것처럼 보이는 것)를 일으킬 수 있는 별도의 위험 요인이다.
  3. vault를 초기화/재설정할 땐 파일만 지우지 말고 플러그인 로컬 DB도 같이 초기화한다. 설정 → Maintenance → “Delete local database to reset or uninstall Self-hosted LiveSync”를 같이 실행해야, 예전 기록이 남아서 똑같은 문제가 재발하는 걸 막을 수 있다.

동기화 플러그인을 믿고 파일을 막 넣고 빼다 보면 한 번쯤 이런 걸 겪을 수 있다. 다행히 이번엔 플러그인 자체의 안전장치(삭제 실패 시 기록 보존) 덕분에 데이터가 진짜로 날아가진 않았지만, 그 안전장치가 없었다면 꽤 아찔했을 사건이다.


Share this post:

Previous Post
뉴스 요약에 날짜가 안 뜨던 이유 — AI 에이전트가 리팩터링하며 조용히 빠뜨린 기능
Next Post
tmux Ctrl-b가 갑자기 안 먹혔던 이유 — 설정 파일과 살아있는 서버는 다르다