성장일기AI 개발 후기 — 스마트폰 안에 AI를 가두다
// RECOMMENDED GEAR
삼성전자 갤럭시 S26 자급제, 블랙, 256GBFlutter 앱 개발 & 테스트에 최적인 최신 Android 플래그십
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
성장일기AI 개발 후기 — 스마트폰 안에 AI를 가두다
"기록만 해. 나머지는 AI가 다 해줄게."
이 한 문장이 성장일기AI의 핵심 철학이다. AI가 일기를 분석해준다는 앱은 많지만, 내 일기를 서버로 보내지 않고 스마트폰 안에서만 처리한다는 앱은 거의 없었다.
왜 만들었나
AI 일기 앱을 쓰다 보면 항상 찜찜한 게 하나 있다. 내가 쓴 감정, 사람 이름, 오늘 있었던 일들이 어딘가의 서버로 날아간다는 것.
일기는 가장 사적인 기록이다. 그걸 클라우드 AI에게 넘기는 게 맞나 싶었다. 그래서 방향을 잡았다.
"AI는 내 폰 안에만 있어야 한다."
마침 Google의 Gemma 모델이 오픈소스로 공개됐고, Flutter에서 flutter_gemma 패키지로 온디바이스 추론이 가능하다는 걸 알게 됐다. 그게 이 프로젝트의 시작이었다.
온디바이스 AI란 무엇인가
일반적인 AI 앱은 이렇게 동작한다:
사용자 입력 → 서버(OpenAI, Gemini API 등) → AI 응답 → 앱 표시
성장일기AI는 다르다:
사용자 입력 → 폰 내부 AI 모델 → AI 응답 → 앱 표시
인터넷이 없어도 된다. 서버가 없어도 된다. 데이터가 폰 밖으로 나가지 않는다.
대신 트레이드오프가 있다. 모델 파일을 기기에 직접 다운받아야 한다 (약 2.6GB). 그리고 성능은 서버 AI보다 느리다. 하지만 프라이버시라는 가치가 이 트레이드오프를 충분히 정당화한다고 생각했다.
무슨 기능을 넣었나
핵심: AI 자동 분석
일기를 저장하면 AI가 자동으로 분석한다:
- 감정 — 기쁨, 슬픔, 설렘, 불안, 평온 등 8가지
- 요약 — 한 줄 요약
- 태그 — 자동 키워드 추출
- 인물 — 등장한 사람 이름
- 장소 — 언급된 장소
- 날씨 — 날씨 표현 감지
AI에게 JSON 형식으로 응답하도록 강제 프롬프트를 짜고, 파싱해서 DB에 저장한다.
이미지 선분석
일기에 사진을 첨부하면, 저장 버튼을 누르기 전에 이미 백그라운드에서 이미지 분석이 돌고 있다. 사진을 고르는 순간부터 AI가 분석을 시작해서, 저장할 때는 결과가 이미 준비돼 있다.
썸네일에 스피너(분석 중)와 체크마크(완료)를 오버레이해서 진행 상태를 직관적으로 보여줬다.
AI 상황 예측 분석
이미지 분석 결과와 일기 텍스트를 교차 분석해서 등장 인물과 상황을 예측하는 기능을 추가했다. "오늘 친구 민준이랑..." 이라고 적고 사진을 첨부하면, AI가 이미지 속 인물이 민준이일 가능성을 파악하는 식이다.
갤러리 + 인물 필터
모든 일기 사진을 날짜별로 모아보는 갤러리 탭을 만들었다. 상단에 인물 칩 필터를 달아서 "민준" 칩을 누르면 민준이가 등장하는 사진만 모아볼 수 있다.
검색
FTS5(Full-Text Search) 가상 테이블을 써서 제목, 내용, AI 요약, 태그, 인물, 장소를 한 번에 검색한다. FTS5를 지원하지 않는 기기는 LIKE 검색으로 자동 fallback.
리뷰/통계
감정 분포 파이 차트, 연속 기록 일수, 월별 일기 수, 자주 등장하는 키워드 클라우드를 보여주는 탭을 만들었다. 내 감정 패턴을 한눈에 볼 수 있다.
개발하면서 부딪힌 것들
1. 온디바이스 AI는 생각보다 까다롭다
flutter_gemma를 쓰면 쉬울 줄 알았다. 하지만 실제로 붙여보니 예상치 못한 버그들이 많았다.
가장 흔한 패턴은 이것이었다:
// 이렇게 쓰면 크래시 난다
final result = await LocalAIService.instance.loadedModel!.generateText(prompt);
// await 이전에 모델을 로컬 변수로 캡처해야 안전하다
final model = LocalAIService.instance.loadedModel;
if (model == null) return;
final result = await model.generateText(prompt);
비동기 코드 사이에 다른 화면에서 모델을 언로드하면, loadedModel이 null이 되면서 null dereference 크래시가 난다. await 이전에 로컬 변수로 캡처하는 것만으로 이 문제가 해결됐다.
이 외에도:
- 한국어는 영어보다 토큰을 2~4배 더 소비해서
maxTokens를 충분히 (6000 이상) 줘야 함 - 이미지 분석은 반드시
createChat()방식으로 해야 함 (createSession은 이미지를 무시) - 모델 다운로드 중
loadModel()을 동시에 호출하면 네이티브 리소스 충돌 - 스트리밍 응답 중
dispose()되면 스트림 취소 타이밍에 따라 데이터 손실
이런 것들을 하나씩 겪으면서 온디바이스 AI 구현 가이드 문서를 별도로 작성했다. 다음 앱 만들 때 그대로 쓰기 위해서.
2. 백그라운드 모델 다운로드
2.6GB 파일을 앱 안에서 다운받는 건 생각보다 복잡하다. background_downloader 패키지를 썼는데, OS 레벨 백그라운드 다운로드를 지원해서 앱이 꺼져도 다운로드가 유지된다.
진행률 표시, 다운로드 취소 버튼, 다운로드 중단 시 부분 파일 삭제 처리, 앱 재시작 시 상태 복구 등을 모두 구현했다. 사용자 경험을 위해서 꼭 필요한 부분들이었다.
3. Phase 0 검증을 먼저 했다
"이미지 5장을 백그라운드에서 AI 분석할 수 있는가"를 먼저 검증한 다음에 전체 기능을 만들었다. 핵심 기술이 실제로 동작한다는 걸 확인하지 않고 UI부터 만들었다가 나중에 기술적으로 불가능하다는 걸 알게 되면 처음부터 다시 해야 하니까.
이 검증 화면을 앱 안에 그대로 남겨두고 성능 테스트 탭으로 활용했다.
개발 기간과 규모
- 개발 기간: 약 1주일 (집중 개발)
- 코드 규모: Flutter 파일 30+개
- 개발 단계: Phase 0~8 (9단계)
- 테스트: Samsung Galaxy Z Flip3 실기기 테스트 완료
- 버전: v1.0.36 (Play Store 내부 테스트 + App Store Connect 제출 완료)
기술 스택
| 영역 | 기술 |
|---|---|
| 프레임워크 | Flutter 3+ |
| 온디바이스 AI | flutter_gemma 0.13.x (LiteRT) |
| AI 모델 | Gemma 4 E2B (2.6GB) / E4B (3.7GB) |
| 데이터베이스 | SQLite (sqflite + FTS5) |
| 상태 관리 | Riverpod |
| 백그라운드 다운로드 | background_downloader |
| 차트 | fl_chart |
| 광고 | Google AdMob |
| 플랫폼 | Android + iOS |
마치며
온디바이스 AI 앱을 만들면서 가장 크게 느낀 건, "가능하다"는 것이었다.
2~3년 전만 해도 거대한 서버에서나 돌아가던 AI가 이제 내 손 안의 스마트폰에서 실시간으로 추론한다. 인터넷 없이, 서버 비용 없이, 데이터 유출 걱정 없이.
물론 아직 느리고, 모델 용량도 크고, 한국어 품질도 완벽하진 않다. 하지만 방향은 맞다고 생각한다. 앞으로 모델은 더 작아지고 빨라질 것이고, 온디바이스 AI 앱의 가능성은 점점 넓어질 것이다.
성장일기AI는 그 가능성을 직접 탐색해본 첫 번째 결과물이다.
성장일기AI는 Play Store 내부 테스트 중입니다.
이 글 공유하기
// SPONSORED
[>]댓글
아직 댓글이 없어요. 첫 댓글을 남겨보세요!