로컬 장부
사용자가 동기화를 선택하기 전에는 서버로 보내지 않습니다.
CASE STUDY 01 모바일 업무관리 · Flutter / Kotlin
고객이 요청한 장부 관리 앱을 직원·근무지·근무일지·정산이 연결되는 업무 흐름으로 구체화하고, 개인 작업과 공용 협업의 저장 규칙을 분리했습니다.
고객의 요구 제시와 작업자의 제품·데이터 문제 구체화만 공개합니다. 고객명, 개인 원문, 금액, 성과 수치는 사용하지 않습니다.
기록에서 정산까지
끊기지 않는 업무 맥락고객 문제
고객은 현장 장부를 관리할 앱을 요청했습니다. 작업자는 이를 단순 입력 화면이 아니라 ‘누가, 어디에서, 언제 일했고, 무엇이 완료되어 어떤 정산으로 이어지는가’를 연결하는 제품·데이터 문제로 바꿨습니다.
직원 · 근무지 · 근무시간 · 청구액 · 지급액
기준정보 · 근무일지 · 완료 상태 · 두 방향 정산
기록 → 진행 확인 → 완료 → 정산 확인의 연속 흐름
해결한 사용자 흐름
입력 화면을 늘리는 대신, 업무가 어느 단계에 있고 다음에 무엇을 확인해야 하는지 하나의 흐름으로 묶었습니다.
장부 안에서 직원과 근무지를 연결하고 시간·청구·지급 정보를 한 흐름으로 기록합니다.
아직 끝나지 않은 근무를 진행 상태로 분리해 다음 행동이 필요한 대상을 놓치지 않게 합니다.
업무 완료를 명시적인 상태로 바꾸고, 원래 맥락을 보존한 채 정산 대상으로 연결합니다.
업체 청구와 직원 지급을 서로 다른 상태로 관리해 미수·미지급을 구분합니다.
데이터가 사라지지 않게
저장 위치를 사용자가 선택하고, 개인 작업과 여러 사람의 협업이 요구하는 일관성 수준을 섞지 않았습니다.
사용자가 동기화를 선택하기 전에는 서버로 보내지 않습니다.
outbox·push·cursor pull로 미전송 변경과 서버 변경을 조율합니다.
membership·role·revision을 서버가 확인하고 참여자가 같은 확정 상태를 봅니다.
원칙로그인이 로컬 데이터 전송의 동의가 되지 않고, WebSocket 신호가 업무 데이터의 정본이 되지 않게 했습니다.
2026.08 · CURRENT IMPLEMENTATION
초기 기능을 나열하는 데서 멈추지 않고, 화면 폭이 바뀌는 순간과 동기화 변경량이 늘어나는 순간, 새 API가 기존 앱과 만나는 순간까지 제품이 이어지도록 고도화했습니다.
LOCAL SIMULATOR · IPAD LANDSCAPE선택한 근무일지는 목록에서 상태와 함께 강조되고, 오른쪽에는 직원·근무지·정산일·근무 시간·지급·청구 상태가 동시에 이어집니다.
2026-08-24 로컬 iPad Simulator 렌더입니다. 실기기·App Store·production 동작을 뜻하지 않습니다.휴대폰은 한 화면에 집중하고, 충분히 넓은 화면은 목록과 상세를 함께 보여 줍니다. 선택·필터·목록 맥락은 회전과 창 크기 변화 뒤에도 가능한 한 이어집니다.
APP adaptive workspace source · implementedsync pull payload를 entity type별 batch로 읽으면서 cursor·revision·tombstone 순서를 유지합니다. 실시간 relay는 DB commit 신호로 깨어나고, 안전 polling과 HTTP cursor pull이 누락을 복구합니다.
API batch materialization · realtime outbox · implementedrelease profile, canonical fixture, OpenAPI baseline과 consumer/provider 의미 테스트를 CI에 연결했습니다. 해석할 수 없는 critical type은 cursor를 전진시키지 않고 로컬 Outbox를 보존합니다.
Root · APP · API compatibility matrix · implemented어떻게 만들었는가
비개발자는 데이터가 어디에 남는지 볼 수 있고, 기술 검토자는 각 계층이 무엇을 결정하고 무엇을 믿지 않는지 내려가며 확인할 수 있습니다.
01Flutter 화면과 Riverpod 상태는 사용자 intent와 화면 상태를 맡습니다.
02Service와 Repository가 use case, 오류 분류, local/remote source 경계를 숨깁니다.
03Quarkus API는 인증·권한·idempotency·revision과 transaction을 서버에서 확정합니다.
04Drift는 local replica, PostgreSQL은 서버 연결 장부의 canonical state입니다.
Widget은 사용자 intent, State는 로딩·오류·선택 상태, Service는 use case와 오류 분류, Repository는 local/remote source, DAO와 Drift는 query·transaction·schema를 맡습니다.
Controller는 HTTP·DTO, Application Service는 authorization·transaction·idempotency, Domain Service는 conflict·merge 의미, Repository와 PostgreSQL은 canonical persistence를 맡습니다.
개인 / 공용 장부 통신
개인 장부는 로컬 변경을 안전하게 서버로 옮기고, 공용 장부는 서버 승인과 권한을 먼저 확인합니다. 두 경로 모두 HTTP cursor pull로 누락을 복구합니다.
사용자 변경과 outbox operation을 같은 로컬 transaction에 기록
같은 논리 operation은 같은 Idempotency-Key와 expectedRevision으로 push
서버는 account·ledger·entity scope와 revision을 검증해 transaction 확정
cursor 이후 변경을 pull하고 local row·revision·cursor를 함께 반영
APP이 온라인 상태와 접근 가능성을 확인하고 HTTP mutation 전송
API가 membership·role·capability·revision·idempotency를 검증
WebSocket은 cursor.changed 같은 최소 변경 신호만 전달
신호 누락·재연결 뒤에도 HTTP cursor pull로 서버 상태에 수렴
응답 유실 뒤 새 작업으로 중복 반영하지 않습니다.
충돌을 성공으로 바꾸거나 조용히 덮어쓰지 않습니다.
클라이언트가 보낸 account·role 주장을 신뢰하지 않습니다.
실패를 숨기지 않는 상태
‘저장 실패’ 하나로 합치지 않고 개인 오프라인, 공용 오프라인, 결과 불명확, 충돌, 권한 부족, 접근 회수, 앱 계약 불일치를 구분했습니다.
업무를 계속하고 연결 후 같은 outbox operation으로 전송
입력과 원본을 지우지 않음
진입·저장을 멈추고 최신 서버 상태 확인 뒤 재개
오프라인 변경을 확정하거나 Outbox로 만들지 않음
새 operation을 만들지 않고 같은 key 또는 status/pull로 결과 확인
중복 반영을 성공처럼 숨기지 않음
최신 상태를 확인하고 다시 저장하거나 취소
last-write-wins로 조용히 덮어쓰지 않음
입력과 접근 가능한 장부를 유지하고 허용 범위를 안내
단일 403을 장부 접근 회수로 오인하지 않음
membership 전체 상태를 재확인하고 허용되지 않는 local replica 정리
다른 장부에서 접근 가능한 기준정보는 보존
서버 쓰기를 멈추고 호환 가능한 업데이트 경로를 안내
local CRUD와 기존 Outbox를 보존하고 cursor를 전진시키지 않음
역할과 AI 협업
AI가 기여한 작업과 사람이 책임진 판단을 분리하고, 공개 근거가 없는 ownership은 사례의 성과로 쓰지 않습니다.
MAKER RESPONSIBILITY
AI ASSISTANCE
공개 근거가 확정되지 않은 코드 영역을 단독 구현으로 표현하지 않습니다. AI를 사용했다는 사실과 작업자가 책임진 결정·검토·검증을 분리합니다.
어떻게 검증했는가
문서·source, 자동화 테스트, 로컬 렌더, 외부 runtime, production은 서로 대체할 수 없는 별도 근거입니다.
현재 문서·source에서 구현 구조를 확인
이번 근거 실행에서 자동화 테스트 통과
이번 산출물을 로컬에서 직접 빌드·렌더 검토
현재 외부 서비스·기기에서 직접 확인
현재 production 동작을 직접 확인
이번 범위에서 확인하지 않아 주장하지 않음
현재 PRODUCT·OFFLINE-FIRST-SYNC·SHARED-LEDGER 정본과 APP 구조에서 로컬/개인/공용 장부 의미를 확인했습니다.
현재 기기 동작이나 production 상태를 뜻하지 않습니다.현재 ARCHITECTURE와 source에서 Flutter–Riverpod–Drift, Quarkus–PostgreSQL 계층과 경계를 확인했습니다.
이 페이지는 전체 endpoint·schema 목록을 복제하지 않습니다.현재 CLIENT_SERVER·DATABASE와 source에서 outbox, Idempotency-Key, expectedRevision, batch pull materialization, cursor pull, realtime ticket·commit signal 경로를 확인했습니다.
exactly-once 또는 완전 동시 CAS 보장을 주장하지 않습니다.현재 APP adaptive workspace와 Root·APP·API compatibility contract에서 목록–상세 연속성, 지원 build profile, fixture·OpenAPI·semantic gate 구조를 확인했습니다.
현재 실기기·Store 채택률·production minimum-build 동작을 뜻하지 않습니다.관련 test source는 확인했지만 이 포트폴리오 작업에서는 Ongyeol 저장소 테스트를 다시 실행하지 않았습니다.
오래된 개수·통과 기록을 현재 성과로 사용하지 않습니다.이번 최신화에서는 온결 제품의 Store·외부 runtime·고객 성과를 다시 검증하지 않았습니다.
온결의 production, 고객 성과, 트래픽·부하 수치를 주장하지 않습니다.production static export와 390px/1440px 대표 흐름, 실제 제품 화면, navigation, keyboard focus, hard states와 기본 contrast를 직접 렌더 검토했습니다.
이 상태는 포트폴리오 산출물의 로컬 검증이며, 공개 runtime 검증과 구분합니다.제공 가능한 외주 범위
비개발자의 요구를 제품·데이터 문제로 번역하고, 실패와 복구까지 포함한 구현 가능한 범위로 정리합니다.
현장 입력, 상태 흐름, 기준정보와 정산을 하나의 모바일 제품으로 구조화합니다.
네트워크가 불안정한 환경에서 저장 위치, 재시도, 충돌, 복구가 보이도록 설계합니다.
반복 업무와 권한 경계를 화면·데이터·API 책임으로 나눠 구현 가능한 범위로 만듭니다.
데이터 손실 위험, 중복 요청, 실패를 성공처럼 보이는 상태와 과도한 수동 절차를 점검합니다.
PROJECT INQUIRY
목표, 사용 환경, 지금 가진 자료, 원하는 일정을 함께 보내주시면 MVP·운영 도구·기존 흐름 개선 중 어디서 시작할지 정리해 답합니다.
문의 방법 확인이 포트폴리오를 전달받은 이메일·메신저·외주 플랫폼 채널에서 그대로 답장해 주세요. 공개용 개인정보나 가짜 연락처는 넣지 않았습니다.