3. 작동 원리 심층 분석 (Under the Hood)
Meetily가 단순한 아이디어를 넘어 실용적인 성능을 내기까지는 상당히 정교한 아키텍처가 필요합니다. 로컬 환경은 클라우드 서버에 비해 연산 자원(CPU, GPU, RAM)이 절대적으로 부족하기 때문입니다. 이 한계를 극복하기 위해 Meetily는 어떻게 설계되었는지 단계별로 파헤쳐 봅니다.
3.1. 아키텍처 개요: 왜 Rust와 Tauri인가?
이 프로젝트의 가장 중요한 기술적 선택은 코어 백엔드 언어로 Rust를 채택한 것입니다. 음성 캡처와 실시간 추론(Inference)은 지연 시간(Latency)에 매우 민감한 작업입니다. Python과 같은 인터프리터 언어로 오디오 버퍼를 처리하면 메모리 오버헤드와 가비지 컬렉션(GC)으로 인해 미세한 끊김 현상이 발생할 수 있습니다.
Rust는 메모리 안전성을 보장하면서도 C/C++에 준하는 하드웨어 제어권과 속도를 제공합니다. Meetily는 OS 수준의 오디오 드라이버(macOS의 경우 AVFoundation, Windows의 경우 WASAPI)와 직접 통신하며, 생성된 오디오 버퍼를 복사본 없이(Zero-copy) 추론 엔진으로 전달하는 구조를 갖추고 있습니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
classDiagram
class CLS_APP_CORE {
+start_recording()
+stop_recording()
+get_status()
}
class CLS_AUDIO_MANAGER {
-buffer: Vec
+capture_stream()
+apply_vad()
}
class CLS_INFERENCE_ENGINE {
-model_weights: Pointer
+transcribe_chunk()
+diarize_speakers()
}
class CLS_LOCAL_DB {
-sqlite_conn: Connection
+save_transcript()
+fetch_history()
}
CLS_APP_CORE *-- CLS_AUDIO_MANAGER
CLS_APP_CORE *-- CLS_INFERENCE_ENGINE
CLS_APP_CORE *-- CLS_LOCAL_DB
프론트엔드 UI는 Tauri 프레임워크를 사용합니다. 웹 기술(HTML, CSS, JavaScript)로 데스크톱 앱을 만들 수 있다는 점에서 Electron과 비슷하지만, 무거운 Chromium 엔진 대신 운영체제에 내장된 기본 웹뷰를 사용하여 앱 용량과 메모리 점유율을 획기적으로 낮췄습니다. 시스템 자원을 온전히 로컬 AI 모델이 사용할 수 있도록 양보한 영리한 설계입니다.
3.2. 음성 인식 및 화자 분리 파이프라인
Meetily가 자랑하는 또 하나의 강점은 실시간 전사(Transcription) 속도입니다. 단순히 표준 Whisper 모델을 사용한 것이 아니라, NVIDIA의 Parakeet 모델 구조와 Whisper.cpp를 고도로 최적화하여 기존 대비 4배 빠른 실시간 전사 속도를 달성했습니다.
Parakeet은 음성을 텍스트로 변환하는 과정에서 RNN-T(Recurrent Neural Network Transducer) 아키텍처를 기반으로 설계되어, 오디오가 끝날 때까지 기다리지 않고 스트리밍 방식으로 텍스트를 즉각 출력하는 데 탁월합니다.
전체 데이터 파이프라인은 다음과 같이 흐릅니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
flowchart TD
A["마이크 및 시스템 오디오"] --> B["Tauri 프론트엔드 캡처"]
B --> C["Rust 백엔드 오디오 버퍼"]
C --> D["음성 활동 감지 및 화자 분리"]
D --> E["Parakeet 및 Whisper 추론 엔진"]
E --> F["실시간 전사 텍스트 스트림"]
F --> G["Ollama 로컬 LLM 호출"]
G --> H["최종 요약 및 액션 아이템 생성"]
H --> I["로컬 데이터베이스 저장"]
오디오 스트림이 들어오면 우선 VAD(Voice Activity Detection, 음성 활동 감지) 알고리즘이 작동하여 침묵 구간을 잘라냅니다. 이를 통해 불필요한 연산을 줄입니다. 이후 화자 분리(Speaker Diarization) 모듈이 음성의 고유한 특징(Embedding)을 분석하여 “발화자 A”, “발화자 B”를 실시간으로 구분해 냅니다. 이 모든 과정이 내 PC의 메모리 위에서만 일어납니다.
회의 중 실시간으로 일어나는 상호작용은 아래 시퀀스 다이어그램으로 확인할 수 있습니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
sequenceDiagram
participant User as "사용자"
participant GUI as "Tauri 클라이언트"
participant Core as "Rust 백엔드"
participant STT as "Whisper 엔진"
participant LLM as "Ollama 서비스"
User->>GUI: "회의 녹음 시작 버튼 클릭"
GUI->>Core: "오디오 스트림 전송 시작"
activate Core
Core->>STT: "청크 단위 오디오 데이터 전달"
activate STT
STT-->>Core: "실시간 텍스트 반환"
deactivate STT
Core-->>GUI: "화면 텍스트 업데이트"
User->>GUI: "회의 종료"
GUI->>Core: "녹음 중지 및 전체 텍스트 병합"
Core->>LLM: "전체 스크립트 기반 요약 요청"
activate LLM
LLM-->>Core: "요약본 및 액션 아이템 반환"
deactivate LLM
Core-->>GUI: "최종 결과물 표시"
deactivate Core
3.3. 로컬 LLM 통합: Ollama를 통한 지능 부여
텍스트로 변환된 회의 스크립트만으로는 훌륭한 비서라고 할 수 없습니다. 1시간짜리 대본을 처음부터 끝까지 다시 읽는 것은 고통스러운 일이니까요. Meetily는 오픈소스 로컬 LLM 구동기인 Ollama와 통합하여 이 문제를 해결합니다.
사용자는 자신의 하드웨어 사양에 맞게 다양한 모델을 선택할 수 있습니다. 예를 들어, 노트북의 RAM이 8GB로 제한적이라면 가벼운 Gemma 3n이나 Mistral 모델을 사용하고, 16GB 이상의 여유로운 환경이라면 추론 능력이 뛰어난 Llama 3 기반 모델을 돌려 훨씬 깊이 있는 요약과 액션 아이템(해야 할 일 목록)을 추출할 수 있습니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
flowchart LR
A["오프라인 텍스트 전사본"] --> B["Ollama 라우터"]
B --> C["Gemma 3n (가벼운 작업)"]
B --> D["Llama 3 (균형 잡힌 요약)"]
B --> E["Mistral (복잡한 논리 분석)"]
C --> F["최종 회의록"]
D --> F
E --> F
이러한 구조 덕분에 Meetily는 외부 인터넷이 완전히 차단된 비행기 안이나 보안 구역에서도 회의 내용을 완벽하게 녹음하고 텍스트로 변환한 뒤, 논리 정연한 요약본까지 만들어낼 수 있습니다. 회의 데이터 모델의 구조는 대략적으로 다음과 같이 관계를 맺습니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
erDiagram
MDL_MEETING {
string meeting_id
string title
datetime start_time
datetime end_time
}
MDL_TRANSCRIPT {
string transcript_id
string meeting_id
string speaker_label
string text_content
float timestamp
}
MDL_SUMMARY {
string summary_id
string meeting_id
string full_summary
string action_items
}
MDL_USER {
string user_id
string local_profile_name
}
MDL_USER ||--o{ MDL_MEETING : "hosts"
MDL_MEETING ||--|{ MDL_TRANSCRIPT : "contains"
MDL_MEETING ||--|| MDL_SUMMARY : "generates"
회의 생명주기 측면에서 보면 상태 전이는 매우 직관적입니다. 녹음 중(Recording) 상태에서 백그라운드 스레드가 끊임없이 텍스트를 변환(Transcribing)하며, 회의가 끝나는 즉시 요약(Summarizing) 상태로 넘어갑니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
stateDiagram-v2
direction LR
[*] --> ST_IDLE
ST_IDLE --> ST_RECORDING : "회의 시작"
ST_RECORDING --> ST_TRANSCRIBING : "오디오 버퍼 채워짐"
ST_TRANSCRIBING --> ST_RECORDING : "실시간 텍스트 반환"
ST_RECORDING --> ST_SUMMARIZING : "회의 종료"
ST_SUMMARIZING --> ST_COMPLETED : "Ollama 응답 완료"
ST_COMPLETED --> ST_IDLE : "저장 후 대기"