권희철

프로젝트 목록

iOS

Insulin Note

인슐린 투여 기록 입력 뎁스를 5단계에서 1단계로 줄인 iOS 위젯 앱

기간:
2024.07.15 ~ 2026.04.26
역할:
개인 개발
SwiftSwiftUISwiftDataWidgetKitAppIntents
5 → 1단계
투여 기록 동선
26건
App Store 다운로드

2026-09-27 기준

iOS 17+
지원 버전
개인 개발
기획부터 배포까지

배경과 사용자 문제

저는 제1형 당뇨로 지효성·속효성 인슐린을 투여합니다. 지효성 인슐린은 보통 저녁 10시에서 11시 사이에 맞지만, 실제 취침은 그보다 늦어 “오늘 투여했는지” 기억이 흐려지는 일이 있었습니다. 누락도 위험하지만 기억만 믿고 중복 투여하는 것 역시 피해야 했습니다.

기존 CGM 앱에도 투여 기록 기능은 있었지만, 앱 실행부터 저장까지 다섯 단계 이상을 지나야 했습니다. 매일 반복해야 하는 행동에서 이 정도의 마찰은 결국 기록 포기로 이어졌습니다. 그래서 새로운 건강관리 앱을 하나 더 만드는 대신, 기억이 필요한 순간에 가장 가까운 홈 화면과 잠금 화면에서 한 번에 기록하는 경험을 목표로 잡았습니다.

설계와 기술 선택

앱, 홈 화면 위젯, 잠금 화면 위젯이 같은 투여 기록을 봐야 했습니다. SwiftData를 로컬 저장소로 두고 App Group을 통해 데이터를 공유했으며, WidgetKit과 AppIntents로 앱을 열지 않고도 기록과 확인이 가능하게 만들었습니다.

  1. 01홈·잠금 화면현재 투여 여부와 최근 시각 확인
  2. 02AppIntent위젯에서 투여 기록 요청
  3. 03ModelActorCRUD를 한 실행 컨텍스트에서 직렬 처리
  4. 04SwiftDataApp Group의 공유 저장소에 저장
  5. 05WidgetKit타임라인을 갱신해 결과 반영
투여 기록이 앱과 위젯에 반영되는 흐름

SwiftData는 빠른 로컬 기록과 추후 iCloud 확장 가능성을 고려해 선택했습니다. iOS 17 이상을 최소 버전으로 둔 이유도 SwiftData와 인터랙티브 위젯에 필요한 AppIntents를 사용하기 위해서입니다. 데이터 접근은 처음부터 완성된 구조가 아니었고, 실제 동기화 문제와 Swift 6 전환을 거치며 단일 컨텍스트에서 ModelActor 기반 구조로 발전했습니다.

화면 쪽은 View와 Model만 두는 MV 패턴입니다. View는 사용자 입력을 받아 ModelActor에 요청만 보내고, 바뀐 Model이 다시 View의 상태를 갱신하는 한 방향 흐름입니다.

User에서 View, ModelActor, Model을 거쳐 다시 View로 돌아오는 MV 패턴 구조도
MV 패턴: View의 요청은 ModelActor를 거쳐 Model을 바꾸고, 바뀐 Model이 View를 다시 그립니다.
잠금 화면 위젯, 메인 화면의 기록·설정 시트, 달력의 기록 수정 화면이 각각 SwiftData의 InsulinRecord와 InsulinSetting으로 저장되는 흐름도
화면 이동 동선: 잠금 화면 위젯, 메인 화면의 기록 시트, 달력에서 고친 기록이 모두 SwiftData의 InsulinRecord·InsulinSetting에 저장됩니다.

핵심 경험과 기능

메인 화면에서는 지효성·속효성 인슐린을 기록하고, 잘못 입력한 기록을 수정하거나 삭제할 수 있습니다. 앱 첫 실행 시 기본 인슐린 설정을 만들어 바로 사용할 수 있게 했습니다.

  • 메인 화면에서 지효성 인슐린 투여량을 골라 기록한 뒤, 지효성과 속효성 기록의 투여량과 시간을 수정하는 장면
    메인 화면
  • 달력에서 날짜를 눌러 그날의 기록을 열고, 복약률과 요일별 속효성 투여 패턴 차트를 바꿔 보는 장면
    캘린더 화면
  • 홈 화면의 지효성·속효성 위젯 버튼을 눌러 앱을 열지 않고 기록하자 투여 시각이 표시되는 장면
    홈 화면 위젯으로 기록
  • 잠금 화면 위젯의 버튼으로 투여를 기록하자 기록 없음이 투여 시각으로 바뀌는 장면
    잠금 화면 위젯으로 기록
실제 앱 화면 녹화. 가로로 넘겨 볼 수 있습니다.

달력은 단순 기록 목록이 아니라 생활 패턴을 돌아보는 화면으로 확장했습니다. 투여일, 복약률, 평균 투여 시각을 보여 주고 속효성 인슐린은 시간과 투여량 패턴을 차트로 확인할 수 있습니다. 홈 화면 위젯에서는 빠르게 기록하고, 잠금 화면에서는 최근 투여 시각을 확인하게 해 앱에 들어가는 횟수를 줄였습니다.

  • 지효성·속효성 인슐린 기록, 수정, 삭제
  • 투여일·복약률·평균 투여 시각을 보여 주는 달력과 대시보드
  • 속효성 인슐린 시간·투여량 패턴 차트와 연속 기록 동기
  • 홈 화면 위젯의 빠른 기록과 잠금 화면의 최신 투여 시각 확인

문제 해결 경험

핵심 맥락은 바로 읽을 수 있게 두고, 구현 세부는 필요한 항목만 펼쳐 볼 수 있게 정리했습니다.

앱과 위젯의 기록이 동기화되지 않던 문제
문제
Interactive Widget에서 투여를 추가해도 메인 앱 UI와 실제 저장소에 반영되지 않았습니다.
원인
AppIntent는 View 계층이 아니어서 환경의 ModelContext를 받을 수 없었습니다. 별도로 만든 컨텍스트를 사용하면서 save 호출까지 누락되어 변경이 메모리에만 남았습니다.
해결
앱과 위젯이 같은 컨텍스트를 보게 하고 저장을 명시했습니다. Swift 6 전환 뒤에는 공유 컨텍스트의 데이터 경쟁 가능성을 제거하기 위해 CRUD를 ModelActor 안으로 옮겼습니다.
결과
모든 데이터 접근을 Actor의 실행 컨텍스트에서 직렬 처리했습니다. 비동기 Task에서 모델이 해제되던 크래시는 진입 전에 필요한 값만 복사해 전달해 해결했습니다.
View 계층의 책임과 데이터 흐름이 꼬인 문제
문제
RecordView, 인슐린별 하위 뷰, 투여량 시트 사이에 Binding과 액션 클로저가 복잡하게 얽혀 있었습니다.
원인
하위 뷰가 자신의 UI뿐 아니라 시트에 전달할 비즈니스 로직과 기본 투여량까지 알고 있어 결합도가 높았습니다.
해결
상태와 핵심 로직은 최상위 뷰가 관리하고, 하위 뷰는 받은 데이터를 렌더링한 뒤 클로저만 호출하는 단방향 MV 구조로 바꿨습니다.
결과
Binding 체인을 줄이고 수정 지점을 부모 뷰로 모았습니다. 기능을 추가할 때 확인해야 할 범위와 사이드 이펙트 가능성이 함께 줄었습니다.
기록은 되지만 계속 쓰고 싶지 않던 UI
문제
초기 버전은 투박한 테두리 중심 UI였고 수정·삭제가 없었습니다. 달력은 투여 여부만 보여 주고 독립된 설정 탭은 공간에 비해 역할이 작았습니다.
원인
지효성 투여 기록이라는 MVP에 집중하면서 잘못된 입력과 속효성 기록 동기처럼 실제 사용에서 생기는 요구를 충분히 반영하지 못했습니다.
해결
기록 수정·삭제를 추가하고 설정을 메인 화면에 통합했습니다. 달력 아래에는 복약 일관성과 속효성 패턴을 보여 주는 대시보드와 스트릭을 배치했습니다.
결과
좁은 화면의 정보 구조를 단순화하고, 강제적인 기록 대신 자신의 생활 패턴을 확인하며 자발적으로 기록할 이유를 만들었습니다.

성과와 회고

잠금 화면에서 기록을 확인한 뒤 바로 투여하는 개인 루틴이 생겼고, “오늘 맞았는지 모르겠다”는 걱정을 줄일 수 있었습니다. 2026년 9월 27일 기준 App Store 다운로드는 26건입니다. 큰 숫자는 아니지만, 개인 문제에서 출발한 기능을 실제 배포 가능한 제품으로 끝까지 운영했다는 점에 의미가 있습니다.

이 프로젝트에서 가장 크게 배운 점은 기능보다 반복 행동의 마찰과 데이터의 신뢰성이 먼저라는 것입니다. 다음 업데이트(1.2)에서는 아래 내용을 준비하고 있습니다.

  • HealthKit 연동: 투여 기록을 건강 앱에 저장하고, CGM 앱이 올린 혈당 기록을 가져와 어떤 상황에서 얼마나 투여했는지 돌아볼 수 있는 지표를 제공합니다.
  • 로컬라이징: 최소한 영어와 일본어를 지원합니다.
  • 코드 정리: 메서드가 많아진 ModelActor를 역할별 파일로 나눕니다.
  • 테스트: Swift Testing으로 핵심 로직을 검증합니다.
  • 버그 수정: 메인 화면에서 기록을 수정해도 화면에 바로 반영되지 않는 문제를 고칩니다.
Insulin Note 메인 화면