순찰관리 API와 Flutter 오프라인 동기화
현장 순찰 담당자는 통신이 불안정한 지하실·기계실을 돌며 점검 기록을 남겨야 합니다. 기존 서버 응답 구조는 웹 화면 기준으로 설계되어 있어, 모바일에서 필요한 순찰 스케줄·회차·QR 코드·이미지 URL 같은 정보를 담기에 부족했습니다. 약 3주간 백엔드 API 확장과 Flutter 앱 구현을 함께 진행했습니다.
서버 큐 대신 로컬 우선을 선택한 이유
오프라인 지원 방식은 크게 두 가지로 갈립니다. 서버 쪽에 요청을 큐로 쌓아두고 연결이 복구되면 순서대로 처리하는 방식, 아니면 클라이언트가 로컬 DB에 우선 저장하고 별도 동기화 계층이 서버와 맞추는 방식입니다. 순찰 기록은 작성자가 지하에서 몇 분~몇 시간 동안 네트워크 없이 여러 건을 연달아 작성할 수 있는 상황이라, 서버 큐 방식은 “연결이 언제 복구될지 모르는 클라이언트의 요청을 서버가 계속 붙잡고 있어야 한다”는 부담이 있었습니다. 그래서 클라이언트 로컬 DB + 동기화 매니저 구조(로컬 우선)를 선택했습니다.
충돌은 어떻게 다뤘나
순찰은 회차·구역별로 담당자 1인이 배정되는 운영 구조입니다. 같은 회차를 두 기기에서 동시에 작성하는 상황은 운영 프로세스상 발생하지 않도록 이미 배제되어 있었기 때문에, 복잡한 3-way 병합 로직 대신 단순한 규칙으로도 충분하다고 판단했습니다. 로컬에서 작성한 기록은 작성 시각을 기준으로 서버에 업로드되고, 서버는 순찰 회차·포인트 단위의 고유 키로 중복 제출을 막습니다. “기술적으로 가능한 모든 충돌 시나리오”를 다 막는 대신, “운영상 실제로 발생하는 시나리오”를 먼저 좁히고 그에 맞는 단순한 규칙을 선택한 것이 이 설계의 핵심입니다.
계층을 나눠 쌓은 이유
Flutter 앱에는 API 클라이언트 → 리포지토리 → 로컬 DB → 동기화 매니저로 이어지는 계층을 뒀습니다. 코드 생성 기반 로컬 DB를 리포지토리 뒤에 숨겨두었기 때문에, 화면 쪽 코드는 “지금 온라인인지 오프라인인지”를 신경 쓰지 않고 리포지토리만 호출하면 됩니다. 실제로 온라인/오프라인 전환 로직은 동기화 매니저 한 곳에만 존재하고, 나머지 계층은 그 사실을 모른 채로 동작합니다. 3주라는 짧은 기간 안에 백엔드 API 확장부터 앱 구현까지 25건 이상의 커밋으로 끝낼 수 있었던 것도, 이 계층 분리 덕에 화면을 만들면서 동기화 로직을 동시에 고민하지 않아도 됐기 때문이라고 생각합니다.
이 프로젝트는 지금까지 백엔드·서버 운영에 집중해온 것과, 사이드 프로젝트로 넓혀온 프론트엔드·모바일 경험이 한 번에 요구된 작업이었습니다. 런덤메이트에서 익힌 클라이언트 상태 관리 감각이 여기서도 그대로 도움이 됐습니다.