Skip to content

음성 추가 탭을 구현합니다. - #15

Merged
snughnu merged 15 commits into
devfrom
feat/CoreML
Jul 15, 2026
Merged

음성 추가 탭을 구현합니다.#15
snughnu merged 15 commits into
devfrom
feat/CoreML

Conversation

@snughnu

@snughnu snughnu commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Situation

SpendLearning은 소비 전용 가계부 앱으로, 지금까지 지출 기록은 카테고리를 먼저 고르고 금액/메모를 입력하는 수동 방식만 지원했다. 매번 앱을 켜서 카테고리 목록을 훑고 입력하는 과정이 번거로워, 음성으로 더 빠르게 기록할 수 있는 수단이 필요했다. 다만 카테고리는 사용자가 자유롭게 추가/수정하는 가변적인 값이라, 고정된 클래스로 카테고리를 예측하는 일반적인 분류 모델은 이 앱의 구조와 맞지 않았다.

Task

  • 음성으로 "메모 금액" 형태로 말하면 자동으로 지출을 기록할 수 있는 새 탭을 추가한다.
  • 카테고리는 매번 재학습이 가능해야 하며, 사용자가 새로 만든 카테고리도 예측 대상에 포함되어야 한다.
  • 온디바이스에서 동작해 개인정보가 외부로 나가지 않고, 별도 서버 비용이 들지 않아야 한다.

Action

모델 설계 및 학습 파이프라인 구축

  • CreateML은 macOS 전용이라 iOS 앱 안에서 직접 모델을 만들 수 없다는 제약을 확인하고, Mac에서 Python(coremltools)으로 updatable kNN 분류기를 학습하는 스크립트(MLTraining/train.py)를 별도로 구성했다.
  • NLEmbedding이 한국어를 지원하지 않는 것을 사전에 확인하고, 한글 자모(초성/중성/종성) 단위로 텍스트를 분해해 벡터화하는 방식을 직접 설계해 vocabulary 크기를 줄이면서도 처음 보는 상호명까지 넓게 커버할 수 있도록 했다.
  • 학습된 모델(CategoryClassifier.mlmodel)과 vocabulary(vocabulary.json)를 앱 번들에 포함시키고, CategoryPredictor가 앱 실행 시 이를 쓰기 가능한 위치로 복사해 예측·온디바이스 재학습(MLUpdateTask)을 수행하도록 구현했다.

음성 입력 파이프라인

  • VoiceExpenseParser로 "메모 금액원" 형식의 발화에서 금액과 메모를 정규식 기반으로 분리하고, 자연스러운 어순("~썼어" 등)까지 지원하도록 반복 검증했다.
  • VoiceExpenseViewModelSpeech/AVAudioEngine으로 온디바이스 음성 인식을 수행하고, 파싱 및 카테고리 예측 결과를 기존 저장 흐름(NewExpenseViewModel)에 연결했다.
  • 마이크를 누르고 있는 동안만 녹음되는 푸시투토크 방식과 실시간 자막 표시를 구현했으며, 최초 SwiftUI로 구현했다가 UIHostingController 중첩 환경에서 fullScreenCover가 동작하지 않는 문제를 발견해 UIKit으로 재작성했다.

기존 화면 개선 및 버그 수정

  • ExpenseInputViewController를 카테고리/금액/메모를 한 화면에서 확인·수정할 수 있는 카드 UI로 재구성하고, 음성 흐름과 기존 수동 추가 흐름이 이 화면을 공유하도록 했다.
  • Xcode가 .mlmodel을 빌드 시 .mlmodelc로 컴파일해 번들에는 원본이 남지 않는다는 사실을 실기기 디버깅으로 발견해, 예측이 항상 "기타"로만 나오던 버그를 수정했다.
  • "전체 초기화" 시 지출/카테고리/예측만 초기화되고 음성추가의 학습 데이터는 그대로 남는 문제를 발견해, CategoryPredictor도 함께 초기화하도록 연결했다.
  • 마이크/음성인식 권한이 없거나 시스템 받아쓰기 기능이 꺼진 경우 설정 화면으로 유도하는 안내를 추가했다.
  • 지출 기록이 전혀 없는데도 예측 차트에 0원짜리 실제 소비 라인이 그려지던 버그와, 예측 정확도 계산식이 완전히 빗나간 예측에도 과도하게 높은 점수를 주던 문제를 수정했다.

Result

  • 실기기에서 음성 발화 → 파싱 → 카테고리 예측 → 확인 화면 → 저장 → 온디바이스 재학습까지 전체 파이프라인이 정상 동작함을 확인했다.
  • 사용자가 새로 만든 커스텀 카테고리도 몇 차례 확정 후 정확히 예측되는 것을 실기기에서 검증했다.
  • 외부 API 호출이나 서버 비용 없이, 온디바이스에서만 동작하는 개인화 학습 루프를 구축했다.
  • 신규 검증 지점(모델 학습 스크립트 존재 확인, 파서 단위 케이스)은 이번 범위에서 직접 확인했고, 기존 SettingsViewModelTestsSettingsViewModel 생성자 시그니처 변경에 맞춰 갱신했다.

@snughnu snughnu self-assigned this Jul 15, 2026
@snughnu snughnu added the feat 기능을 구현/수정합니다. label Jul 15, 2026
@snughnu
snughnu merged commit 2ba1e29 into dev Jul 15, 2026
1 check passed
@snughnu
snughnu deleted the feat/CoreML branch July 15, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능을 구현/수정합니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant