작업 목록

Company · Flutter App · Production · 2025-2026

Bigtablet Intranet

Flutter 앱에서 협업 업무의 흐름과 상태를 끊김 없이 연결하다

Flutter로 만든 사내 모바일 앱에서 홈·메신저·업무 메뉴를 하나의 앱 셸로 연결하고, 메일·전자결재·알림의 캐시와 실시간 동기화, 딥링크, 부분 실패를 정리했습니다.

Flutter
iOS·Android 모바일 앱
BLoC · go_router · 자체 디자인 시스템
BLoC + SQLite
상태와 오프라인 캐시
cache-then-network
SSE + FCM
실시간 동기화와 푸시
딥링크·재연결·음소거 상태

Context

사내 인트라넷은 Flutter로 만든 iOS·Android 모바일 앱입니다. 홈, 메신저, 전체 업무의 3탭 앱 셸 안에서 메일, 전자결재, 일정, 문서, 조직 정보, 내선전화가 함께 움직입니다. 모바일에서는 연결이 끊기거나 앱이 종료된 상태에서 알림을 누르는 일이 흔하고, 목록과 상세 데이터가 서로 다른 시점에 갱신되기도 합니다. 정상적인 API 응답만 가정하면 사용자는 방금 읽은 내용이 사라지거나, 잘못된 화면에 도착하거나, 실패한 일괄 작업을 성공으로 오해할 수 있었습니다.

웹과 모바일의 기능 차이도 문제였습니다. 즐겨찾기, 알림 수준, 섹션 상태가 플랫폼마다 다르면 같은 계정을 사용해도 다른 제품처럼 느껴집니다. 기능을 추가하는 것만큼 여러 채널의 상태를 같은 의미로 유지하는 일이 중요했습니다.

Decisions

메일 목록은 cache-then-network 흐름으로 바꿨습니다. 로컬 SQLite 스냅샷을 먼저 그려 사용자가 즉시 내용을 확인하게 하고, 최신 서버 응답이 도착하면 같은 화면에서 교체했습니다. 캐시 전용 조회와 라이브 조회를 분리해 로딩 속도와 데이터 신뢰성을 서로 대체 관계로 만들지 않았습니다.

알림은 도착 자체보다 목적지까지의 연결을 중심으로 정리했습니다. FCM 토큰을 최초 발급에만 의존하지 않고 회전 스트림에서 다시 등록했으며, 종료 상태에서 시작해도 메일과 결재 알림이 해당 문서로 이동하도록 초기 라우팅 순서를 보강했습니다. 음소거한 채널은 연결 중에도 알림 상태에 반영되게 했습니다.

일괄 작업에서는 낙관적 성공 메시지를 줄였습니다. 메일 삭제·보관과 결재 일괄 승인에서 transport 실패, 부분 성공, 중복 결과를 나눠 처리하고, 페이지를 빠르게 이동했을 때 늦은 응답이 현재 목록을 덮어쓰지 않도록 stale response를 버렸습니다.

Outcome

앱은 연결 상태에 따라 빈 화면과 갑작스러운 갱신을 반복하는 대신, 사용 가능한 로컬 상태에서 시작해 최신 상태로 자연스럽게 이동하게 됐습니다. 알림은 단순 배너가 아니라 사용자가 처리해야 할 메일과 결재 문서의 정확한 진입점이 됐고, 실패한 일괄 작업도 성공처럼 보이지 않게 됐습니다.

이 작업에서 가장 중요했던 점은 오프라인이나 실패를 예외 화면으로만 취급하지 않은 것입니다. 협업 도구에서 신뢰는 모든 데이터가 즉시 최신인 데서만 생기지 않습니다. 현재 무엇을 보고 있는지, 새 데이터가 언제 반영됐는지, 요청이 실제로 성공했는지를 사용자가 이해할 수 있을 때 신뢰가 유지됩니다.

04

근거

역할

Flutter 앱 셸, 메신저·메일·전자결재 화면, 오프라인 데이터 흐름, 알림 딥링크와 실패 복구 경험

집중한 영역

Flutter app shell · BLoC architecture · Offline-first · Realtime sync

기술

Flutter · Dart · BLoC · Clean Architecture · SQLite · WebSocket · SSE · FCM · go_router · Y-CRDT

공개 범위

내부 제품이므로 고객·조직 정보와 실제 화면은 제외하고, 저장소에서 확인 가능한 구조와 익명화된 흐름만 설명합니다.

다음 작업 Boundra