site-icon Hong Cheung
Jun 8, 2025

마침내 Appwrite와 RxDB에서 오프라인 동기화를 작동시키다

서론

어렵지는 않았지만, 그렇다고 해서 바로 쓸 수 있는 건 아니었다. 기반을 제대로 돌아가게 만드는 데 몇 주가 걸렸는데, 가장 큰 어려움은 예제와 튜토리얼이 부족하다는 점이었다. Powersync + Supabase와 비교하거나 그냥 Firebase를 쓰는 것과 비교하면, 이건 확실히 어느 정도 설정이 필요하다.

구현

Appwrite + RxDB를 구현하는 방법에 대한 git 예제가 포함된 공식 블로그 글이 있었다. 하지만 내가 직접 해보니 다소 버그가 있었고, 실시간 동기화도 되지 않았다. 그래서 결국 Appwrite와 RxDB 문서를 모두 읽으면서 처음부터 직접 만들게 되었다.

주요 난관

RxDB와 함께하는 상태 관리 처리

나는 LegendState를 사용하는데, rxdb 컬렉션의 인스턴스에 .set()을 시도하는 경우가 있는데, 이는 내 뷰에서 루프를 일으킨다. 그래서 rxdb 컬렉션에는 .get()을 사용하고, LegendState로 업데이트를 읽고 모니터링해야 한다. 반면, 내 viewModel에는 rxdb를 직접 조작하는 모든 함수가 있다. Appwrite 복제본 수정은 viewModel 함수에서 일어난다.

실시간 동기화가 뷰를 루프로 업데이트하는 문제

앞의 점과 관련이 있다. 나는 LegendState에 익숙하지 않았지만, 지금은 점점 나아지고 있다. 이 문제를 해결하기 위해 디바운스를 추가했다.

동기화 지연이 로컬 상태를 망가뜨리는 문제

RxDB Appwrite 복제는 한 번에 하나의 쓰기만 수행할 수 있기 때문에, 짧은 시간에 여러 작업을 수행할 때마다 로컬 상태에 경쟁 문제가 생겼다. 이는 컬렉션을 실시간으로 구독하고 있는데도 로컬에서 변경되고 수신된 데이터가 동기화되지 않기 때문이다. 그래서 충돌을 처리하기 위해 updatedAt을 만들었다.

삭제된 필드

복제를 위해 deleted 필드를 설정할 수 있다. 삭제는 .remove()를 사용하여 수행하라고 안내되어 있다. 하지만 이 삭제 메서드는 “삭제 취소”가 불가능하다는 결과를 낳는다. RxDB는 .remove() 메서드를 통해 소프트 삭제된 것은 무엇이든 숨기기 때문에, 클라이언트는 !deleted를 확인하는 것만으로는 해당 항목에 접근할 수 없다. 삭제된 파일을 검색하기 위한 get/findone 메서드가 있긴 한데, 다소 번거롭지만 rxdb에는 자체적인 소프트 삭제 메커니즘이 필요하다는 건 이해한다. 그래서 나는 나만의 deleted 필드를 추가로 만들었다. 따라서 총 3개의 deleted 필드가 있다 - rxDeleted, isDeleted, deleted(Appwrite 기본값).

이제 사용자가 항목을 삭제하면, 나는 isDeleted만 토글하여 복구할 수 있도록 휴지통으로 보낸다. 클라이언트 측에서는 isDeleted를 확인하는 30일 조건을 설정하여 rxDeleted를 토글하는 rxdb 소프트 삭제를 수행한다. 그런 다음 서버 측에서는 rxDeleted의 30일을 확인하는 또 다른 검사를 수행하여 실제로 삭제한다.

rxDeleted를 통한 소프트 삭제는 RxDB가 기기 간에 deleted 필드를 동기화하는 데 필요하다. 하지만 나는 사용자가 항목을 쉽게 복구할 수 있기를 원하기 때문에 그 위에 isDeleted를 추가했다.

결론

나에게 그리 쉽지는 않았고, 이 모든 것을 해결하는 데 몇 주가 걸렸다. 나는 Apple CoreData가 모든 동기화 절차를 처리해 주던 SwiftUI에서 시작했기 때문이다. 단지 동기화를 위해 이렇게 더 많은 일을 해야 한 것은 이번이 처음이었다. 하지만 해내서 기쁘다.


이제 다음 큰 일에 착수할 수 있다!

이전
Copyright 2026
사이트맵
개인정보 처리방침
서비스 약관

테마 제공 Astro-Yi