
Kalaam 구축기: Gemini가 런타임에 UI를 조립하는 아랍어 튜터
요약
Kalaam은 Gemini 모델이 런타임에 Flutter 위젯을 직접 구성하는 AI 네이티브 아랍어 학습 앱 구현 사례입니다. 고정된 UI 대신 모델이 위젯 카탈로그를 사용하여 학습 상황에 맞는 화면을 동적으로 생성합니다.
핵심 포인트
- Gemini가 런타임에 UI를 직접 조립하는 GenUI 방식 채택
- 13개의 아랍어 전용 위젯과 14개의 GenUI 프리미티브 활용
- 하드코딩된 레이아웃 없이 모델이 위젯 트리를 결정
- Flutter 개발자를 위한 오픈 소스 참조 구현체 제공
TL;DR
- 정의: Kalaam은 오픈 소스 Flutter 앱으로, Gemini 모델이 13개의 커스텀 아랍어 위젯(Arabic widgets)과 14개의 genui SDK 프리미티브(primitives)로 구성된 통합 카탈로그를 사용하여 각 아랍어 레슨 화면을 런타임(runtime)에 구성합니다. 고정된 화면이 없으며, 하드코딩된 레슨 흐름도 없습니다.
- 대상: 단순한 챗봇 래퍼(chatbot wrapper)가 아닌, 실제로 실행 가능한 참조 구현체(reference implementation)를 원하는 AI 네이티브 앱 구축 Flutter 개발자.
- 지금 바로 실행하기 (Firebase 불필요, API 키 불필요, 약 30초 소요):
git clone https://github.com/sayed3li97/kalaam.git cd kalaam && flutter pub get dart run build_runner build --delete-conflicting-outputs...
- 전체 소스: https://github.com/sayed3li97/kalaam
- 성숙도: 알파(Alpha) 단계. 데모 모드(Demo Mode)는 안정적입니다. 라이브 모드(Live Mode, Gemini가 실시간으로 사용자의 Firebase 프로젝트를 호출)는 README에 문서화된 설정 단계가 필요합니다.
목차
- 이것이 실제로 해결하는 문제
- Kalaam이란 무엇인가
- 주요 기능 심층 분석
- Kalaam의 비교 분석
- 솔직한 반론
- 5분 안에 시작하기
- 기여하는 방법
- 이것이 Flutter 개발에 의미하는 바
- 개발자들이 Kalaam에 대해 실제로 묻는 질문들
- 다음 단계
- 카탈로그가 새로운 진실의 원천(Source of Truth)이다
- 참고 문헌
- 저자 소개
아랍어 형태론(Arabic morphology)은 영어에는 실질적인 대응물이 없는 원칙에 따라 작동합니다. 이 언어의 거의 모든 단어는 예측 가능한 형태론적 패턴을 통해 세 글자 어근(root)에서 파생됩니다. 어근 ك-ت-ب (k-t-b, 쓰기라는 개념)는 كَتَبَ (그는 썼다), كِتَاب (책), كَاتِب (작가), مَكْتَب (사무실), مَكْتُوب (쓰인) 등을 생성합니다. 고전 아랍 문법학자들은 이러한 패턴에 대한 완전한 변환 표를 만들었습니다. 현대 언어학자들은 이에 대해 논문을 씁니다. 이 시스템은 우아하고 생성적이며, 플래시카드(flashcard)만으로는 정말 가르치기 어렵습니다.
제가 검토한 모든 아랍어 학습 앱은 이 복잡성에 대해 동일한 방식으로 대응합니다. 즉, 고정된 화면, 하드코딩된 어휘 목록, 형태가 절대 변하지 않는 객관식 그리드, 그리고 AI 모델이 아무런 의견도 가질 수 없는 '다음' 버튼을 사용하는 방식입니다. 모델이 나타나더라도, 모델이 생성한 텍스트는 개발자가 학습자에게 무엇이 필요할지 알기도 몇 달 전에 미리 연결해 둔 레이아웃 내부의 Text 위젯 안에 배치될 뿐입니다. 인터페이스는 모델이 제어하는 영역의 일부가 아닙니다.
Kalaam은 다른 입장을 취합니다. 언어 모델에게 미리 정해진 양식을 채우라고 요청하는 대신, Kalaam은 Gemini에게 실제 Flutter 위젯 카탈로그를 제공하고 무엇을 구성할지 결정하도록 맡깁니다. 모델은 13개의 맞춤형 아랍어 교육 위젯과 14개의 GenUI 내장 프리미티브 (primitives)를 설명하는 JSON 스키마를 읽은 다음, GenUI SDK가 기기 상의 실제 위젯 트리 (widget tree)로 구체화할 A2UI 서피스 (surface)를 스트리밍합니다. 루트 다이어그램에서 단어 노드를 탭하면, 해당 상호작용은 UserActionEvent로서 Gemini에게 다시 전달됩니다. 그러면 모델은 다음에 올 서피스를 무엇이든 작성합니다.
이것이 실제로 해결하는 문제
AI 지원 Flutter 앱을 위한 표준 아키텍처 (architecture)는 다음과 같은 코드를 생성합니다:
// 대부분의 "AI 기반" 학습 앱이 실제로 하는 방식
final response = await model.generateContent([Content.text(prompt)]);
return Text(response.text ?? '');
모델은 텍스트를 생성합니다. 개발자가 설계한 고정된 UI가 이를 감쌉니다. 모델은 활용형 표 (conjugation table)가 어떻게 생겼는지 알지 못하며, 학습자가 이미 단어를 알고 있을 때 빈칸 채우기 대신 어휘 캐러셀 (vocabulary carousel)을 보여주기로 결정할 수도 없고, 탭한 단어에 대해 해당 단어의 삼자근 (triliteral root) 다이어그램을 구성하여 반응할 수도 없습니다. 모든 상태 전이 (state transition)는 컴파일 타임 (compile time)에 결정되었습니다.
아키텍처의 격차 (architectural gap): 고정된 위젯에
String만 채워 넣는 모델은 더 나은 콘텐츠를 생성할 수는 있습니다. 하지만 더 나은 교육을 생성할 수는 없습니다. 이 둘은 서로 다른 것입니다.
genui Flutter 패키지 (A2UI v0.9)는 모델에게 카탈로그 스키마 (catalog schema)와 구조화된 전송 프로토콜 (structured transport protocol)을 제공함으로써 이 문제를 해결합니다. 모델은 카탈로그 아이템 (catalog item) 이름을 사용하여 위젯 트리 (widget tree)를 설명하는 createSurface 및 updateComponents와 같은 JSON 메시지를 생성합니다. SDK는 이러한 메시지를 파싱하고 각 위젯에 등록된 widgetBuilder를 호출합니다. 개발자는 카탈로그를 정의합니다. 모델은 어떤 아이템을, 어떤 데이터와 함께, 어떤 순서로 사용할지 결정합니다.
Kalaam은 아랍어 교육을 위해 이 패턴을 완전하게 구현한 오픈 소스 프로젝트로, 형태론 (morphology), 어휘 (vocabulary), 음성학 (phonetics), 대화 (dialogue), 문화적 맥락 (cultural context), 그리고 학습 진도 추적 (progress tracking)을 다루는 13개의 커스텀 CatalogItem 위젯을 포함하고 있습니다. 전체 소스 코드는 https://github.com/sayed3li97/kalaam에서 확인할 수 있습니다.
Kalaam이란 무엇인가
Kalaam은 Gemini가 각 아랍어 레슨 인터페이스를 런타임 (runtime)에 조립하는 Flutter 애플리케이션입니다. 시나리오(커피 주문하기, 카이로 시장에서의 협상, 공식 비즈니스 편지 등)를 선택하면, 모델은 학습자가 무엇을 하는지 그리고 어떻게 답변하는지에 따라 장면 설정 (scene-setter)을 먼저 보여준 뒤, 어휘 카드 (vocabulary cards), 삼자근 도표 (triliteral root diagram), 활용표 (conjugation table), 퀴즈 등을 구성하여 레슨 화면을 만듭니다. 위젯 순서는 미리 작성된 스크립트가 아닙니다. 모델이 직접 선택합니다.
시나리오 선택기 · 어근 시스템 (ش-ر-ب) · 노드를 탭하여 형태론적 패턴(morphological pattern) 확인 — 모든 화면은 Gemini에 의해 실시간으로 구성됩니다.
저(Sayed Ali Alkamel, @sayed3li97)는 초기에 다른 모든 것을 결정짓는 중요한 아키텍처적 결정을 내렸습니다. 바로 모델과 마주하는 전체 계약(contract)을 YAML 설정 파일이나 JSON 레지스트리가 아닌, 타입이 지정된 Dart CatalogItem 객체로 표현하기로 한 것입니다. 각 아이템의 dataSchema와 widgetBuilder는 동일한 Dart 파일 내에 존재합니다. 모델이 학습하는 JSON 스키마(JSON schema)와 이를 렌더링하는 Flutter 코드가 같은 위치에 배치됩니다. 따라서 스키마를 변경하면, 컴파일러가 위젯이 더 이상 스키마와 일치하지 않는지 여부를 알려줍니다.
데모 모드에서 설치 및 실행 (Flutter 3.44+ 필요, Firebase 및 API 키 불필요):
git clone https://github.com/sayed3li97/kalaam.git
cd kalaam
flutter pub get
...
핵심 프리미티브(primitive)는 CatalogItem입니다. Kalaam이 모델에 노출하는 모든 커스텀 위젯은 하나의 CatalogItem으로 등록됩니다. 간소화된 RootExplorer 등록 예시는 다음과 같습니다:
final rootExplorerItem = CatalogItem(
name: 'RootExplorer',
dataSchema: S.object(
...
Gemini가 ش-ر-ب 어근군을 가르치고 싶을 때, 어근 글자, 의미, 단어군이 채워진 RootExplorer 컴포넌트를 포함하는 createSurface 메시지를 생성합니다. GenUI SDK는 widgetBuilder를 호출하고 실시간 Flutter 위젯을 반환합니다. 모델은 Flutter 코드를 전혀 보지 못했습니다. 모델은 오직 JSON 스키마만을 알고 있었습니다.
내부적으로는 SurfaceController가 양방향 루프(bidirectional loop)를 관리합니다. 들어오는 A2UI 메시지는 현재의 서피스(surface)를 구축합니다. 나가는 UserActionEvent 페이로드(payload)는 학습자의 상호작용을 모델의 대화 기록으로 다시 전달하여, 다음 턴을 위한 루프를 완성합니다.
주요 기능 심층 분석
어근 시스템 탐색기 (The Root System Explorer)
RootExplorer는 삼자근 (triliteral root)을 중심으로 하는 방사형 다이어그램 (radial diagram)을 렌더링합니다. 각 위성 노드 (satellite node)는 해당 어근 가계의 파생어입니다. 노드를 탭하면, 단순히 아랍어 단어만 보여주는 92×46pt 크기의 필 (pill) 형태에서, 형태론적 패턴 (morphological pattern, وزن / wazn), 음차 (transliteration) 및 영어 의미, 그리고 "Explore" 버튼을 보여주는 155×144pt 크기의 카드로 확장됩니다.
مَشْرُوب (음료)를 탭하면 노드가 확장되어 wazn (وزن مَفْعُول), 음차, 영어 의미, 그리고 Gemini가 구성한 새로운 화면으로 분기되는 Explore→ 버튼이 나타납니다.
그 Explore 버튼은 단순히 장식적인 것이 아닙니다. 이는 Gemini로 UserActionEvent를 다시 보냅니다:
sendKalaamAction(ctx, 'explore_word', {
'word': word,
'root': rootWord,
...
Gemini는 다음 대화 턴 (conversation turn)에서 이를 수신하며, 해당 특정 단어를 위한 완전히 새로운 화면을 구성할 수 있습니다. 예를 들어 어원을 포함한 VocabCard, 동사인 경우 ConjugationTable (활용표), 또는 단어가 관용적 의미를 가진 경우 CulturalNote (문화적 노트) 등을 생성할 수 있습니다. 이 다이어그램은 정적인 삽화가 아닙니다. 이는 레슨의 분기점입니다.
HarakatBuilder
아랍어 모음 기호 (harakat)는 중급 학습자들에게 가장 흔한 장벽 중 하나이며, 플래시카드 (flashcard)로 가르치기 가장 어려운 부분 중 하나이기도 합니다. 대부분의 학습 앱은 단순히 모음이 모두 표기된 텍스트를 보여줄 뿐입니다. HarakatBuilder는 이 연습 방식을 뒤집습니다.
자음 골격 (consonant skeleton)과 목표 발음 (target vocalization)이 주어지면, 학습자는 각 글자를 탭하고 팔레트에서 모음 기호를 선택합니다: fatha (َ), kasra (ِ), damma (ُ), sukun (ْ), shadda (ّ). 각 모음 기호가 배치될 때마다 단어는 실시간으로 다시 렌더링됩니다. 올바르게 완료하면 위젯은 다음을 전송합니다:
sendKalaamAction(ctx, 'completed', {'isCorrect': true});
모델은 이를 수신하고 학습자가 방금 보여준 지식을 바탕으로 다음 단계를 구성합니다. Flutter 코드 내에 하드코딩된 분기 로직(branching logic)은 없습니다. 모델이 어디로 이동할지를 결정합니다.
Live GenUI Inspector
Inspector는 개발자가 처음 실행했을 때 가장 놀라워하는 Kalaam의 구성 요소입니다.
세션 화면의 앱 바(app bar)에 있는 {} 버튼을 누르면 슬라이드 업 패널(slide-up panel)이 열리며, 현재 세션 동안 Gemini가 방출한 모든 A2UI 메시지를 최신순으로 JSON 형식의 프리티(pretty-printed) 형태로 보여줍니다. 레슨이 로드됨에 따라 createSurface 메시지가 나타나는 것을 지켜볼 수 있습니다. 컴포넌트 이름, 모델이 채우기로 선택한 데이터, 서피스 ID(surface IDs)를 확인할 수 있습니다. 어떤 JSON이 화면에 정확히 어떤 위젯을 생성했는지 정확하게 추적할 수 있습니다.
이 기능을 처음 구축했을 때, 저는 내부적으로 진행하는 모든 데모에서 Inspector를 열어두었습니다. 스트림에 `
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
