1부에서는 Dropbox를 이용해 SQLite DB를 안전하게 주고받을 수 있는지 POC로 확인했고, 2부에서는 언제 Pull하고 언제 Push할지, 충돌이 나면 어떻게 멈출지 정책을 정했다.
이제 구현 방향은 정리된 상태였다. Pull은 Startup에서만 하고, Runtime 중 DB를 갈아끼우지 않으며, Local과 Remote가 모두 바뀌었으면 자동으로 덮어쓰지 않는다. Windows와 macOS 모두에서 같은 정책으로 동작해야 한다는 조건도 포함했다. 이 조건을 기준으로 ChatGPT와 구현 경계를 정리했고, 실제 MyStock 소스 조사와 Production Sync 구현, 회귀 검증은 Codex에 맡겼다.
Production Sync 적용
Codex가 실제 Startup과 persistence 구조를 조사한 뒤, 2부에서 정한 정책을 MyStock 실행 흐름에 연결했다.
MyStock 실행
↓
Dropbox 상태 확인
↓
필요하면 DB Download / 검증 / 교체
↓
SQLite Bootstrap
↓
GUI 시작
GUI가 시작된 이후에는 여러 기능이 DB를 사용하기 때문에 Runtime Pull은 넣지 않았다. Runtime에서는 5분 주기로 Snapshot을 확인하고, 마지막으로 Sync한 상태와 실제 DB 내용이 달라진 경우에만 Conditional Push하도록 구현됐다.
5분 경과
↓
SQLite Snapshot 생성
↓
마지막 Sync 상태와 비교
↓
같음 → NOOP
다름 → Conditional Push
중요한 것은 5분마다 무조건 Dropbox에 파일을 올리는 구조가 아니라는 점이다. 실제 DB 내용이 바뀌었을 때만 전송한다.
장치별 Sync 상태
정책을 정할 때부터 DB 자체와 장치별 Sync 상태는 분리하기로 했다. 이 부분도 Codex 구현에서 그대로 반영됐다.
Dropbox
└─ mystock.sqlite3
각 PC
└─ mystock.sync.json
Dropbox에는 공유 대상인 DB만 두고, 각 PC는 mystock.sync.json을 Local에 따로 가진다. 마지막으로 정상 Sync한 Remote revision과 Snapshot hash, 진행 중인 Upload 또는 Pull 같은 정보는 장치마다 다를 수 있기 때문이다. 실제로 Dropbox App Folder를 확인했을 때도 DB 파일만 보였고 Local JSON은 올라가지 않았다. 처음에는 JSON도 같이 있어야 하나 싶었지만, 오히려 장치 상태를 공유하지 않는 것이 의도한 구조였다.
Crash Recovery
구현 과정에서 가장 신경 쓴 부분 중 하나는 전송 성공 여부가 애매하게 끝나는 경우였다.
Snapshot 생성
↓
Dropbox Upload 성공
↓
Remote 파일 갱신
↓
Local Sync 정보 기록 전
↓
프로그램 종료
이 경우 Dropbox에는 새 DB가 있지만 Local은 이전 Sync 상태만 기억한다. 다음 실행에서 단순 비교만 하면 다른 PC가 Remote를 변경한 것으로 오해할 수 있다.
이 시나리오를 ChatGPT와 검토한 뒤, Codex가 Pending Journal과 재시작 복구 로직을 구현했다. Upload나 Pull을 시작하기 전에 진행 중인 작업의 identity를 남기고, 다음 실행에서 Remote revision과 hash를 다시 확인해 직전 작업의 실제 성공 여부를 복구하는 방식이다. 단순히 실패하면 다시 시도하는 것보다, 전송은 성공했지만 응답이나 Local 기록만 남지 않은 경우를 구분하는 것이 더 중요했다.
DB Update Activity
Production Sync가 동작한 뒤 실제로 MyStock을 사용해보니 다른 문제가 눈에 들어왔다. 앱을 실행하면 REST API를 통해 계좌정보, 가격 이력, ETF 구성종목, 시장지표, 투자자 수급, 거래내역 등을 다시 확인하고 필요한 경우 DB를 갱신한다. 그런데 대부분 Background에서 진행되기 때문에 사용자 입장에서는 지금 어떤 데이터가 갱신 중인지 알기 어려웠다. 그래서 나는 상태바에서 현재 DB 작업을 보여달라는 요구를 추가했다. Codex는 각 기능의 단순한 UI 완료 신호가 아니라 실제 canonical DB 작업의 진행 상태를 연결해 표시하도록 구현했다.
DB 업데이트 · PLUS 글로벌휴머노이드로봇액티브 구성종목
최근 14일 범위 · 12/15 확인 중...
DB가 없는 상태에서 처음 실행하면 초기 데이터 구성 과정도 별도로 보이게 했고, 여러 DB 작업이 동시에 진행되면 대표 작업 하나와 외 N개 진행 중 형태로 나머지 작업 수도 표시하도록 했다.

DB와 Dropbox 상태 분리
상태바를 정리하면서 DB Update와 Dropbox Sync는 서로 다른 작업이라는 점도 분리했다. 왼쪽에는 REST API에 따른 현재 DB 갱신 상태를 보여주고, 오른쪽에는 Dropbox 상태를 독립적으로 표시한다.
왼쪽
DB 업데이트 · ...
오른쪽
Dropbox · 동기화 완료 · 11:11
실제 화면에서도 DB는 ETF 구성종목을 확인하는 중인데 Dropbox는 이미 동기화 완료 상태일 수 있다. 둘을 하나의 진행 상태로 묶으면 무엇이 끝났는지 오해할 수 있어 별도 영역으로 두었다.
실환경 확인
구현과 자동화 검증이 끝난 뒤에는 내가 직접 사용 환경에서 확인했다. 먼저 기존 DB를 제거한 상태로 실행해 Clean DB Initialization이 정상적으로 진행되는지 보고, ETF 초기 데이터 구성 과정과 상태 표시가 실제 화면에서도 자연스럽게 보이는지 확인했다. 그다음 Dropbox Sync를 활성화해 실제 App Folder에 Production DB가 생성되는 것도 확인했다.
Apps
└─ MyStock
└─ mystock
└─ mystock.sqlite3
Local의 mystock.sync.json은 Dropbox에 올라가지 않았고, 화면에서는 DB Update와 Dropbox Sync 상태가 각각 따로 표시됐다. 설계 단계에서 정했던 책임 분리가 실제 파일 구조와 UI에서도 그대로 확인됐다.
Dropbox 전용 구조인가
실제 적용까지 확인하고 나니 마지막으로 궁금해진 것은 이 구조가 Dropbox에서만 가능한가 하는 점이었다. 이번 Sync의 핵심은 SQLite Snapshot, 장치별 Local 상태, Remote revision, Conditional Update, Conflict 처리다. 상위 정책 자체는 특정 클라우드에 완전히 묶여 있지 않다.
다른 클라우드에도 revision이나 checksum에 대응하는 개념은 있지만, Dropbox는 내가 확인한 Remote revision을 기준으로 오래된 상태의 Update를 막는 흐름이 이번 설계와 잘 맞았다.
Dropbox의 Desktop Sync가 안정적이라서 이 구조가 가능했던 것이 아니라, MyStock에서 정한 DB File Sync 정책에 Dropbox API가 잘 맞았다고 보는 편이 더 정확했다.
나는 필요한 조건과 운영 방식을 정하고, ChatGPT와 설계를 좁힌 뒤 Codex에 실제 구현과 검증을 맡겼다. 이후 실제 환경에서 실행해 결과를 확인하고, 사용하면서 필요했던 DB Update와 Dropbox 상태 표시를 다시 요구하는 방식으로 작업을 이어갔다.
