지금까지 모든 Ripplic 파이프라인은 로컬 폴더에서 시작했습니다. FSEvents가 폴더를 감시하고, 모든 변경 사항을 선택한 대상으로 단방향 복제합니다. 다음 업데이트에서 이 시작점이 클라우드로 이동합니다 — Google Drive가 소스가 될 수 있습니다.
Google Drive ──▶ Cloudflare R2
──▶ Amazon S3 / MinIO / B2 / GCS (모든 S3 호환 스토리지)
──▶ 다른 Google Drive
파일은 Drive에서 대상으로 직접 흐릅니다. 중간에 서버는 없습니다 — 당신의 Mac이 유일한 통로이며, 자격 증명은 macOS 키체인을 절대 벗어나지 않습니다. 이것은 Ripplic이 첫날부터 지켜온 약속이고, 클라우드 소스도 이를 굽히지 않습니다.
세 번째 파이프라인이 Google Drive 폴더에서 읽어 AWS us-east-1의 S3 버킷으로 복제하는 중입니다(15.5 MB/s). 각 파이프라인은 독립적이며, 느린 대상이 다른 파이프라인을 막는 일은 없습니다.
서버 없이 변경을 감지하기
로컬 폴더는 이벤트를 앱에 푸시할 수 있지만 클라우드는 그럴 수 없습니다. Google의 푸시 알림에는 공개 HTTPS 엔드포인트, 즉 서버가 필요하기 때문입니다. Ripplic은 서버를 두지 않으므로 Drive가 제공하는 다른 방법을 사용합니다: changes.list 델타 피드입니다.
Google 서버는 당신의 Drive에서 일어난 모든 변경의 저널을 유지합니다. Ripplic은 그 저널의 커서를 쥐고 30초마다 "여기 이후로 무슨 일이 있었나?"라고 묻습니다. 답은 변경 목록과 새 커서입니다. 커서는 디스크에 저장되므로 앱을 재시작해도 피드는 정확히 멈춘 지점부터 재개됩니다 — 재스캔이 필요 없습니다.
이 설계를 확장 가능하게 만드는 성질: 조용한 폴링 한 번의 비용은 Drive의 크기와 완전히 무관합니다. 폴더에 파일이 백 개든 십만 개든, 작은 HTTPS 요청 하나. 변경이 있을 때의 비용은 변경 수에 비례하며, 트리 전체 크기에 비례하는 일은 없습니다.
이벤트는 사실이 아니라 힌트
Ripplic 엔진은 모든 변경 이벤트를 힌트로 취급합니다. 확인할 가치는 있지만, 맹목적으로 행동할 근거는 아닙니다. 힌트된 경로는 Drive의 실제 메타데이터와 로컬 인덱스에 대조된 후에야 전송이 시작됩니다. 이벤트 유실, 중복, 순서 뒤바뀜 — 모두 무해합니다. 주기적인 전체 비교가 모든 것을 뒷받침하므로, 정확성이 개별 이벤트를 놓치지 않는 것에 의존하지 않습니다.
까다로운 경우에도 깔끔한 답이 나옵니다. Drive에서 파일 이름을 바꾸면 이전 경로(삭제 정책에 따라 처리 — 기본은 플래그만 표시하며, 당신의 허락 없이는 아무것도 삭제되지 않습니다)와 새 경로가 모두 힌트됩니다. 폴더 이름 변경은 하위의 모든 경로를 바꾸므로 엔진은 트리 전체 재스캔으로 물러섭니다 — 오류가 아니라 우아한 성능 저하입니다.
Mac이 일주일 동안 오프라인이었다면?
Ripplic이 꺼져 있거나 Mac이 잠들어 있는 동안 Drive에서 수백 개의 파일이 바뀌었다고 해봅시다. 따라잡기는 히스토리를 재생하는 방식이 아닙니다. 파이프라인은 시작할 때마다 먼저 전체 비교를 수행합니다: 각 파일의 체크섬과 크기를 로컬 인덱스와 대조합니다. 실제로 다른 것만 전송되며, 자리를 비운 사이 백 번 수정된 파일도 업로드는 딱 한 번 — 최종 버전만입니다.
그다음 델타 피드가 저장된 커서에서 재개되고, Google이 그동안의 모든 변경을 재생합니다. 대부분은 이미 비교 단계에서 처리됐지만 괜찮습니다 — 중복 힌트는 애초에 무해하게 설계되어 있으니까요. 중단이 너무 길어 커서 자체가 만료됐다면, 소스는 새 커서를 받아 전체 비교를 한 번 더 요청할 뿐입니다. 오류가 아니라 우아한 성능 저하입니다.
이를 중단에 강하게 만드는 세부 사항 하나: 커서는 해당 배치의 처리가 완료된 후에만 저장됩니다. 따라잡기 도중에 또 끊겨도, 다음 시작은 마지막으로 완료된 위치에서 재개됩니다. 놓치는 일은 결코 없습니다.
드리프트는 어떻게 처리하나요?
상태가 어긋나는 또 다른 경로가 있습니다. 누군가 Ripplic을 거치지 않고 대상을 직접 건드린 경우 — 예를 들어 S3 콘솔에서 오브젝트 몇 개를 지웠다면. 일상적인 대조는 소스와 로컬 인덱스를 비교하며, 의도적으로 매번 대상까지 감사하지는 않습니다. 그래서 이런 드리프트는 평소에는 보이지 않습니다. 이를 위해 Ripplic에는 별도의 감사 경로가 있습니다: 파이프라인 메뉴의 "Verify Destination Now", 그리고 파이프라인을 다시 활성화할 때의 자동 감사입니다.
감사가 하는 일은 단순합니다. 인덱스가 "동기화됨"이라 믿는 파일마다 대상에 오브젝트가 여전히 존재하고 크기가 일치하는지 확인합니다. 없거나 어긋난 항목은 동기화 상태가 지워지고 — 다음 패스에서 손상된 파일만 정확히 다시 업로드됩니다. 멀쩡한 파일은 하나도 더 보내지 않습니다. 복구는 언제나 소스에서 바깥으로 흐르며, 대상의 상태가 소스로 되돌려 써지는 일은 없습니다. 단방향은 철칙이고, 드리프트 복구도 예외가 아닙니다.
현재 감사는 존재와 크기를 검증합니다. 다음 단계로, Ripplic이 이미 보관하는 블록 단위 해시를 활용해 내용을 직접 비교하는 것과 예약 감사(유휴 시간에 매일/매주 자동 실행)를 진행하고 있습니다. 목표는 같습니다: 아무도 대상을 건드리지 않았다고 "믿을" 필요 없이, 클릭 한 번으로 "증명"할 수 있어야 합니다.
변경되지 않은 파일은 1바이트도 다운로드하지 않습니다
Drive는 파일마다 MD5 체크섬과 크기를 메타데이터로 알려줍니다. Ripplic은 이를 인덱스와 비교해 무엇이 변했는지 판단합니다 — "알고 보니 동일했다"를 확인하려고 내용을 다운로드하는 일은 없습니다. 실제로 파일을 옮길 때는 청크 단위로 스트리밍하므로, 20 GB 영상과 2 KB 메모의 메모리 사용량이 똑같이 평탄합니다.
읽기 전용은 설계입니다
Drive를 대상으로 연결할 때 요청하는 권한은 drive.file — Ripplic은 자신이 만든 파일만 만질 수 있습니다. 소스는 반대의 문제입니다. 기존 파일을 볼 수 있어야 하므로 drive.readonly를 요청합니다. 핵심은 "읽기 전용": Ripplic의 소스에 쓰기가 일어나는 일은 없습니다. 단방향은 단방향입니다. 소스가 책상 위 폴더든 클라우드의 Drive든.
솔직한 제한 하나: Google 문서·스프레드시트·프레젠테이션에는 복제할 파일 바이트가 없습니다 — 클라우드 문서로만 존재합니다. Ripplic은 근사치를 내보내는 대신 건너뛰는 쪽을 택했습니다.
출시 시기
소스로서의 Google Drive는 현재 테스트 중이며 다음 업데이트에서 제공됩니다. 기존 소스(로컬 폴더, 외장 드라이브)와 대상은 그대로입니다. Dropbox와 OneDrive도 같은 청사진을 따릅니다 — 엔진은 애초에 "델타 피드"를 모국어로 자랐으니까요.