"๋ง๋น(Malbit)"์ ๊ตฌ์์ฅ์ (Dysarthria) ๋ฐ ์ธ์ด ์ฅ์ ๊ฐ ์๋ ์ฌ์ฉ์๊ฐ ์ง์ ํ๊ฒฝ์ด๋ ์ผ์์ํ์์ ๊ฒช๋ ์์ฌ์ํต์ ์ฅ๋ฒฝ์ ๊ทน๋ณตํ๋๋ก ๋๋ AI ๊ธฐ๋ฐ ์ด์์คํดํธ & ๋ฐ์ ๊ฐ์ ํ๋ จ ํ๋ซํผ์ ๋๋ค.
์ด ๋ ํฌ์งํ ๋ฆฌ๋ ์ฌ์ฉ์์ ๊ตฌ์ด ๋ถ์, ๋ง์ถคํ ๋ฐ์ ํผ๋๋ฐฑ(์ํฉ๊ทน ์ฐ์ต), ๋จ๋ฌธ ๋ฆฌ๋ง์คํฐ๋ง(๋ฐํ ๋ณด์ ), ๊ทธ๋ฆฌ๊ณ ํ์๋ก/์ ๋ฌด ๋ด์ฉ์ ์์ฝํ์ฌ ์บ๋ฆฐ๋์ ์ฐ๋ํด ์ฃผ๋ ๊ธฐ๋ฅ์ ๋ด๋นํ๋ Spring Boot ๋ฐฑ์๋ ๋ฐ FastAPI AI ์๋ฒ๋ฅผ ํฌํจํ๊ณ ์์ต๋๋ค.
๋์ฉ๋ ์์ฑ ์ฒ๋ฆฌ ๋ฐ ๋ํ ์ธ์ด ๋ชจ๋ธ(LLM) ์ถ๋ก ์ ์์ ์ ์ด๊ณ ๋ฐ์ ์๋ ์ ํ ์์ด ์ํํ๊ธฐ ์ํด Java Spring Boot ๊ธฐ๋ฐ ๋น์ฆ๋์ค/๋ณด์ ์๋ฒ์ Python FastAPI ๊ธฐ๋ฐ AI ๋ชจ๋ธ ์ถ๋ก ์๋ฒ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ค๊ณํ์ต๋๋ค.
graph TD
Flutter[Flutter Mobile Client] <-->|REST API / JWT Auth| Boot[Spring Boot Backend <br> Java 21 / Spring Boot 3.2.4]
Boot <-->|Async Non-blocking WebClient| Fast[FastAPI AI Server <br> Python / Uvicorn]
subgraph AI Engine Pipeline
Fast <-->|Local GPU / PyTorch Pipeline| Whisper[Custom Fine-tuned Whisper <br> whisper-dysarthria-ko / whisper-medium]
Fast <-->|boto3 API Client| Bedrock[AWS Bedrock <br> Claude 3 Haiku]
end
Fast -->|Async HTTP Webhook / POST| BootCalendar[Spring Boot /api/calendar/manual]
Boot <-->|Spring Data JPA| DB[(MySQL Database)]
- ๋ชจ๋ฐ์ผ ํด๋ผ์ด์ธํธ (Flutter): ์ค๋์ค ๋ฐ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ REST API ํํ๋ก ์์ฒญํ๋ฉฐ JWT ํ ํฐ์ ํค๋์ ์ค์ด ์ ์กํฉ๋๋ค.
- ๋ฐฑ์๋ ์๋ฒ (Spring Boot): ํ ํฐ์ ๊ฒ์ฆํ๊ณ , ์์ฑ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๋ณต์กํ ์์
์ธ ๊ฒฝ์ฐ
Spring WebFlux WebClient๋ฅผ ํ์ฉํ์ฌ FastAPI AI ์๋ฒ๋ก ์ค๋์ค ํ์ผ์ ๋น๋๊ธฐ(Non-blocking Mono) ๋ฐฉ์์ผ๋ก ์ ๋ฌํฉ๋๋ค. - AI ์๋ฒ (FastAPI): ์ ๋ฌ๋ฐ์ ์ค๋์ค๋ฅผ ์ ์ฒ๋ฆฌํ ๋ค, ๊ตฌ์์ฅ์ (Dysarthria) ํ์ธํ๋ Whisper์ ํต๊ณผ์์ผ ๋ ๊ฒ์ ํ ์คํธ(Raw Text)๋ฅผ ์ ์ฌํฉ๋๋ค.
- LLM ์ค์ผ์คํธ๋ ์ดํฐ (AWS Bedrock): ์ ์ฌ๋ ํ
์คํธ๋ฅผ
Claude 3 Haikuํ๋กฌํํธ๋ฅผ ์ฌ์ฉํด ์ ์คํ๊ณ ์์ฑ๋ ๋์ ๋น์ฆ๋์ค์ฉ ๋ฌธ์ฅ(Refined Text)์ผ๋ก ์ ์ ํ๊ฑฐ๋, ์ ๋ฌด ํ์ ๋ถ์ ๋ฐ์ดํฐ(์์ฝ, ํ ์ผ, ์ผ์ )๋ฅผ ์ถ์ถํด JSON ๊ฐ์ฒด๋ก ํ์ฑํฉ๋๋ค. - ์บ๋ฆฐ๋ ๋๊ธฐํ: AI ๋ถ์ ๊ณผ์ ์์ ์ผ์ ์ ๋ณด๊ฐ ์ถ์ถ๋๋ฉด, AI ์๋ฒ๊ฐ ์ฆ์ ๋ฐฑ์๋ ์๋ฒ์ ์๋ ์ผ์ ์์ฑ API(
POST /api/calendar/manual)๋ฅผ ํธ์ถํ์ฌ ํด๋น ์ฌ์ฉ์์ ์บ๋ฆฐ๋์ ์๋ ์ฐ๋ํฉ๋๋ค.
- Java 21 & Spring Boot 3.2.4
- ๋๊ท๋ชจ ์ค๋ ๋ ๋์์ฑ ์ํฉ์์ ๊ฐ์ ์ค๋ ๋(Virtual Threads)์ ์ฐ๋์ฑ๊ณผ ์ต์ JVM ์ฑ๋ฅ ์ด์ ์ ํ๋ณดํ๊ธฐ ์ํด Java 21 LTS ๋ฒ์ ์ฑํ.
- Spring WebFlux WebClient
- ์์ฑ ์ ์ฌ(ASR) ๋ฐ LLM ์ฒ๋ฆฌ ๊ณผ์ ์ ํ์ฐ์ ์ผ๋ก 5~15์ด ์์ค์ ๋์ ์ง์ฐ ์๊ฐ(Latency)์ ์ ๋ฐํฉ๋๋ค. ๊ธฐ์กด MVC์
RestTemplate๊ฐ์ ๋๊ธฐ์ ๋ํน ํต์ ์ ๋๋์ ์ค๋์ค ์ ์ก ์ ์๋ธ๋ฆฟ ์ค๋ ๋ ๊ณ ๊ฐ์ ์ด๋ํฉ๋๋ค. ์ด๋ฅผ ์๋ฐฉํ๊ณ ๊ทน๋๋ก ๊ฒฝ๋ํ๋ ๋น๋๊ธฐ ๋ ผ๋ธ๋กํน ํ์ดํ๋ผ์ธ์ ๊ตฌ์ถํ๊ธฐ ์ํด WebClient ์ฑํ.
- ์์ฑ ์ ์ฌ(ASR) ๋ฐ LLM ์ฒ๋ฆฌ ๊ณผ์ ์ ํ์ฐ์ ์ผ๋ก 5~15์ด ์์ค์ ๋์ ์ง์ฐ ์๊ฐ(Latency)์ ์ ๋ฐํฉ๋๋ค. ๊ธฐ์กด MVC์
- Spring Security, OAuth2 Client, JJWT (0.11.5)
- ํ๋ก ํธ์๋๊ฐ ํฌ๋ก์ค ํ๋ซํผ ๋ชจ๋ฐ์ผ ํ๊ฒฝ(Flutter)์ด๋ฏ๋ก ์ธ์ ๋์ ์ํ๋ฅผ ์ ์งํ์ง ์๋ Stateless ํ ํฐ ๊ธฐ๋ฐ(JWT) ์ธ์ฆ ํํฐ๋ฅผ ์ ์ฉํ์ต๋๋ค.
- ๊ตฌ๊ธ, ์นด์นด์ค ๋ฑ์ ์์ ๊ฐํธ ์ฐ๋์ ์ ์ํ ๊ตฌํํ๊ธฐ ์ํด ์คํ๋ง ํ์ค OAuth2 ํด๋ผ์ด์ธํธ ํตํฉ.
- Spring Data JPA & MySQL
- ๊ฐ์ฒด ์งํฅ์ ์ํฐํฐ ์ค๊ณ์ ์์์ฑ ์ปจํ ์คํธ(1์ฐจ ์บ์, ์ฐ๊ธฐ ์ง์ฐ)๋ฅผ ๊ทน๋ํํ์ฌ ํธ๋์ญ์ ๋ฌด๊ฒฐ์ฑ๊ณผ ํจ์จ์ ์ธ ๋๋ฉ์ธ ๋ชจ๋ธ ๊ด๋ฆฌ ๋ณด์ฅ.
- FastAPI & Uvicorn
- Python ๊ธฐ๋ฐ์ ๊ณ ์ฑ๋ฅ ๋น๋๊ธฐ ์น ํ๋ ์์ํฌ๋ก, ๋ฉํฐํํธ ์ค๋์ค ์คํธ๋ฆฌ๋ฐ ๋ฐ์ดํฐ์ AI ์ถ๋ก ๋ฃจํ๋ฅผ ๋์ ์ฒ๋ฆฌ๋์ผ๋ก ์๋นํ๊ธฐ ์ํด ์ฑํ.
- Fine-Tuned ASR Whisper (
openai/whisper-medium๊ธฐ๋ฐ)- ์ผ๋ฐ์ ์ธ ์์ฉ STT๋ ๋ถ๋ถ๋ช
ํ ์กฐ์(Stammering, Dysarthria)์ ์์์ด๋ ์ค๋ฐ์์ผ๋ก ์น๋ถํ์ฌ ์ธ์๋ฅ ์ด ๊ทน๋๋ก ๋จ์ด์ง๋๋ค. ์ด๋ฅผ ๋ณด์ํ๊ณ ์ ํ๊ตญ์ด ๊ตฌ์์ฅ์ ์์ฑ ๋ฐ์ดํฐ์
์ผ๋ก ๋ฏธ์ธ ์กฐ์ ๋ ๋ชจ๋ธ(
tepo6640/whisper-dysarthria-ko)์Transformers Pipelineํํ๋ก ๋ก๋ํ์ฌ ํ์ฉํฉ๋๋ค. librosa์ค๋์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ๋ชจ๋ ์ ๋ ฅ ์์์ 16,000Hz, ๋ชจ๋ ธ ์ฑ๋๋ก ๋ฆฌ์ํ๋งํ์ฌ ์ถ๋ก ์ค์ฐจ ์ต์ํ.- ๋ค์ค ์ค๋์ค์ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ์ํด
batch_size=8,stride_length_s=5, ๊ทธ๋ฆฌ๊ณ ๋ฐ๋ณต์ ์ด๊ณ ๊ผฌ์ด๋ ๋ฐ์์ ๊ฐ์ธํ๋๋กrepetition_penalty=1.1ํ๋ผ๋ฏธํฐ๋ฅผ ๋ง์ถค ๊ตฌ์ฑ.
- ์ผ๋ฐ์ ์ธ ์์ฉ STT๋ ๋ถ๋ถ๋ช
ํ ์กฐ์(Stammering, Dysarthria)์ ์์์ด๋ ์ค๋ฐ์์ผ๋ก ์น๋ถํ์ฌ ์ธ์๋ฅ ์ด ๊ทน๋๋ก ๋จ์ด์ง๋๋ค. ์ด๋ฅผ ๋ณด์ํ๊ณ ์ ํ๊ตญ์ด ๊ตฌ์์ฅ์ ์์ฑ ๋ฐ์ดํฐ์
์ผ๋ก ๋ฏธ์ธ ์กฐ์ ๋ ๋ชจ๋ธ(
- AWS Bedrock (Claude 3 Haiku)
- Whisper์ ์ ์ฌ ํ ์คํธ ์ ์ธ์ด์ ์ค๋ฅ๋ฅผ ๊ทน๋ณตํ๊ณ , ์ ๊ตํ๊ณ ๊ณต์ํ ๋ฌธ๋งฅ์ ์์ฑํฉ๋๋ค.
- ๊ฐ์ฑ๋น์ ๋งค์ฐ ๋น ๋ฅธ ์๋ต ์๊ฐ(Latency)์ ๋ณด์ ํ
Claude 3 HaikuAPI ๋ชจ๋ธ์ ์ ์ฉํ์ฌ ์ผ์ ๋ฐ ์ง์ ์ ๋ฐํ ๋ถ์ ๊ธฐ๋ฅ ๊ตฌ๋.
๋ฐฑ์๋์ AI ์๋ฒ๋ ์์ ํ ๋ถ๋ฆฌ๋์ด ์์ผ๋ฉฐ ์คํ๋ง์ WebClient๋ฅผ ์ฌ์ฉํด ๋ฐ์ํ ๋น๋๊ธฐ ๋จ์ผ ์๋ํฌ์ธํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
// RemasteringService.java ์ค ์ผ๋ถ ๋ฐ์ท
public Mono<RemasteringLogResponse> remaster(String email, MultipartFile audioFile, String preferredTone) {
long startTime = System.currentTimeMillis();
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
bodyBuilder.part("file", audioFile.getResource());
if (preferredTone != null && !preferredTone.isBlank()) {
bodyBuilder.part("preferred_tone", preferredTone);
}
return webClient.post()
.uri("/api/analyze")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(bodyBuilder.build()))
.retrieve()
.bodyToMono(AiServerResponseDto.class)
.map(aiRes -> {
long latency = System.currentTimeMillis() - startTime;
String raw = aiRes.getRawText() != null ? aiRes.getRawText() : "์ธ์ ๋ด์ฉ ์์";
String refined = aiRes.getRefinedText() != null ? aiRes.getRefinedText() : raw;
// ํต๊ณ ๋์ ๋ฐ ๋น๋๊ธฐ ์์์ฑ ์ปจํ
์คํธ ์ฒ๋ฆฌ
userService.addCorrection(email, 50);
ConversationLog savedLog = conversationService.saveResult(email, raw, refined, latency);
return new RemasteringLogResponse(savedLog.getLogId(), savedLog.getSttOrigin(), savedLog.getRefinedText(), (int) latency);
});
}- ๋์ ์๋ฆฌ: ์ฌ์ฉ์๊ฐ ์ ์กํ ๋ฐ์ดํฐ๋ ์คํธ๋ฆผ ํ์์ผ๋ก AI ์๋ฒ์ ์ ์ก๋๋ฉฐ, ์ฒ๋ฆฌ ์ง์ฐ ์๊ฐ์ด ์ ์ง๋๋ ๋์ ๋ฐฑ์๋ ํ๋ก์ธ์ค์ ์ค๋ ๋๋ ํ๋ก ๋ฐํ๋์ด ๋๊ธฐํ์ง ์์ต๋๋ค. ์ต์ข ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๋ฐ์ ๋ ๋น๋ก์ ๋งคํ ์์ ๊ณผ DB ์ ์ฅ์ด ํธ๋ฆฌ๊ฑฐ๋๋ฏ๋ก ์๋ฒ ์์์ ํจ์จ์ฑ์ด ํ๋๋ฉ๋๋ค.
๊ตฌ์์ฅ์ ์๋ ๋ฐ ์ฑ์ธ์ ๋ํ ํน์ง์ ๋จ์ด ๋ฐ๋ณต, ๋ชจ์ ์๊ณก, ์์ ๋๋ฝ์ ๋๋ค. AI ์๋ฒ๋ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด 2๋จ๊ณ ํ์ดํ๋ผ์ธ์ ํต๊ณผ์ํต๋๋ค.
# 1๋จ๊ณ: Custom ASR (Whisper)๋ฅผ ํตํ ์์ฑ ์ ์ฌ
raw_text = transcribe_audio(asr_pipe, audio_path)
# 2๋จ๊ณ: AWS Bedrock LLM์ ํตํ Contextual Refinement
# Prompt Engineering์ ํตํด ๋น๋
ผ๋ฆฌ์ ์ ์ฌ๋ฅผ ๋ฌธ๋งฅ์ ๋ง๋ ๋น์ฆ๋์ค์ ๊ตฌ์ด๋ก ์นํํฉ๋๋ค.
prompt = f"""
You are the "Malbit Smart Workplace Assistant," specialized in supporting workers with speech or language impairments.
Analyze the [Transcript] below and refine it to clear, polite Korean.
Ensure immediate action items (checklists) and schedules are properly structured.
Reference Date: {current_date}
"""์ด 2๋จ๊ณ ๋ณด์ ํ์ดํ๋ผ์ธ์ ๊ตฌ์์ฅ์ ์์ฑ์ธ์ ์ ํ๋์ ํ์์ ๋ถํ์ค์ฑ์ LLM ๋ฌธ๋งฅ ๋ณต์๋ ฅ์ผ๋ก ๋ณด์ ํ์ฌ ์ค์ ๋น์ฆ๋์ค ๋ฐ ์ํต ํ์ฅ์์ ๊ฐ๋ ฅํ ์ค์ฉ์ฑ์ ๊ฐ์ง๊ฒ ํด์ค๋๋ค.
ํ์ ๋ด์ฉ๊ณผ ์ฅ๋ฌธ ๋ฐํ ๋ฐ์ดํฐ ๋ถ์ ์ ๋๋์ ๋คํ์ฑ ํ
์คํธ๋ฅผ ์ฒด๊ณ์ ์ผ๋ก ๋ถ๋ฅ ์ ์ฅํ๊ธฐ ์ํด Log ์ํฐํฐ์ ์ํธ ์ฐ๊ด๋ LogDetail ์ํฐํฐ ๊ตฌ์กฐ๋ฅผ ์ ์ฉํ์ต๋๋ค.
- Log: ์ ์ฌ ์ธ์ ์ ๋ํ ๊ณ ์ ๋ฉํ๋ฐ์ดํฐ(ํ์ดํ, ์ผ์, ์์ ์๊ฐ, ์ํ, ์ธ์ ์์ ์ฃผ) ๊ด๋ฆฌ.
- LogDetail: ๋คํ์ฑ ํ์
ํ๋(
DetailType)๋ฅผ ํ์ฉํด ํ๋์ ์์ฑ ํ์ผ๋ก๋ถํฐ ๋์จ ๋ณตํฉ์ ์ธ AI ๊ฐ๊ณต ์ ๋ณด๋ฅผ ์ ์ฐํ๊ฒ ๊ตฌ์กฐํ ๋ฐ ๋งคํ.SUMMARY: ํ์์ ์ค์ ์ฃผ์ ๋ฐ AI ๋จ๋ฌธ ์์ฝ๋ณธ.DECISION: ํ์ ์ค ๊ตฌ๋๋ก ํ์ ๋ ๋น์ ๊ธฐ์ ํฉ์์ ๋ฐ ์ฒดํฌ๋ฆฌ์คํธ.TODO: ์ผ์ (์์์ผ, ๋ง๊ฐ์ผ, ์ง๋ฌด ์นดํ ๊ณ ๋ฆฌ)์ผ๋ก ํ์ฐ๋ ์ ์๋ ์ธ๋ถ์ ์ธ ์ค๋ฌด ๋ชฉ๋ก.
erDiagram
USER ||--|| USER_STATISTICS : "has"
USER ||--o{ LOG : "creates"
LOG ||--o{ LOG_DETAIL : "contains"
USER ||--o{ TRAINING_SESSION : "conducts"
TRAINING_SESSION ||--o{ STEP_RESULT : "produces"
SCENARIO_STEP ||--o{ STEP_RESULT : "evaluates"
USER ||--o{ TASK : "manages"
USER {
Long id PK
String email UK
String password
String name
DisabilityType disabilityType
CognitiveLevel cognitiveLevel
JobType jobType
}
USER_STATISTICS {
Long id PK
Long userId FK
int totalCorrectionCount
int totalSummaryCount
int totalRoleplayCount
}
LOG {
Long id PK
Long userId FK
String title
LocalDate date
LocalTime startTime
String duration
LogType type
}
LOG_DETAIL {
Long id PK
Long logId FK
String content
DetailType type
String assignee
}
TRAINING_SESSION {
Long id PK
Long userId FK
Long categoryId FK
Long currentStepId FK
SessionStatus status
}
SCENARIO_STEP {
Long id PK
Long categoryId FK
int stepOrder
String currentSituation
String guestScript
String hintText
String missionText
String retryScript
}
STEP_RESULT {
Long id PK
Long sessionId FK
Long stepId FK
String rawText
String refinedText
int score
boolean isPassed
}
TASK {
Long id PK
Long userId FK
String content
String category
LocalDateTime startAt
LocalDateTime endAt
boolean isCompleted
}
-
POST
/api/remaster- ์ค๋ช : ๋จ๋ฌธ ์์ฑ ๋ น์ ํ์ผ์ ์ ๋ก๋ํ์ฌ ๊นจ๋ํ๊ณ ๊ณต์ํ ๋ฌธ์ฅ์ผ๋ก ๋ฆฌ๋ง์คํฐ๋ง์ ์ํํฉ๋๋ค.
- ๋ณด์: Bearer JWT ํ ํฐ ํ์
- Form Data:
audio_file: MultipartFile (์์ฑ ๋ฐ์ดํฐ)preferred_tone: String (์ํ๋ ๋งํฌ ํํฐ - ์ ํ)
- Response:
RemasteringLogResponse(๋ก๊ทธ ID, STT ์๋ฌธ, ๋ณด์ ๋ฌธ์ฅ, ์์์๊ฐ)
-
POST
/api/remaster/analyze-meeting- ์ค๋ช : ์ฅ๋ฌธ ํ์ ๋ น์์ ์ ๋ก๋ํ์ฌ ํ ์คํธ ์์ฝ, ๊ฒฐ์ ์ฌํญ, ๋ค๊ฐ์ค๋ ํ ์ผ์ ์ถ์ถํ๊ณ ๋ฐฑ์๋ ์บ๋ฆฐ๋์ ์ฐ๋ํฉ๋๋ค.
- ๋ณด์: Bearer JWT ํ ํฐ ํ์
- Form Data:
audio_file: MultipartFile
- Response:
MeetingAnalysisResponse(๋ก๊ทธ ID, ์ ์ฒด ํ ์คํธ ์๋ฌธ, ์์ฝ, ์ถ์ถ๋ ์ฒดํฌ๋ฆฌ์คํธ ๋ฐฐ์ด, ์บ๋ฆฐ๋ ์ฐ๋ ์ผ์ ๋ฐฐ์ด)
- GET
/api/training/categories- ์ค๋ช : ํ๋ จ ๊ฐ๋ฅํ ์ง๋ฌด ์ํฉ๊ทน(์นดํ, ํธ์์ ๋ฑ)์ ๋ชฉ๋ก์ ์ ์ฒด ๋ฐํํฉ๋๋ค.
- POST
/api/training/start- ์ค๋ช : ํน์ ์ง๋ฌด ์นดํ ๊ณ ๋ฆฌ์ ํ๋ จ ์ธ์ ์ ์ด๊ธฐํํ๊ณ ์์ํฉ๋๋ค. (์ฒซ ๋จ๊ณ ์๋๋ฆฌ์ค ๋๋ณธ ๋ฐํ)
- Body:
{ "categoryId": Long }
- POST
/api/training/analyze-voice- ์ค๋ช : ์ฌ์ฉ์์ ์ค์๊ฐ ๋ง์๋ฆฌ ๋ น์ ํ์ผ์ ์ ์ฌ ๋ฐ ๋ถ์ํ์ฌ ์ ๋ต ์๋๋ฆฌ์ค ๋์ฌ์์ ์ ์ฌ์ฑ์ ์ฑ์ ํ๊ณ , ํต๊ณผ(70์ ) ์ ๋ค์ ๋จ๊ณ๋ฅผ ๋์ ์ผ๋ก ๋ฐํํฉ๋๋ค.
- Form Data:
audio_file(Multipart),sessionId(Parameter)
- POST
/api/training/finish/{sessionId}- ์ค๋ช : ์ํฉ๊ทน ์ฐ์ต์ ์ ์ ์ข ๋ฃํ๊ณ , ์งํ๋ ๋ชจ๋ ์ธ๋ถ ํ๊ฐ ๊ธฐ๋ก๊ณผ ์ข ํฉ ์ ์ ๋ฐ ์ต์ข ์ดํ์ ์์ฑํ์ฌ ์ ๋ฌํฉ๋๋ค.
- POST
/api/calendar/manual- ์ค๋ช : ์๋ก์ด ์ผ์ ์ ์บ๋ฆฐ๋์ ์๋์ผ๋ก ์ง์ ๋ฑ๋กํฉ๋๋ค. (AI ์๋ฒ์์ ํ์ฑ๋ ์ผ์ ๋ ์ด ๊ฒฝ๋ก๋ฅผ ํตํด ๋๊ธฐํ๋ฉ๋๋ค.)
- Body:
{ "content": "string", "category": "string", "start_at": "YYYY-MM-DD HH:MM:SS", "end_at": "YYYY-MM-DD HH:MM:SS" }
- GET
/api/calendar- ์ค๋ช : ํน์ ๋ ์ง ๊ธฐ์ค์ ์ฃผ๊ฐ/์๊ฐ ์ผ์ ์ ๋ชจ๋ ์ค์บํ์ฌ ์กฐํํฉ๋๋ค.
- Query Param:
query_date(์:2026-05-27)
๋ฐฑ์๋ ๊ตฌ๋์ ํ์์ ์ธ ์ฃผ์ ๋ก์ปฌ ๋ณ์์ ๋๋ค:
DB_URL=jdbc:mysql://localhost:3306/malbit?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul
DB_USERNAME=your_mysql_username
DB_PASSWORD=your_mysql_password
# JWT ์ํธํ ๋์นญํค ์ค์ (32์ ์ด์์ ๊ฐ๋ ๋์ ํค)
JWT_SECRET=your_jwt_signature_secret_key_strong_and_unique
# Social OAuth2 Credentials
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
KAKAO_CLIENT_ID=your_kakao_client_id
KAKAO_CLIENT_SECRET=your_kakao_client_secret
# AI ์๋ฒ Endpoint ์ฃผ์
AI_SERVER_URL=http://localhost:5000# AWS Bedrock access keys for Claude-3-Haiku API invocation
AWS_ACCESS_KEY_ID=your_aws_access_key
AWS_SECRET_ACCESS_KEY=your_aws_secret_key
# ๊ธฐ๋ณธ ๋ํ ๋ณด์ ๋ชจ๋ธ ์ค์
LLM_MODEL=anthropic.claude-3-haiku-20240307-v1:0
# ํ์ธํ๋ Whisper ๋ก๋ ํน์ standard ๊ฐ์ค์น ๊ฒฝ๋ก
MODEL_PATH=openai/whisper-medium
# Spring Boot ์๋ฒ Endpoint
BACKEND_URL=http://localhost:8080- Java 21 JDK ์ด์
- Python 3.9 ์ด์
- FFmpeg ๋ผ์ด๋ธ๋ฌ๋ฆฌ: ์์์ ๋ณํ ๋ฐ ๋์ฝ๋ฉ ์ฐ์ฐ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด OS ํ๊ฒฝ ๋ณ์์ ํ์์ ์ผ๋ก ๋ฑ๋ก๋์ด ์์ด์ผ ํฉ๋๋ค.
- MacOS:
brew install ffmpeg - Windows:
choco install ffmpegํน์ ์๋ ๋ค์ด๋ก๋ ํ Path ์ถ๊ฐ
- MacOS:
cd malbit_backend
# ์์กด์ฑ ๋น๋ (ํ
์คํธ ์คํ ํจ์ค ์ต์
)
./gradlew build -x test
# ์คํํ Jar ๊ตฌ๋
java -jar build/libs/demo-0.0.1-SNAPSHOT.jarcd malbit_backend/malbit_ai
# ๊ฐ์ํ๊ฒฝ ๊ฒฉ๋ฆฌ ๋ฐ ํจํค์ง ์ค์
python -m venv venv
source venv/Scripts/activate # Windows: .\venv\Scripts\activate
# ์์กด ๋ชจ๋ ์ ์ฒด ์ค์น
pip install -r requirements.txt
# FastAPI ์๋ฒ 5000ํฌํธ๋ก ์คํ
uvicorn app:app --host 0.0.0.0 --port 5000 --reload๋ณธ ๋ ํฌ์งํ ๋ฆฌ ๋ด์ Dockerfile๋ค์ ๊ธฐ๋ฐ์ผ๋ก ๋ค์ค ๊ฒฉ๋ฆฌ ๋ฐฐํฌ ํ๊ฒฝ์ ์์ฑํ ์ ์์ต๋๋ค.
# 1. Spring Boot ๋ฐฑ์๋ ์ปจํ
์ด๋ ๋น๋ & ์คํ
cd malbit_backend
docker build -t malbit-backend:latest .
docker run -d -p 8080:8080 --env-file .env malbit-backend:latest
# 2. AI ์๋ฒ GPU ๊ฐ์ ๋ฐฐํฌ (GPU ๋ณด์ ์ธ์คํด์ค ์ ์ )
cd malbit_ai
docker build -t malbit-ai:latest .
docker run -d -p 5000:5000 --gpus all --env-file .env malbit-ai:latest