TRENDSHIFT 딥다이브 · 2026-06-02

Echo Loop 딥다이브
— 영어 듣고 말하기를 '자동으로' 굴려주는 Flutter 앱

오디오 한 편을 "들어는 봤다"에서 끝내지 않고, 입으로 나올 때까지 끌고 가는 영어 듣기·말하기 훈련 앱입니다. 핵심은 콘텐츠가 아니라 학습 절차의 자동화 — 사용자는 "지금 뭘 해야 하지?"를 고민할 필요 없이 듣고 따라 말하기만 하면 됩니다. 하나의 Dart 코드베이스로 iOS·Android·macOS·Windows·Linux를 모두 굴리는 Flutter 앱이라, 코드를 공부하기에도 좋은 표본입니다. (저장소: echo-loop/Echo-Loop · Dart 95% · ★565 · TrendShift 일간 5위 · AGPL-3.0)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

이 레포가 무엇을 하는 물건인가.

핵심 메시지

"영어 자료가 부족한 게 아니라,
한 편을 '제대로 끝까지' 굴리는 방법이 없었다."

Echo Loop는 듣기 → 정밀히 듣기 → 따라 말하기 → 내 말로 다시 말하기 → 간격 복습을 하나의 자동 진행 루프로 엮습니다. 사용자가 매번 "오늘 뭘, 몇 번, 언제 복습하지?"를 결정하지 않아도 되게 만드는 것이 이 앱의 본질입니다.

대부분의 영어 학습은 자료가 모자라서 실패하지 않습니다. 오늘은 팟캐스트 하나, 내일은 연설 하나 — 자료는 쌓이는데 진짜로 練熟(연습해서 익힘)한 건 거의 없는 상태가 반복됩니다. 들을 땐 이해한 것 같다가 며칠 뒤 잊어버리고, 그 표현을 내 입으로 말하는 건 더더욱 안 됩니다.

Echo Loop는 이 "방법을 몰라서, 그리고 알아도 매 단계 판단하기가 지쳐서 못 하는" 문제를 정조준합니다. 한 단락의 오디오를 골라 시작 버튼을 누르면, 블라인드 리스닝(맨귀로 한 번 통째로 듣기)부터 통관(클리어)까지 매 단계 "지금 뭘 하라"를 직접 띄워 줍니다. 중앙민족대학 외국어대학 양옌(杨艳) 교수(베이징대 영문학 박사)가 방법론을 자문했고, 라이선스는 AGPL-3.0(강한 카피레프트 오픈소스)입니다.

용어
셰도잉 (Shadowing, 따라 말하기)
원어민 음성을 거의 동시에 그림자처럼 따라 말하는 훈련. 발음·리듬·억양을 통째로 흉내 내 "귀로 들은 것"을 "입 근육"으로 옮기는 단계입니다. 노래를 따라 부르며 가사를 외우는 것과 비슷합니다.

2왜 주목받는가

트렌딩 이유 · 경쟁 앱 대비 장점.

기능 하나하나만 보면 다른 앱에도 일부 있습니다. Echo Loop의 진짜 차별점은 "각 단계를 하나로 이어 붙여 자동으로 끝까지 끌고 간다"는 점입니다. 사용자가 방법을 스스로 터득하거나, 반복 횟수를 세거나, 복습 시점을 기억할 필요가 없습니다.

또 하나는 듣기에서 멈추지 않고 '말하기'까지 닫힌 고리로 만든다는 점입니다. 많은 앱이 정밀 듣기·따라 말하기까지는 지원해도, "들은 내용을 내 말로 다시 말하는" 복술(復述, retell) 단계는 빠져 있습니다. 입력(듣기)과 출력(말하기)의 비율까지 통계로 보여 주는 건 그래서 흔치 않습니다.

항목흔한 학습앱Echo Loop
학습 리듬사용자가 직접 통제앱이 단계를 자동 진행
듣기→말하기대개 듣기·따라읽기까지정밀듣기+따라읽기+복술까지
긴 문장통째로 제시의미 단위로 끊어 제시
복습없거나 수동망각 직전 자동 예약
발음 평가서버형이 많음기기 내(오프라인) 평가
코드비공개오픈소스(AGPL)

여기에 에빙하우스 망각곡선에 맞춘 간격 복습이 들어갑니다. 한 자료를 1회 학습 + 7회 복습으로 나누고, 복습 간격을 6시간 → 1일 → 2일 → 4일 → 7일 → 14일 → 28일로 점점 늘립니다. "막 잊으려는 순간"에 다시 만나게 해 장기 기억으로 굳히는 방식입니다.

진짜 문제
의지력을 갉아먹는 건 '결정' 그 자체

올바른 방법을 알아도 오래 못 가는 이유 — 막상 실행하면 "오늘 어느 단락을, 몇 번 듣고, 언제 자막을 보고, 어느 문장을 반복하고, 며칠 뒤 복습할지"를 매 순간 스스로 판단해야 합니다. 학습을 시작하기도 전에 결정 부담이 마음의 힘을 다 써버립니다.

Echo Loop의 해결
결정을 앱에 떠넘기는 자동 루프

"못 알아듣는 것"이 아니라 "다음에 뭘 할지 모르는 것"이 병목이라고 보고, 그 결정들을 앱이 대신 내립니다. 사용자는 앱을 여는 습관만 지키면 되고, 횟수·복습 타이밍·난이도는 시스템이 추진합니다. "한 자료를 제대로 굴리는 게 100개를 흘려듣는 것보다 낫다"가 슬로건입니다.

3기술 스택 전체 지도

UI·상태·오디오·음성인식·데이터·인프라 한눈에.

전부 Dart 한 언어로 쓰인 Flutter 앱입니다. 네이티브 코드(Swift·Kotlin·C++)는 5% 미만으로, 주로 iOS/macOS 음성 인식 같은 OS 고유 기능을 호출하는 다리 역할만 합니다.

레이어기술 / 버전역할
UI 프레임워크Flutter + Material 3하나의 코드로 5개 플랫폼 UI 렌더링
언어/SDKDart · Flutter SDK 3.9.2+앱 전체 로직
상태 관리Riverpod 2.6 (코드 생성)단방향 데이터 흐름
오디오 재생just_audio · audio_session구간 반복·속도 제어 엔진
음성 합성(TTS)flutter_tts단어·예문 읽어 주기
음성 인식(ASR)sherpa_onnx + iOS/macOS 네이티브따라 말하기 채점 (기기 내)
데이터 저장Drift(SQLite) · shared_preferences학습 진도·수집·캐시
라우팅go_router 17화면 전환·딥링크
알림flutter_local_notifications · timezone간격 복습 리마인더
텍스트 처리subtitle(SRT/VTT) · lemmatizerx자막 파싱·단어 원형화
네트워크dio · cached_network_imageAI 번역/해설 API·이미지 캐시
분석/지표Firebase Analytics · Umeng · PostHog사용 통계(동의 기반)
국제화flutter_localizations + ARB한국어 관점에선 en/zh 2종
개발 도구build_runner · drift_dev · mocktail코드 생성·테스트·정적분석
용어
Flutter / Dart
Flutter는 구글이 만든 UI 프레임워크로, 한 번 짠 코드를 iOS·Android·데스크톱·웹으로 동시에 빌드합니다. Dart는 그 전용 언어입니다. "도면 한 장으로 여러 나라 규격의 집을 짓는" 느낌이라, 작은 팀이 다섯 플랫폼을 동시에 유지할 수 있는 이유가 여기 있습니다.
용어
ASR (Automatic Speech Recognition, 자동 음성 인식)
사람의 말소리를 텍스트로 바꾸는 기술. Echo Loop는 사용자의 따라 말하기를 텍스트로 받아 원문과 맞춰 보고 점수를 매깁니다. sherpa_onnx기기 안에서 처리하므로 인터넷 없이도, 음성을 서버로 보내지 않고도 평가가 됩니다.

4아키텍처 심화 분석

데이터가 어디서 어디로 흐르는가 — 먼저 전체 그림, 그다음 한 줄기 추적.

이 앱은 계층형(layered) + 단방향 데이터 흐름 구조를 따릅니다. 저장소의 CLAUDE.md(개발 규범 문서)에 원칙이 못 박혀 있는데, 핵심은 "화면은 동작만 던지고, 상태 관리자가 상태를 바꾸고, 화면은 그 상태를 보고 다시 그린다"는 한 방향 사이클입니다. UI에 비즈니스 로직을 섞지 않습니다.

① 먼저 전체 그림

┌──────────────────────────────────────────────────────┐ │ Echo Loop (Flutter) │ │ │ │ Screen ──동작(action)──▶ Provider/Notifier │ │ ▲ │ (Riverpod) │ │ │ 상태(state) 구독 │ 호출 │ │ └──────────◀───────────────┤ │ │ ▼ │ │ Service / 편성 로직 │ │ ┌───────────┬──────────┼──────────┬─────────┐ │ │ ▼ ▼ ▼ ▼ ▼ │ │ just_audio flutter_tts sherpa Drift 알림 │ │ (오디오) (TTS) _onnx (SQLite) 스케줄 │ │ (ASR) │ │ │ (iOS/macOS는 다리로) │ │ ▼ │ │ 네이티브 SFSpeechRecognizer │ └──────────────────────────────────────────────────────┘

맨 위 Screen은 조립만 합니다. 버튼을 누르면 Provider/Notifier에게 "이거 해"라고 알리고, Provider가 상태를 바꾸면 화면이 그 상태만 보고 다시 그립니다. 실제 오디오 재생·음성 인식·DB 쓰기 같은 무거운 일은 Service / 편성(orchestration) 로직이 도구들을 불러 처리합니다.

② 흐름 한 줄기 따라가기 — "오디오를 가져와 한 단락을 통관하기까지"

해피 패스(정상 경로) 하나만 손으로 따라가 봅니다. 곁가지(에러·예외)는 일부러 생략합니다.

① 로컬 오디오 + 자막 가져오기 file_picker → subtitle 파서가 SRT/VTT를 문장 리스트로 │ (자막이 없으면 AI 자동 전사로 생성) ▼ ② 블라인드 리스닝 / 정밀 듣기 just_audio가 문장 구간을 재생, 재생 위치 ──▶ 지금 어느 문장? SentenceTracker가 '이진 탐색'으로 현재 문장 즉시 계산 ▼ ③ 따라 말하기(셰도잉) + 채점 마이크 입력 ──▶ sherpa_onnx(또는 iOS 네이티브)가 텍스트로 인식 결과를 원문과 정렬 → 맞은 단어 하이라이트 + 등급 ▼ ④ 복술(내 말로 다시 말하기) → 학습 데이터 기록 학습 시간 · 입력/출력 비율 · 고유 단어수 ──▶ Drift(SQLite) 저장 ▼ ⑤ 통관 → 다음 복습 자동 예약 에빙하우스 간격(6h→28d)으로 다음 일정 계산 flutter_local_notifications + timezone이 리마인더 예약

여기서 초보자가 눈여겨볼 "이 프로젝트의 정상적인 모양새"가 둘 있습니다. 첫째, 복잡한 흐름은 화면이 아니라 순수 Dart 클래스로 빼서 테스트 가능하게 만든다는 것(예: SentenceTracker, BookmarkManager). 둘째, 비동기 작업에는 항상 경쟁 상태(race condition) 방어가 들어간다는 것 — 이게 이 레포의 가장 값진 학습 포인트입니다(6장에서 자세히).

설계 패턴
단방향 데이터 흐름 (Unidirectional Data Flow)
상태를 바꾸는 입구를 한 곳으로 모으는 방식. "UI는 요청만, 상태 변경은 정해진 메서드로만, UI는 결과를 구독해 다시 그림"의 일방통행입니다. 양방향으로 아무 데서나 상태를 고치면 누가 언제 바꿨는지 추적이 안 되는데, 일방통행이면 버그의 출처가 좁혀집니다.
설계 패턴
제너레이션 카운터 (Generation Counter)
새 작업을 시작할 때마다 번호(세대)를 1 올리고, 비동기 콜백이 돌아오면 "내가 시작될 때의 번호"와 현재 번호가 같은지 확인해 다르면 결과를 버리는 패턴. 음성 인식처럼 콜백이 늦게 도착하는 작업에서 옛 작업의 콜백이 새 작업을 망가뜨리는 사고를 막습니다. "주문표에 번호를 매겨, 번호가 안 맞으면 그 주문은 무시"하는 것과 같습니다.

5디렉토리 구조 해부

어떤 폴더가 무슨 일을 하나.

코드의 본체는 lib/ 안의 Dart 파일들(약 376개 — 비생성 소스 기준 / 432개 이상 — .g.dart 포함)이고, android/ios/macos/windows/linux/는 각 플랫폼의 네이티브 껍데기입니다. 구조는 "페이지 외형이 아니라 역할(기능 도메인)별로 자른다"는 규범을 따릅니다.

Echo-Loop/ ├── lib/ ★ 앱 본체 (Dart) │ ├── l10n/ 다국어 번역 (ARB 포맷, en/zh) │ ├── models/ 데이터 모델 (오디오·문장·수집 등) │ ├── providers/ Riverpod 상태 관리 │ │ ├── audio_engine/ 오디오 재생 저수준 제어 │ │ └── listening_practice/ 듣기 연습 비즈니스 로직 │ │ ├── sentence_tracker.dart 현재 문장(이진 탐색) │ │ └── bookmark_manager.dart 책갈피 관리 │ ├── screens/ 페이지 (조립 담당) │ ├── services/ 서비스층(저장·자막파서·TTS) │ ├── widgets/ 재사용 UI 컴포넌트 │ ├── database/ Drift(SQLite) ★ │ │ ├── app_database.dart DB 정의 (+ .g.dart 생성물) │ │ ├── daos/ 데이터 접근 객체(13개 + 자동생성 13개 = 26파일) │ │ ├── tables/ 테이블 스키마(15개) │ │ ├── migration/ 버전 간 마이그레이션 │ │ └── enums.dart │ ├── features/ 기능 단위 모듈 │ │ ├── audio_import/ 오디오 가져오기 │ │ ├── auth/ 인증 │ │ ├── official_collections/ 공식 학습 합집 │ │ ├── onboarding_survey/ 온보딩 설문 │ │ ├── podcast/ 팟캐스트 │ │ ├── subtitle_editor/ 자막 편집 │ │ └── usage/ 사용 통계 │ ├── analytics/ 지표 수집(채널·동의·지역) │ ├── config/ api_config · build_config │ └── data/ demo_content.dart (체험용) ├── ios/ Runner/AppDelegate.swift ← 네이티브 음성인식 다리 ├── android/ · macos/ · windows/ · linux/ 각 플랫폼 껍데기 ├── integration_test/ 端到端(E2E) 테스트 ├── test/ 단위·위젯 테스트 ├── scripts/ lib/build_number.sh · release_*.sh ├── .github/workflows/ CI (release.yml 등) ├── pubspec.yaml 의존성·버전 (앱의 설계도) ├── PLAN.md · TASKS.md 파일 주도 개발 (계획·할일) └── CLAUDE.md · AGENTS.md AI 협업 작업 규범
위치역할
lib/providers/상태·비즈니스 로직의 심장. audio_engine(저수준 재생)과 listening_practice(연습 흐름)로 나뉨.
lib/database/Drift 기반 SQLite. daos(읽기/쓰기)·tables(스키마)·migration(버전 업)으로 분리.
lib/services/저장·자막 파싱·TTS 등 외부 자원을 다루는 서비스층.
lib/analytics/Firebase·Umeng·PostHog 채널 + 동의 관리(consent_manager)·지역 인터셉터.
ios/Runner/AppDelegate.swiftDart에서 호출하는 iOS 네이티브 음성인식 핸들러(IOSSpeechPracticeHandler).
scripts/릴리스 자동화. scripts/lib/build_number.sh가 커밋 수로 versionCode를 생성.
PLAN.md · TASKS.md"채팅 기억이 아니라 파일에 결정을 적는다"는 파일 주도 개발의 산물.

6학습 포인트 (기술별)

이 레포에서 배울 만한 것 + 어디를 보면 되는지.

이 레포의 가장 큰 매력은 "예쁜 앱"이 아니라 실전 Flutter 앱이 부딪히는 어려운 문제들과 그 해법이 문서로 남아 있다는 점입니다. 특히 비동기 경쟁 상태 3건은 교과서에 안 나오는 실무 함정입니다.

포인트 1 — 상태 관리

Riverpod + 코드 생성

상태 관리는 Riverpod(라이버팟)으로, 그것도 코드 생성 방식을 씁니다. Provider를 손으로 다 적는 대신 어노테이션을 달면 build_runner가 나머지(.g.dart)를 자동으로 만들어 줍니다. 어디를 보면 되나: lib/providers/ 전체와, "Provider는 컴포넌트 개수가 아니라 기능 도메인으로 나눈다"는 CLAUDE.md 규범.

실행 순서는 이렇습니다:

# Provider 코드를 고친 뒤 매번
dart run build_runner build   # .g.dart 재생성
포인트 2 — 로컬 DB

Drift로 타입 안전한 SQLite 다루기

Drift(드리프트)는 SQLite를 Dart 타입으로 감싸 주는 ORM입니다. 문자열 SQL을 손으로 쓰다 오타로 깨지는 대신, 컴파일 단계에서 잘못을 잡아 줍니다. tables/(스키마 15개) · daos/(읽기·쓰기 26개) · migration/(버전 업)으로 깔끔히 나뉘어 있어, "스키마를 어떻게 진화시키나"를 배우기 좋은 표본입니다.

용어
ORM (Object-Relational Mapping, 객체-관계 매핑)
DB의 표(행·열)를 프로그래밍 언어의 객체로 자동 변환해 주는 다리. SQL을 직접 안 써도 객체를 다루듯 데이터를 읽고 씁니다. "외국어(SQL) 통역사를 한 명 두는 것"과 비슷합니다.
포인트 3 — 온디바이스 AI

sherpa_onnx로 '기기 안에서' 음성 인식

따라 말하기 채점에 서버를 쓰지 않고 sherpa_onnx로 기기 안에서 음성을 텍스트로 바꿉니다. ONNX(Open Neural Network Exchange, 표준 신경망 교환 포맷) 런타임으로 학습된 음성 모델을 폰에서 직접 돌리는 방식입니다. 인터넷이 없어도, 목소리를 외부로 보내지 않아도 평가가 됩니다(프라이버시·오프라인 둘 다 이득). iOS/macOS에서는 OS가 가진 SFSpeechRecognizer메서드 채널(Method Channel)로 호출해 더 매끄럽게 처리합니다.

용어
Method Channel (메서드 채널)
Dart 코드와 네이티브(Swift/Kotlin) 코드가 메시지를 주고받는 통로. Flutter가 직접 못 만지는 OS 기능(여기선 음성 인식)을 네이티브에 부탁할 때 씁니다. "프런트 데스크에 쪽지를 보내 뒷방 직원에게 일을 시키는" 창구입니다.
포인트 4 — 실무의 핵심 ★

비동기 경쟁 상태(race condition) 방어 3종

CLAUDE.md의 "트러블슈팅" 절에 실제로 겪고 고친 함정 3건이 박제돼 있습니다. 비동기 콜백이 늦게 도착해 새 작업을 망치는, 초급 강의엔 안 나오는 진짜 버그들입니다.

① iOS 음성 인식 — 옛 콜백이 새 세션을 파괴
SFSpeechRecognitionTask.cancel()의 콜백이 비동기라, 새 녹음이 시작된 뒤 옛 콜백이 도착해 새 세션 상태를 망가뜨렸습니다(녹음이 멈추고, 새 인식 작업이 취소됨). 해법은 제너레이션 카운터 — 세션마다 번호를 올리고, 콜백에서 번호가 안 맞으면 그냥 버립니다.

② flutter_tts — 빠른 stop→speak가 완료 신호를 헷갈림
플래시카드를 빠르게 넘길 때 TTS가 3~9밀리초 만에 "끝났다"고 거짓 신고했습니다. stop()의 취소 콜백이 다음 speak()의 완료를 잘못 마감한 탓. 해법은 라이브러리 내부 완료 신호에 기대지 않고 직접 Completer + 시작 핸들러 플래그로 관리하는 것.

③ Android versionCode는 전역 단조 증가여야 함
Android는 버전 이름이 아니라 정수 versionCode만 보고 업/다운그레이드를 판단합니다. minor 버전을 올리며 빌드번호를 1로 되돌렸더니 4→1로 역행해 설치가 막혔습니다. 해법: 태그는 순수 SemVer로, versionCode는 CI가 git rev-list --count(커밋 수)로 자동 생성.

왜 어려운가
"성공했을 때"만 생각하면 못 보는 버그

세 사례의 공통점은 모두 "타이밍"입니다. 한 번 천천히 누르면 멀쩡하고, 빠르게 연타하거나 세션을 급히 바꿀 때만 터집니다. 그래서 재현이 어렵고, 로컬에선 안 보이다가 사용자 손에서만 터집니다.

배울 점
비동기엔 '신분 확인'을 붙여라

모든 비동기 콜백에 "내가 시작될 때의 신분(번호/토큰/세션ID)"을 들려 보내고, 돌아왔을 때 신분이 유효한지 검사해 옛것은 버리는 습관. Echo Loop는 이 패턴을 음성 인식·TTS·릴리스 번호 세 군데에 일관되게 적용합니다.

포인트 5 — 알고리즘

이진 탐색으로 '지금 어느 문장?' 즉시 찾기

오디오가 흐르는 동안 매 프레임 "현재 재생 위치가 몇 번째 문장인지"를 알아내야 합니다. 문장이 수백 개면 처음부터 하나씩 비교하는 건 낭비라, sentence_tracker.dart이진 탐색(binary search)으로 단번에 좁힙니다. "사전에서 단어 찾을 때 가운데를 펼쳐 앞/뒤를 가르는" 그 방법입니다 — 500문장도 약 9번 비교로 끝납니다.

포인트 6 — 학습 과학 → 코드

에빙하우스 간격 복습을 알림으로 굽기

학습 과학(망각곡선)을 실제 스케줄러로 옮긴 사례입니다. 통관 시점에 다음 복습 시각(6h→1d→2d→4d→7d→14d→28d)을 계산해 flutter_local_notifications + timezone으로 예약합니다. 시간대(timezone)를 명시적으로 다루는 것도 포인트 — 사용자가 비행기로 시차를 넘어도 복습 시각이 어긋나지 않도록.

7시스템 요구사항

개발자로 돌릴 때 / 사용자로 쓸 때 각각 무엇이 필요한가.

순수 소프트웨어 앱이라 특별한 하드웨어는 없지만, 두 입장에서 필요 조건이 다릅니다.

구분항목요구사항
개발Flutter SDK3.9.2 이상 (Dart 3.9.2+)
개발실행 대상iOS·Android 시뮬레이터/실기기, 또는 macOS·Windows·Linux 데스크톱 환경
개발코드 생성build_runner(Riverpod·Drift 코드 생성 필수)
사용모바일iOS(App Store) / Android(APK 다운로드)
사용데스크톱macOS 개발 중 · Windows 계획 · Web 계획 없음
사용오프라인핵심 학습·기기 내 음성 평가는 오프라인 가능
사용네트워크AI 번역·문장 해설 등 일부 기능은 API 연결 필요

빌드 시 API 주소를 주입하는 방식이라(--dart-define), 개발/운영 서버를 빌드 한 줄로 바꿀 수 있습니다:

flutter run -d <ios|android|macos>          # 대상 지정 실행
flutter build apk \
  --dart-define=API_BASE_URL=https://www.echo-loop.top

8직접 해볼 수 있는 실습 과제

난이도별로 손에 익히는 단계.

실습 1

로컬에 띄우기 난이도 ★☆☆ 입문

클론 → 의존성 설치 → 코드 생성 → 실행까지. "복사만 하면 OK"입니다.

git clone https://github.com/echo-loop/Echo-Loop.git
cd Echo-Loop
flutter pub get            # 의존성 내려받기
dart run build_runner build  # Riverpod·Drift 코드 생성
flutter run -d macos       # 또는 ios / android

실행되면 데모 콘텐츠(lib/data/demo_content.dart)로 학습 루프가 어떻게 도는지 먼저 체험해 보세요.

실습 2

간격 복습 일정 바꿔 보기 난이도 ★★☆ 중급

복습 간격(6h→28d)을 정의한 곳을 찾아 한 단계를 짧게(예: 6h→10분) 바꿔 보고, 통관 후 알림이 그 시각에 예약되는지 확인합니다. 학습 과학이 코드의 어느 숫자로 박혀 있는지 감을 잡는 연습입니다.

실습 3

Drift에 컬럼 추가 + 마이그레이션 난이도 ★★☆ 중급

lib/database/tables/에 컬럼을 하나 더하고, migration/에 버전 업 규칙을 적은 뒤 build_runner를 돌려 봅니다. "스키마를 깨지 않고 진화시키는" 실무 감각을 얻습니다.

실습 4

제너레이션 카운터 재현하기 난이도 ★★★ 고급

음성 인식이나 TTS를 빠르게 stop→start 연타해 경쟁 상태를 일부러 재현해 보고, 제너레이션 카운터(또는 토큰) 검사를 넣어 옛 콜백을 버리도록 고쳐 봅니다. ios/Runner/AppDelegate.swiftlib/services/tts_service.dart가 정답지입니다.

실습 5

새 언어(ARB) 추가 난이도 ★★★ 고급

lib/l10n/에 한국어 ARB 파일을 추가해 메뉴를 우리말로 바꿔 봅니다. 템플릿(app_en.arb)의 키를 그대로 두고 값만 번역 → flutter gen-l10n으로 생성하는 국제화 흐름을 체험합니다.

9관련 기술 심화 학습 로드맵

한 주씩 따라가는 계획.

주차주제학습 자료
1주차Flutter·Dart 기초, 위젯·레이아웃, 로컬 실행flutter.dev 공식 튜토리얼 · 이 레포 클론·실행
2주차Riverpod 상태 관리 + 코드 생성riverpod.dev · lib/providers/ 정독
3주차Drift로 SQLite·DAO·마이그레이션drift.simonbinder.eu · lib/database/
4주차오디오(just_audio)·TTS·자막 파싱각 pub.dev 문서 · services/·audio_engine/
5주차온디바이스 ASR + 메서드 채널(네이티브 연동)sherpa_onnx 문서 · AppDelegate.swift
6주차비동기 경쟁 상태·테스트·CI 릴리스CLAUDE.md 트러블슈팅 · test/ · scripts/

10핵심 키워드 사전

본문에 나온 용어 빠른 참조.

용어의미
Flutter구글의 크로스플랫폼 UI 프레임워크. 한 코드로 iOS·Android·데스크톱·웹 빌드.
DartFlutter 전용 프로그래밍 언어. 이 레포 코드의 95%.
RiverpodFlutter 상태 관리 라이브러리. 코드 생성으로 보일러플레이트를 줄임.
DriftSQLite를 Dart 타입으로 감싸는 ORM. 컴파일 단계에서 SQL 오류를 잡음.
ORM객체-관계 매핑. DB 표를 객체로 자동 변환해 주는 다리.
ASR자동 음성 인식. 말소리 → 텍스트. 따라 말하기 채점의 핵심.
sherpa_onnxONNX 런타임 기반 온디바이스(기기 내) 음성 인식 라이브러리.
ONNX표준 신경망 교환 포맷. 학습된 AI 모델을 폰에서 직접 실행.
TTS음성 합성. 텍스트 → 말소리. 단어·예문 읽어 주기에 사용.
Method ChannelDart ↔ 네이티브(Swift/Kotlin) 코드 간 메시지 통로.
제너레이션 카운터비동기 작업마다 번호를 매겨 옛 콜백을 버리는 경쟁 상태 방어 패턴.
경쟁 상태여러 비동기 작업의 타이밍이 엉켜 생기는 버그. 연타·세션 전환 때 터짐.
이진 탐색정렬된 데이터를 절반씩 좁혀 찾는 알고리즘. 현재 문장 위치 계산에 사용.
에빙하우스 망각곡선시간이 지날수록 기억이 사라지는 곡선. 간격 복습 일정의 근거.
셰도잉원어민 음성을 거의 동시에 따라 말하는 훈련.
복술(復述)들은 내용을 자기 말로 다시 말하는 출력 단계.
ARBApplication Resource Bundle. Flutter 다국어 번역 파일 포맷.
AGPL-3.0강한 카피레프트 오픈소스 라이선스. 네트워크로 제공해도 소스 공개 의무.
go_routerFlutter 선언형 라우팅 라이브러리. 화면 전환·딥링크 처리.
build_runner어노테이션을 보고 .g.dart 같은 코드를 자동 생성하는 도구.

11참고 링크