"๋ ์ ํํ๊ณ ์์ฐ์ค๋ฌ์ด ๋น์ฆ๋์ค ์คํผ์น, AI๋ก ๋ง๋น์ ๋ํ๋ค"
'๋ง๋น(Malbit)'์ ์ง๋ฌด๋ณ ๋น์ฆ๋์ค ์ํฉ๊ทน ์๋ฎฌ๋ ์ด์ , AI ์ค์๊ฐ ๋ฌธ์ฅ ๊ต์ (Remaster), ์ํฉ๋ณ ๋ฐํ ์ถ์ฒ, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ์์ฑ ํ๋กํ ํ์ต์ ํตํด ์ง์ฅ ๋ฐ ๋น์ฆ๋์ค ํ์ฅ์์์ ์์ฌ์ํต ๋ฅ๋ ฅ์ ๊ทน๋ํํ ์ ์๋๋ก ์ง์ํ๋ AI ๊ธฐ๋ฐ ์คํผ์น ํธ๋ ์ด๋ ๋ชจ๋ฐ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋ค.
๋ณธ ์ ํ๋ฆฌ์ผ์ด์ ์ Feature-First MVVM ๊ตฌ์กฐ๋ฅผ ์ฑํํ์ฌ ๊ฐ ๋ชจ๋(Feature)์ด ๋ ๋ฆฝ์ ์ธ Model, Screen(View), Service(API Client)๋ฅผ ๊ฐ์ง๋๋ค. ์ด๋ฅผ ํตํด ๊ธฐ๋ฅ๋ณ ๋ชจ๋ํ์ ๊ฒฐํฉ๋ ์ต์ํ๋ฅผ ๋ฌ์ฑํ์ต๋๋ค.
sequenceDiagram
autonumber
actor User as ์ฌ์ฉ์
participant App as Flutter Client (Remaster)
participant Storage as FlutterSecureStorage
participant Serv as API Gateway (Remaster)
participant TTS as Flutter TTS Engine
User->>App: ๋
น์ ๋ฒํผ ํด๋ฆญ (๋ง์ดํฌ ๊ถํ ํ๋)
App->>App: WAV 16kHz, Mono, 128kbps ๋ก์ปฌ ๋
น์ ๊ฐ์
User->>App: ์์ฑ ์
๋ ฅ ์ข
๋ฃ (๋
น์ ์ค์ง)
App->>Storage: Access Token ๋ก๋
App->>Serv: POST /api/remaster (Multipart: WAV File & preferred_tone)
Note over Serv: Backend: Whisper ASR ์ํ<br/>LLM ๊ธฐ๋ฐ ๋น์ฆ๋์ค ํค ๊ต์
Serv-->>App: HTTP 200 OK (original_speech & refined_text ๋ฐํ)
App->>App: ํ๋ฉด UI ์
๋ฐ์ดํธ (๋ค๋ฆฐ ๋๋ก ํ์ ๋ฐ ์ถ์ฒ ๋ฌธ์ฅ ์นด๋ ๋ ๋๋ง)
User->>App: '์ฌ์' ๋ฒํผ ํด๋ฆญ
App->>TTS: refined_text ์ ๋ฌ ๋ฐ ์ฌ์ ์์ฒญ (ko-KR)
TTS-->>User: ๋น์ฆ๋์ค ํค์ผ๋ก ๋ณด์ ๋ ์์ฑ ์ถ๋ ฅ
sequenceDiagram
autonumber
actor User as ์ฌ์ฉ์
participant App as Flutter Client (Order/Report/Call Screen)
participant API as Training API
participant STT as On-Device SpeechToText
App->>API: POST /api/training/start (categoryId)
API-->>App: Session ID ๋ฐํ (์ธ์
์์ ๋ฐ ํธ๋ํน ๊ฐ์)
loop ๊ฐ ๋จ๊ณ ์๋๋ฆฌ์ค ์งํ (1 ~ N ๋จ๊ณ)
App->>User: ํ์ฌ ์ํฉ ๋ฌ์ฌ & ๊ณ ๊ฐ/์๋๋ฐฉ ๋์ฌ ๋ ๋๋ง
User->>App: '์ง์ ๋งํด๋ณด๊ธฐ' ํญ (STT ๋
น์ ๊ฐ์)
User->>STT: ์์ฑ ๋ฐํ
STT-->>App: ์ค์๊ฐ ํ๊ธ ํ
์คํธ ๋ณํ (ko_KR)
User->>App: ๋ฐํ ์๋ฃ
App->>App: Dice Coefficient ์๊ณ ๋ฆฌ์ฆ์ผ๋ก ๋ฐ์/ํ
์คํธ ์ ์ฌ๋(F1) ์ค์๊ฐ ์ฐ์ฐ
App->>App: ๋จ๊ณ๋ณ ์ฑ์ ๋ฐ์ดํฐ ๋ก์ปฌ ์บ์ฑ ๋ฐ ๋ค์ ๋จ๊ณ ์ ํ
end
App->>API: PATCH /api/users/statistics/roleplay (ํต๊ณ ๊ฐ์ ์ฆ๋ถ ๋ฐ์)
App->>API: POST /api/training/finish/{sessionId} (์ธ์
์ข
๋ฃ)
API-->>App: ์ ์ฒด ์ฑ์ (Total Score, Average Accuracy, Evaluation) ๋ฐํ
App->>User: ์ต์ข
๋ถ์ ๋ ํฌํธ ํ์
์ ๊ณต (์ ํ๋๋ณ ์ปค์คํ
ํผ๋๋ฐฑ ๋ฆฌ์คํธ ์ถ๋ ฅ)
- ์์ฑ ์์ง ์คํ: ํ๋์จ์ด ๋ง์ดํฌ ๊ถํ์ ์์ ํ๊ฒ ํ๋ํ๊ณ ,
recordํจํค์ง๋ฅผ ์ด์ฉํด **WAV ์ปจํ ์ด๋ ํฌ๋งท(16,000Hz Sample Rate, ๋จ์ผ ์ฑ๋ Mono, 128,000bps Bit Rate)**์ ์ ์ง์ฐ ๊ณ ์์ง ์์ฑ์ ์บก์ฒํฉ๋๋ค. - API ํตํฉ:
http.MultipartRequest๋ฅผ ํ์ฉํ์ฌ ์คํธ๋ฆฌ๋ฐ ๋ฐ์ดํธ ๋ฐฐ์ด๋ก ์์ฑ ํ์ผ์ ์บก์ฒํ๊ณ , ์ฌ์ฉ์๊ฐ ์ ํํ ๋น์ฆ๋์ค ๋ง์จ(Gentle ๋ฑ) ํ๋์ ํจ๊ปMultipartFile๋ก ์ ์กํฉ๋๋ค. - ์ข
๋จ๊ฐ ํผ๋๋ฐฑ ๋ฃจํ: ๋ฐฑ์๋๊ฐ ์ ์กํ Whisper ASR ํ
์คํธ(
original_speech)์ LLM ๋ฌธ์ฅ ๊ต์ ๋ณธ(refined_text)์ ๋ฐ์ ์นด๋ ํํ๋ก ์๊ฐํํฉ๋๋ค. ๋๋ฐ์ด์ค์ TTS ์์ง(flutter_tts)๊ณผ ๋ค์ด๋ ํธ๋ก ๊ฒฐํฉ๋์ด ๋ฐํ ๊ฐ์ด๋๋ฅผ ๊ธฐ๊ณ์ ํํ๋ก ์ ํํ ์ ๋ฌํ๋ฉฐ, ์๋ ฅ์ด ๋ฎ์ ์ฌ์ฉ์๋ ์ง์ค ํ๋ จ์ ์ํด ํ ์คํธ๋ฅผ ํฌ๊ฒ ๋์์ฃผ๋ExpandTextScreens๋ทฐ ๋ชจ๋๋ฅผ ์ง์ํฉ๋๋ค.
์ค์ ํ์ฅ์์ ๋ง๋ฅ๋จ๋ฆฌ๋ ์์ฌ์ํต ์๋๋ฆฌ์ค๋ฅผ ๋จ๊ณ๋ณ ์ํธ์์ฉ ๋ฐฉ์์ผ๋ก ์ฐ์ตํฉ๋๋ค.
- 4๋ ๋น์ฆ๋์ค ํ
ํ๋ฆฟ:
- ์ฃผ๋ฌธ ๋ฐ๊ธฐ (Take Orders โ): ์นดํ/๋งค์ฅ ๋ฑ์์ ๊ณ ๊ฐ์ ์๋ฃ ์ ํ, ์ต์ ๋ณ๊ฒฝ, ํ์ ๋์ฒ, ๊ฒฐ์ ์๋ด ๋ฐ ์ฃผ๋ฌธ ์ต์ข ํ์ธ.
- ์ํ ์๋ดํ๊ธฐ (Product Guide ๐): ๊ณ ๊ฐ ๋ง์ด, ์ ํ ์ ๋ณด ์ ๋ฌ, ๊ฐ๊ฒฉ/ํํ ๊ณ ์ง, ์ถ๊ฐ ์ถ์ฒ ๋ฑ ๋ฅ๋์ ์๋ด ์ธ์ผ์ฆ ์คํผ์น.
- ์ ๋ฌด ๋ณด๊ณ ํ๊ธฐ (Work Report ๐): ํ๋ก์ ํธ ์งํ ์ฌํญ ๋ณด๊ณ , ๋ฌธ์ ์ํฉ ๊ณต์ ๋ฐ ํ์ ํผ๋๋ฐฑ ์ ๋ฌ.
- ์ ํ ๋ฐ๊ธฐ (Call Support ๐): ํ์ฌ ์ ํ ์๋์ ๊ธฐ๋ณธ ํฌ๋งทํ , ๋ฌธ์ ์ฌํญ ๊ฒฝ์ฒญ ๋ฐ ๋ด๋น์ ์ ๋ฌ ๋ฉ์ปค๋์ฆ ํ๋ จ.
- ์ธ์
๋ผ์ดํ์ฌ์ดํด ํธ๋ํน: ์์ ์ ๋ฐฑ์๋
TrainingApi.startSessionํธ์ถ์ ํตํด ๋ฐ๊ธ๋ฐ์sessionId๋ฅผ ์ปจํ ์คํธ ๋ด์์ ์ ์งํฉ๋๋ค. ์๋ฎฌ๋ ์ด์ ์ด ์ข ๋ฃ๋๋ฉดfinishSession์์ฒญ์ ๋ ๋ ค ์ ์ฒด ํต๊ณ๋ฅผ ๋ถ์ํ๊ณ ์๋ฒ์ ๋ถ์ ์ฑ๊ณผ ์งํ(์ข ํฉ ์ ์, ํ๊ท ์ ํ๋, ์ ์ฑ ํ๊ฐ ํผ๋๋ฐฑ)๋ฅผ ์์งํฉ๋๋ค.
- ์์ฑ ๊ธฐ๋ฐ ์ํฉ ํ์ง (Speech-to-Text): ์ฌ์ฉ์๊ฐ ์ฒํ ์ํฉ์ ์
์ผ๋ก ๋งํ๋ฉด (
speech_to_text), ์จ๋๋ฐ์ด์ค ํ๊ธ ์์ฑ ์์ง์ ํตํด ์ํฉ์ ์ธ์งํฉ๋๋ค. - ์ค๋งํธ ์นจ๋ฌต ํธ๋ฆฌ๊ฑฐ (Silence Detection): ์ฌ์ฉ์์ ๋ง์ด ๋๊ธฐ๋ฉด
Timer๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋ํ๋ ์นจ๋ฌต ๊ฐ์ง ๋ก์ง์ด ๊ฐ์ ํ์ฌ, 3์ด๊ฐ ์ ๋ ฅ์ด ๊ฐ์ง๋์ง ์์ ์ ์๋์ผ๋ก ๋ถ์ API(/api/recommendations/suggest)๋ก ์ง์๋ฅผ ๋ ๋ ค ์ํฉ ๊ทน๋ณต์ ์ํ ๋ชจ๋ฒ ๋ฐํ ์คํฌ๋ฆฝํธ์ ํ์ ์ ๊ณตํฉ๋๋ค. - ๋ค์ํ ํ๋ฆฌ์ : ๋น๋ฒํ ์๊ตฌ๋๋ ์ค์ ์ฌ๊ณผ, ํ์ ์ค ๋ฐ์ธ, ์ฃผ๋ฌธ/๊ฒฐ์ , ์ฒซ ์ธ์ฌ ๋ฑ์ ์ํฉ์ ์นดํ ๊ณ ๋ฆฌ ํ๊ทธ๋ก ๋น ๋ฅด๊ฒ ํ์ํ ์ ์์ต๋๋ค.
- ์์ฑ ์ฌ๋ฑ๋ก ํ์ดํ๋ผ์ธ: ํฐํธ ๋ฐ ๋ชจ์์ด ๊ท ํ ์กํ 6๊ฐ์ ํ์ค ๋์กฐ์ฉ ํ๊ตญ์ด ๋ฌธ์ฅ("๋๋ ์ค๋ ๊ธฐ์ฐจ๋ฅผ ํ๊ณ ...", "๋ฌ์ฝคํ ๋นต๊ณผ ๋ฐ๋ปํ...")์ ์ฌ์ฉ์์๊ฒ ์์ฐจ ์ ๊ณตํฉ๋๋ค.
- AI ํ๋ จ ๋ค์ค ํ์ผ ์ ์ก: ๊ฐ ๋ฌธ์ฅ๋ณ ๋
น์ ๋ฐ์ดํฐ๋ฅผ ๋ก์ปฌ ์์ ํด๋(
.wavํฌ๋งท)์ ์ ์ฅํ๋ค๊ฐ, ์ฌ์ฉ์๊ฐ ์๋ฃ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋จ์ผ Multipart Request์voiceFiles์คํธ๋ฆผ ๋ฆฌ์คํธ์ ์ค์ด/api/users/voice/re-register๋ก ์ผ๊ด ์ ์กํจ์ผ๋ก์จ ๋ฐฑ์๋ ์ธก ๊ฐ์ธํ AI ๋ณด์ด์ค ๋ชจ๋ธ์ ํ๋ํ๊ธฐ ์ํ ๊ธฐ์ด ๋ฐ์ดํฐ๋ฅผ ํ์ฑํฉ๋๋ค.
- ๊ณ ๊ธ ์บ๋ฆฐ๋ ์ปค์คํฐ๋ง์ด์ง:
table_calendar๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐํ์ผ๋ก, ๊ตญ๋ฌธ ๋ก์ผ์ผ ์ ์ฉ์ ๋ฌผ๋ก ํ ์์ผ(ํ๋์), ์ผ์์ผ/๊ณตํด์ผ(๋นจ๊ฐ์) ํ ์คํธ ์คํ์ผ์ ๋ถ๋ฆฌํ ์๋ คํ ์ปค์คํ UI๋ฅผ ๊ตฌ์ถํ์ต๋๋ค. - ๊ณตํด์ผ API ๊ฒฐํฉ: ๋ก์ปฌ ๊ณตํด์ผ ์ ๋ณด ์กฐํ ์๋น์ค(
HolidayService)๋ฅผ ๊ตฌ์ถํด ๊ตญ๊ฒฝ์ผ/๊ณตํด์ผ ์ ๋ณด ๋ฐ์ดํฐ๋ฅผ ์ธ๋ถ ๊ณต๊ณต ๋ฐ์ดํฐ API ํน์ ๋ฐฑ์๋๋ฅผ ๊ฑฐ์ณ ๋ฐ์์ ๋ฌ๋ ฅ ์์ ๊ธฐ๋ ์ผ ๋ง์ปค ๋ฐ ๊ธฐ๋ ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค. - Task/Event CRUD: ์ผ์ ๋ฑ๋ก(
manual), ์์ , ์ญ์ ๋ฐ ์๋ฃ ์ฌ๋ถ ํ ๊ธ API(/api/calendar/{taskId}/completion)๋ฅผ ์๋ฒฝํ ์ฐ๋ํด ์ผ์ผ ์ ๋ฌด ๋ฐ ํ๋ จ ์ค์ ์ ๋ฌ๋ ฅ์ผ๋ก ํ๋์ ํ์ ํ๋๋ก ์ง์ํฉ๋๋ค.
์ฌ์ฉ์๊ฐ ์ง๋ฌด ์๋๋ฆฌ์ค์์ ์ ์ํ ํํธ ๋ฌธ์ฅ(Ideal Script)์ ์ผ๋ง๋ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐ์ํ๊ณ ์๋ฏธ๋ฅผ ๋ง์ท๋์ง ์จ๋๋ฐ์ด์ค์์ 1์ฐจ ํ๊ฐํ๊ธฐ ์ํด, ํํ์ ๋ฐ ๋จ์ด ํ ํฐ์ ๊ต์งํฉ ๋น์จ์ ์ธก์ ํ๋ Dice Coefficient (F1-Score) ๊ธฐ๋ฐ ์ ์ฌ๋ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํฉ๋๋ค.
๋จ์ด์ ์งํฉ
์ด ๊ณต์์ ์ ๋ฐ๋(Precision)์ ์ฌํ์จ(Recall)์ ์กฐํ ํ๊ท ์ธ F1-Score์ ์ํ์ ์ผ๋ก ์์ ํ ๋์ผํฉ๋๋ค.
-
$\text{Recall} = \frac{|X \cap Y|}{|Y|}$ (ํํธ ๋ฌธ์ฅ์์ ์ฌ์ฉ์๊ฐ ๋น ๋จ๋ฆฌ์ง ์๊ณ ๋งํ ๋จ์ด ๋น์จ) -
$\text{Precision} = \frac{|X \cap Y|}{|X|}$ (์ฌ์ฉ์๊ฐ ๋ฑ์ ๋ง ์ค์์ ํํธ ๋ฌธ์ฅ๊ณผ ๋งค์นญ๋๋ ๋น์จ) $\text{F1-Score} = \frac{2 \times \text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}} = \frac{2 \times |X \cap Y|}{|X| + |Y|}$
double _similarity(String input, String hint) {
if (input.trim().isEmpty) return 0.0;
// 1. ํ๊ตญ์ด ์๋ชจ, ์์ด ์ํ๋ฒณ, ์ซ์, ๊ณต๋ฐฑ์ ์ ์ธํ ๋ฌธ์ฅ๋ถํธ ๋ฐ ํน์๋ฌธ์ ์ ๊ฑฐ
String clean(String s) => s
.replaceAll(RegExp(r'[^\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F\s\w]'), '')
.trim();
// 2. ๊ณต๋ฐฑ ๊ธฐ์ค ํ ํฐํ ๋ฐ ๊ณ ์ ๋จ์ด ์ธํธ(Set)ํ ์ํ
final inputWords = clean(input).split(RegExp(r'\s+')).where((w) => w.isNotEmpty).toSet();
final hintWords = clean(hint).split(RegExp(r'\s+')).where((w) => w.isNotEmpty).toSet();
if (hintWords.isEmpty || inputWords.isEmpty) return 0.0;
// 3. ๊ต์งํฉ(Intersection) ์ฐ์ฐ
final common = inputWords.intersection(hintWords);
// 4. Recall, Precision ์ฐ์ฐ
final recall = common.length / hintWords.length;
final precision = common.length / inputWords.length;
if (recall + precision == 0) return 0.0;
// 5. ์กฐํํ๊ท (F1-Score)์ ์ต์ข
๊ฒฐ๊ณผ๋ก ๋์ถ
return 2 * recall * precision / (recall + precision);
}| ๋ถ๋ฅ | ๊ธฐ์ ๋ฐ ํจํค์ง | ๋น๊ณ |
|---|---|---|
| SDK & ์ธ์ด | Dart ^3.9.2, Flutter SDK |
๋ค์ค ํ๋ซํผ ํ๊ฒํ ๊ธฐ๋ฐ ๊ตฌ์กฐ |
| ๋ณด์ ์คํ ๋ฆฌ์ง | flutter_secure_storage ^9.0.0 |
Android: EncryptedSharedPreferencesiOS: Keychain ์ฐ๋ ๊ธฐ๋ฐ ํ ํฐ ์ํธํ ์ ์ฅ |
| ๋ก์ปฌ ์ ์ฅ์ | shared_preferences ^2.2.2 |
๋น์ ํ ์ฌ์ฉ์ ๊ฐ๋ฒผ์ด ์ค์ ๊ฐ ์ ์ฅ |
| ์์ฑ ๋ น์ | record ^6.2.0 |
์ค๋์ค ์ธ์ฝ๋ฉ ์ง์ ํจํค์ง (WAV ํฌ๋งท ์ต์ ํ) |
| ์์ฑ ๋ฌธ์ ๋ณํ | speech_to_text ^7.0.0 |
๋ค๋ฐ์ด์ค ๋ค์ดํฐ๋ธ STT ํ๊ธ ํจํค์ง ๋ชจ๋ |
| ๋ฌธ์ ์์ฑ ๋ณํ | flutter_tts ^4.2.2 |
ํผ๋๋ฐฑ ๊ตฌ๋ฌธ ์ฝ์ด์ฃผ๊ธฐ ๊ธฐ๋ฅ |
| ๋คํธ์ํฌ ํต์ | http ^1.2.0, http_parser ^4.1.2 |
๋น๋๊ธฐ JSON REST API ์ฐ๋ ๋ฐ ํ์ผ ์ ๋ก๋ ์ฒ๋ฆฌ |
| ๊ฐํธ ์ธ์ฆ(OAuth) | kakao_flutter_sdk_user, google_sign_in |
์์ ๋ก๊ทธ์ธ ์ง์ ์ฐ๋ ํจํค์ง |
| ๋ ์ง ๋ฐ ์บ๋ฆฐ๋ | table_calendar ^3.2.0, intl ^0.20.2 |
๋ ์ง ํ์งํ(ko_KR) ๋ฐ ์บ๋ฆฐ๋ ๋ ๋๋ฌ |
์ฝ๋๋ ๋ชจ๋ ๊ฐ ๋ ๋ฆฝ์ฑ์ ๋ณด์ฅํ๊ณ ๊ตฌ์กฐ์ ์ ์ฐํจ์ ๊ฐ์ถ๊ธฐ ์ํด ํผ์ฒ ๋จ์๋ก ๋ถ๋ฆฌ๋์ด ๊ด๋ฆฌ๋ฉ๋๋ค.
lib/
โโโ core/
โ โโโ services/
โ โโโ storage.dart # FlutterSecureStorage ๊ณตํต ํฉํ ๋ฆฌ ๊ตฌ์ฑ ์ค์
โ โโโ training_api.dart # ๊ณตํต ํ์ต ์ธ์
(์์/์ข
๋ฃ) ๊ด๋ จ ์ ์ญ ํธ์ถ ํด๋ผ์ด์ธํธ
โโโ features/
โ โโโ auth/
โ โ โโโ screens/ # ๋ก๊ทธ์ธ(Login), ํ์๊ฐ์
(SignUp), ๋น๋ฐ๋ฒํธ ์ฌ์ค์
โ โ โโโ services/ # ์ด๋ฉ์ผ/์์
์ธ์ฆ ์ฒ๋ฆฌ ์๋น์ค (auth_service, social_login_service)
โ โโโ home/
โ โ โโโ models/ # CalendarEvent ๋ชจ๋ธ
โ โ โโโ widgets/ # ์ผ์ ์์ดํ
์์ ฏ, CRUD ๋ฐํฐ์ํธ ๋ค์ด์ผ๋ก๊ทธ
โ โ โโโ services/ # calendar_service (์ผ์ CRUD, ์๋ฃ ํ ๊ธ), holiday_service (๊ณตํด์ผ)
โ โ โโโ screens/ # home_screen (๋ฉ์ธ ๋์๋ณด๋), calendar_screen (TableCalendar ํ๋ฉด)
โ โโโ main_navigation/
โ โ โโโ screens/ # ๋ฉ์ธ ํ๋จ ๋ค๋น๊ฒ์ด์
์
ธ ์ฝํ
์คํธ (MainScreen)
โ โโโ profile/
โ โ โโโ screens/ # ์ฌ์ฉ์ ํ๋กํ ์นด๋ ์กฐํ, ๋น๋ฐ๋ฒํธ/์ด๋ฉ์ผ ๋ณ๊ฒฝ, ์ง๋ฌด ํ๊ฒฝ ์ค์
โ โโโ record/
โ โ โโโ models/ # ํ์๋ก ์ ๋ณด ๋ฐ์ดํฐ ์ ์ก ๊ฐ์ฒด (Log, LogDetail)
โ โ โโโ services/ # log_service (ํ์ ์ผ์ง ์กฐํ, ๊ฐ๋ณ ๋ํ
์ผ ๋ก๋, ๋ฉ๋ชจ ๋ณด์กด API)
โ โ โโโ screens/ # record_screen (์ค๋/ํน์ ๋ ์ง ์
๋ฌด ๊ธฐ๋ก ๋ฆฌ์คํธ), summary_screen (ํ์๋ก ์์ธ ๋ฐ ๋ฉ๋ชจ)
โ โโโ remaster/
โ โ โโโ services/ # remaster_service (์์ฑ ์
๋ก๋ ๋ฐ ๋ณด์ ํ
์คํธ ํ๋)
โ โ โโโ screens/ # remaster_screen (AI ๋ฌธ์ฅ ๊ต์ ๋ฉ์ธ ์ธํฐํ์ด์ค), expand_text_screens (ํฐํธ ์ค์ธ ํ๋ฉด)
โ โโโ roleplay/
โ โ โโโ screens/ # roleplay_list_screen (์ค์๊ฐ ์์ฑ ์๋๊ฐ์ง AI ๋ฐํ ์ถ์ฒ๊ธฐ)
โ โโโ template/
โ โ โโโ screens/ # template_screen (์ง๋ฌด ์ํฉ๊ทน ์ง์
๋ก), 4๋ ์ง๋ฌด ์ํฉ ๋ชจ์ ํ๋ฉด (Order, Product, Report, Call)
โ โโโ voice_settings/
โ โโโ screens/ # voice_register_screen (์ฌ์ฉ์ AI ์์ฑ ํ๋กํ ์ฌ๋ฑ๋ก ์์ง)
โโโ main.dart # ์ฑ์ ๋ถํ
๋ฐ ์ ์ญ ์ํ(์ธ์ฆ ํ ํฐ ๊ธฐ๋ฐ ์ด๊ธฐ ์ง๋ก ์ฒดํฌ, ko_KR ๋ก์ผ์ผ) ์ค์ ์ํธ๋ฆฌํฌ์ธํธ
- Flutter SDK ์ค์น (Dart SDK
3.9.2๋ฐ Flutter3.29.x์ด์ ๊ถ์ฅ) - ๋ฌผ๋ฆฌ ๋ชจ๋ฐ์ผ ๋๋ฐ์ด์ค ๋๋ ์๋ฎฌ๋ ์ดํฐ / ์๋ฎฌ๋ ์ดํฐ ํ๊ฒฝ
- ์นด์นด์ค ๊ฐ๋ฐ์ ์ฝ์ ๋ฐ Google Cloud Console์ ๊ธฐ๊ธฐ ํ๋ซํผ๋ณ ํจํค์ง๋ช
(Android:
com.example.malbit_frontend, iOS BundleID) ๋ฐ OAuth ํค ํด์ ์ฌ์ ๋ฑ๋ก ์๋ฃ ํ์.
-
Repository Clone
git clone https://github.com/your-username/malbit_frontend.git cd malbit_frontend -
Flutter ํจํค์ง ์์กด์ฑ ํด๊ฒฐ
flutter pub get
-
ํ๋ซํผ๋ณ ์ด๊ธฐํ ๊ฒ์ฆ
- Android:
/android/local.properties์ Flutter SDK ๊ฒฝ๋ก๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ์ธํฉ๋๋ค. - iOS: CocoaPods ํ๊ฒฝ ์ค์น ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฐ๋ ํ๋ก์ธ์ค๋ฅผ ์งํํฉ๋๋ค.
cd ios pod install cd ..
- Android:
-
์ ํ๋ฆฌ์ผ์ด์ ๋๋ฒ๊ทธ ๋ชจ๋ ์คํ
flutter run
-
Release ๋น๋ ๋ฐฐํฌ๋ณธ ์์ฑ
- Android (APK)
flutter build apk --release
- iOS (App Bundle)
flutter build ipa --release
- Android (APK)
- ๋ณธ ํ๋ก์ ํธ๋ ์์
์ ๋ฐฐํฌ๊ฐ ๋ถ๊ฐ๋ฅํ๋ฉฐ ์ธ๋ถ ๋ฆด๋ฆฌ์ฆ๋ฅผ ์ ํํ๋
publish_to: 'none'์์ฑ์ด ์ ์ธ๋์ด ์์ต๋๋ค. - Kakao SDK ๋ชจ๋์ ์ฌ์ฉํ๋ ๋ถ๋ถ์ด ํฌํจ๋์ด ์์ผ๋ฏ๋ก ๋น๋ ์
main.dartํ์ผ ๋ด์nativeAppKey์ ํ๋ซํผ ๋ค์ดํฐ๋ธ ์์ค์ ์ฐ๋ ์ค์ (Kakao Scheme)์ ํ์ธํด ์ฃผ์ญ์์ค. - ๋ง์ดํฌ ์บก์ฒ(
record) ๋ฐ ์ค์๊ฐ ์์ฑ์ธ์(speech_to_text) ์ฌ์ฉ์ ์ํด ๋๋ฐ์ด์ค ํ๋ซํผ ์ค์ ํ์ผ(AndroidManifest.xml,Info.plist)์ ๊ถํ ํ๋ ์คํ ๊ธฐ์ ์ ์ค์ํด์ผ ์ ์์ ์ธ ๋ฐํ์ ๋์์ ๋ณด์ฅ๋ฐ์ ์ ์์ต๋๋ค.