homb Module Marketplace & Integrations 설계
가족 앱 homb의 모듈 시스템을 B2B 마켓플레이스로 확장하여, 외부 사업체가 모듈을 개발·등록하고 사용자가 설치·사용할 수 있는 플랫폼으로 진화시킨다.
1. 현재 상태 (As-Is)
모듈 시스템
| 구성요소 | 설명 | 위치 |
|---|---|---|
ModuleDescriptor | 모듈 정적 정의 (key, name, icon, entityTypes) | core/modules/ |
ModuleRegistry | 인메모리 레지스트리 — 하드코딩 | default_module_registry.dart |
ModuleInstall | 가족별 설치 상태 (Firestore) | families/{id}/modules/{key} |
ModuleInstance | 모듈 인스턴스 (여행 N개 등) | families/{id}/module_instances/ |
ModuleEntityRef/Link | 모듈 간 엔티티 참조 | families/{id}/entity_links/ |
ModuleActivityEvent | 액티비티 피드 이벤트 | families/{id}/activity/ |
ModuleService | 설치/정렬/즐겨찾기/QuickLaunch | Cloud Functions + Firestore |
등록된 모듈 (9개)
| 구분 | 모듈 | moduleKey |
|---|---|---|
| Core (4) | Family Core, 채널, 추억, 아카이브 | family_core, channels, memories, archives |
| Plugin (5) | 여행, 대화방, 종교, 장바구니, 집안일 | travel, chat, religion, shopping, chores |
한계
- 모듈 = 앱 번들 — 모든 모듈 코드가 Flutter 앱에 포함, 배포 없이 추가 불가
- 레지스트리 = 하드코딩 — 외부 모듈 등록 경로 없음
- 단일 개발자 — 모듈 개발이 homb 팀에 종속
2. 목표 상태 (To-Be)
┌─────────────────────────────────────────────────────┐
│ homb Platform │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ membloc-app │ │ membloc-app- │ │ Module │ │
│ │ (Flutter)│ │ engine (Go) │ │ Marketplace │ │
│ │ │ │ │ │ (Registry) │ │
│ │ Core UI │◄─┤ Auth/Family │ │ │ │
│ │ Module │ │ Module API │◄─┤ Publish/Review│ │
│ │ Runtime │ │ Marketplace │ │ Discovery │ │
│ │ │ │ API │ │ Billing │ │
│ └──────────┘ └──────────────┘ └───────────────┘ │
│ ▲ ▲ │
│ │ ┌─────────────────────┘ │
│ │ │ │
│ ┌─────┴──────────────┴───────────────────────────┐ │
│ │ Third-Party Modules │ │
│ │ ┌─────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ WebView │ │ Server │ │ Native (Partner) │ │ │
│ │ │ Module │ │ Module │ │ Module │ │ │
│ │ └─────────┘ └──────────┘ └──────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
3. 모듈 타입 설계
Flutter는 런타임 코드 로딩이 제한적이다. 외부 모듈을 지원하기 위해 3가지 모듈 타입을 정의한다.
3.1 Native Module (1st-party)
현재 방식. homb 팀이 직접 개발, 앱 번들에 포함.
execution: in-process (Flutter widget tree)
performance: ★★★★★
capabilities: 전체 Flutter API, Firestore 직접 접근
개발 주체: homb 팀
예시: channels, memories, travel, chores
3.2 WebView Module (3rd-party 주력)
외부 사업체가 웹앱으로 개발, homb 앱 내 WebView에서 실행. WeChat Mini Program / LINE LIFF 모델.
execution: WebView (sandboxed)
performance: ★★★☆☆
capabilities: homb JS Bridge API를 통해 제한된 기능 접근
개발 주체: 외부 사업체
예시: 가계부, 건강관리, 교육, 레시피
┌─────────────────────────────────┐
│ membloc-app (Flutter) │
│ ┌───────────────────────────┐ │
│ │ WebView Container │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ 3rd-party Web App │ │ │
│ │ │ │ │ │
│ │ │ homb.user.getInfo()│ │ │
│ │ │ homb.family.list() │ │ │
│ │ │ homb.storage.put() │ │ │
│ │ └────────┬────────────┘ │ │
│ │ │ JS Bridge │ │
│ └───────────┼───────────────┘ │
│ ▼ │
│ ModuleRuntime (permission check│
│ + data relay) │
└─────────────────────────────────┘
3.3 Server Module (headless)
UI 없이 백엔드에서 동작. Webhook/이벤트 기반으로 데이터를 처리하고 결과를 homb에 푸시.
execution: 외부 서버 (사업체 호스팅)
performance: 비동기
capabilities: homb REST API + Webhook 구독
개발 주체: 외부 사업체
예시: AI 사진 정리, 일정 자동 동기화, 외부 캘린더 연동
4. Module SDK & Interface
4.1 Module Manifest (homb-module.json)
모든 외부 모듈은 manifest를 제출해야 한다.
{
"manifest_version": 1,
"module_key": "com.example.budget",
"display_name": "가계부",
"description": "가족 지출을 함께 관리하세요",
"version": "1.2.0",
"min_homb_version": "2.0.0",
"type": "webview",
"author": {
"name": "Example Corp",
"email": "dev@example.com",
"website": "https://example.com"
},
"icon_url": "https://cdn.example.com/icon.png",
"entry_url": "https://modules.example.com/budget/",
"supported_entity_types": ["transaction", "budget"],
"permissions": [
"family.read",
"members.read",
"storage.write",
"notifications.send"
],
"settings_schema": {
"currency": { "type": "string", "default": "KRW", "enum": ["KRW", "USD", "JPY"] },
"monthlyBudget": { "type": "number", "default": 0 }
},
"webhook_events": ["module.installed", "module.uninstalled", "family.member_joined"],
"pricing": {
"model": "free",
"trial_days": 0
},
"category": "finance",
"tags": ["가계부", "지출", "예산"],
"screenshots": [
"https://cdn.example.com/screenshot1.png",
"https://cdn.example.com/screenshot2.png"
],
"privacy_policy_url": "https://example.com/privacy",
"support_url": "https://example.com/support"
}
4.2 JS Bridge API (WebView Module용)
// membloc JS SDK — WebView 모듈이 사용하는 API
interface MemblocBridge {
// 인증
auth: {
getToken(): Promise<string>; // 현재 사용자의 membloc 토큰
getUserInfo(): Promise<UserInfo>; // uid, displayName, photoUrl
};
// 가족
family: {
getCurrent(): Promise<Family>; // 현재 가족 정보
getMembers(): Promise<Member[]>; // 가족 멤버 목록
};
// 모듈 데이터 (sandboxed — 자기 모듈 데이터만 접근)
data: {
get(key: string): Promise<any>;
set(key: string, value: any): Promise<void>;
delete(key: string): Promise<void>;
list(prefix?: string): Promise<DataEntry[]>;
query(options: QueryOptions): Promise<DataEntry[]>;
};
// 파일 스토리지
storage: {
upload(file: File, path: string): Promise<StorageRef>;
getUrl(path: string): Promise<string>;
delete(path: string): Promise<void>;
};
// 알림
notifications: {
send(options: NotificationOptions): Promise<void>;
};
// 액티비티 피드
activity: {
post(event: ActivityEvent): Promise<void>;
};
// UI 연동
ui: {
close(): void; // 모듈 화면 닫기
showToast(message: string): void;
setTitle(title: string): void;
openEntity(ref: EntityRef): void; // 다른 모듈 엔티티로 이동
shareToChannel(data: ShareData): Promise<void>; // 채널에 공유
};
// 설정
settings: {
get(): Promise<ModuleSettings>;
update(partial: Partial<ModuleSettings>): Promise<void>;
};
}
4.3 REST API (Server Module용)
# 인증: Bearer <module_api_key>
# 모듈 데이터 CRUD
GET /api/modules/{moduleKey}/families/{familyId}/data
POST /api/modules/{moduleKey}/families/{familyId}/data
PUT /api/modules/{moduleKey}/families/{familyId}/data/{key}
DELETE /api/modules/{moduleKey}/families/{familyId}/data/{key}
# 액티비티 피드
POST /api/modules/{moduleKey}/families/{familyId}/activity
# 알림
POST /api/modules/{moduleKey}/families/{familyId}/notifications
# Webhook 수신 등록
POST /api/modules/{moduleKey}/webhooks
GET /api/modules/{moduleKey}/webhooks
DELETE /api/modules/{moduleKey}/webhooks/{id}
5. Marketplace Backend (membloc-app-engine 확장)
5.1 데이터 모델
-- 모듈 퍼블리셔 (사업체)
CREATE TABLE publishers (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
website TEXT,
logo_url TEXT,
status TEXT NOT NULL DEFAULT 'pending', -- pending, approved, suspended
firebase_uid TEXT NOT NULL, -- 퍼블리셔 계정
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 모듈 레지스트리
CREATE TABLE modules (
id TEXT PRIMARY KEY,
module_key TEXT NOT NULL UNIQUE, -- com.example.budget
publisher_id TEXT NOT NULL REFERENCES publishers(id),
display_name TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
long_description TEXT,
category TEXT NOT NULL DEFAULT 'general',
type TEXT NOT NULL DEFAULT 'webview', -- native, webview, server
icon_url TEXT,
entry_url TEXT, -- WebView entry point
manifest JSONB NOT NULL DEFAULT '{}',
permissions TEXT[] NOT NULL DEFAULT '{}',
settings_schema JSONB NOT NULL DEFAULT '{}',
pricing_model TEXT NOT NULL DEFAULT 'free', -- free, paid, freemium
price_krw INTEGER DEFAULT 0,
status TEXT NOT NULL DEFAULT 'draft', -- draft, review, published, suspended
min_homb_version TEXT DEFAULT '1.0.0',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_modules_category ON modules(category);
CREATE INDEX idx_modules_publisher ON modules(publisher_id);
CREATE INDEX idx_modules_status ON modules(status);
-- 모듈 버전
CREATE TABLE module_versions (
id TEXT PRIMARY KEY,
module_id TEXT NOT NULL REFERENCES modules(id),
version TEXT NOT NULL,
changelog TEXT DEFAULT '',
manifest JSONB NOT NULL,
entry_url TEXT,
status TEXT NOT NULL DEFAULT 'review', -- review, approved, rejected
reviewed_by TEXT,
reviewed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(module_id, version)
);
-- 모듈 설치 (가족 단위)
CREATE TABLE module_installations (
id TEXT PRIMARY KEY,
family_id TEXT NOT NULL,
module_id TEXT NOT NULL REFERENCES modules(id),
version TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active', -- active, disabled, uninstalled
settings JSONB NOT NULL DEFAULT '{}',
installed_by TEXT NOT NULL,
installed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(family_id, module_id)
);
CREATE INDEX idx_installations_family ON module_installations(family_id);
CREATE INDEX idx_installations_module ON module_installations(module_id);
-- 모듈 리뷰
CREATE TABLE module_reviews (
id TEXT PRIMARY KEY,
module_id TEXT NOT NULL REFERENCES modules(id),
family_id TEXT NOT NULL,
user_uid TEXT NOT NULL,
rating SMALLINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
title TEXT DEFAULT '',
body TEXT DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(module_id, family_id, user_uid)
);
-- 모듈 데이터 (sandboxed key-value)
CREATE TABLE module_data (
id TEXT PRIMARY KEY,
module_id TEXT NOT NULL REFERENCES modules(id),
family_id TEXT NOT NULL,
data_key TEXT NOT NULL,
data_value JSONB NOT NULL DEFAULT '{}',
created_by TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(module_id, family_id, data_key)
);
CREATE INDEX idx_module_data_lookup ON module_data(module_id, family_id);
-- Webhook 구독
CREATE TABLE module_webhooks (
id TEXT PRIMARY KEY,
module_id TEXT NOT NULL REFERENCES modules(id),
event_type TEXT NOT NULL,
target_url TEXT NOT NULL,
secret TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 모듈 API 키
CREATE TABLE module_api_keys (
id TEXT PRIMARY KEY,
module_id TEXT NOT NULL REFERENCES modules(id),
key_hash TEXT NOT NULL,
name TEXT NOT NULL DEFAULT 'default',
permissions TEXT[] NOT NULL DEFAULT '{}',
last_used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
5.2 API 설계
# === Marketplace (공개) ===
GET /api/marketplace/modules # 모듈 목록 (검색, 카테고리 필터)
GET /api/marketplace/modules/:key # 모듈 상세
GET /api/marketplace/modules/:key/reviews # 리뷰 목록
GET /api/marketplace/categories # 카테고리 목록
GET /api/marketplace/featured # 추천 모듈
# === 모듈 설치/관리 (가족 관리자) ===
POST /api/families/:id/modules/:key/install # 모듈 설치
POST /api/families/:id/modules/:key/uninstall # 모듈 제거
PATCH /api/families/:id/modules/:key/settings # 모듈 설정 변경
POST /api/families/:id/modules/:key/reviews # 리뷰 작성
# === 퍼블리셔 (사업체) ===
POST /api/publisher/register # 퍼블리셔 등록
GET /api/publisher/modules # 내 모듈 목록
POST /api/publisher/modules # 모듈 등록
PUT /api/publisher/modules/:key # 모듈 수정
POST /api/publisher/modules/:key/versions # 새 버전 제출
GET /api/publisher/modules/:key/analytics # 설치 통계
POST /api/publisher/modules/:key/api-keys # API 키 발급
# === 모듈 런타임 (모듈이 호출) ===
GET /api/runtime/:moduleKey/families/:fid/data # 모듈 데이터 조회
POST /api/runtime/:moduleKey/families/:fid/data # 모듈 데이터 저장
POST /api/runtime/:moduleKey/families/:fid/activity # 액티비티 발행
POST /api/runtime/:moduleKey/families/:fid/notify # 알림 전송
# === Admin (homb 팀) ===
GET /api/admin/modules/pending # 심사 대기 모듈
POST /api/admin/modules/:key/approve # 모듈 승인
POST /api/admin/modules/:key/reject # 모듈 반려
POST /api/admin/publishers/:id/approve # 퍼블리셔 승인
6. Flutter 앱 변경점
6.1 Dynamic Module Registry
현재: defaultModuleRegistry (하드코딩 9개)
↓
변경: NativeModuleRegistry (Core + 1st-party, 번들)
+ RemoteModuleRegistry (Marketplace에서 fetch, 캐시)
= MergedModuleRegistry
6.2 Module Runtime (WebView 실행 환경)
// 개념적 구조
class ModuleRuntime {
// WebView 모듈 실행
// - JS Bridge 주입
// - Permission 체크
// - 데이터 샌드박싱 (자기 모듈 데이터만 접근)
// - 토큰 발급 (모듈용 scoped token)
}
class ModuleWebView extends StatefulWidget {
final String moduleKey;
final String entryUrl;
final String familyId;
// WebView + JS Bridge 바인딩
}
6.3 Module Hub 확장
현재 ModuleHubScreen: 하드코딩 모듈 목록 표시
↓
변경:
├── 설치된 모듈 탭 (현재와 유사)
├── 마켓플레이스 탭 (검색, 카테고리, 추천)
└── 모듈 상세 (스크린샷, 리뷰, 권한 확인, 설치)
7. 권한 모델
7.1 Module Permissions
모듈이 요청할 수 있는 권한:
| Permission | 설명 |
|---|---|
family.read | 가족 기본 정보 읽기 |
members.read | 가족 멤버 목록 읽기 |
members.profile | 멤버 프로필 상세 |
storage.read | 모듈 파일 읽기 |
storage.write | 모듈 파일 쓰기 |
notifications.send | 푸시 알림 전송 |
activity.write | 액티비티 피드에 게시 |
calendar.read | 캘린더 읽기 |
calendar.write | 캘린더 쓰기 |
channel.share | 채널에 콘텐츠 공유 |
7.2 설치 시 권한 동의 플로우
사용자가 "설치" 클릭
→ 권한 목록 다이얼로그 표시
→ "이 모듈은 다음 권한을 요청합니다:"
✅ 가족 정보 읽기
✅ 알림 전송
✅ 채널에 공유
→ 동의 → 설치 완료
7.3 데이터 샌드박싱
- 각 모듈은 자기 모듈의 데이터만 접근 가능
module_data테이블:(module_id, family_id)스코프- Storage:
/modules/{moduleKey}/families/{familyId}/경로로 격리 - 다른 모듈 데이터에 접근하려면
EntityLink를 통한 명시적 공유만 가능
8. 보안
| 영역 | 대책 |
|---|---|
| WebView 격리 | JavaScript Bridge를 통해서만 homb 데이터 접근, 직접 Firestore 접근 불가 |
| API 인증 | 모듈별 API Key + 요청 서명 (HMAC) |
| 코드 심사 | 퍼블리셔 등록 → 모듈 제출 → homb 팀 심사 → 승인 후 게시 |
| Rate Limiting | 모듈별 API 호출 한도 (free: 1000/day, paid: 10000/day) |
| CSP | WebView Content-Security-Policy로 외부 리소스 제한 |
| 개인정보 | 모듈은 최소한의 사용자 정보만 접근, privacy_policy_url 필수 |
9. 수익 모델
| 모델 | 설명 |
|---|---|
| Free | 무료 모듈 — 마켓플레이스 활성화 목적 |
| Paid | 유료 모듈 — homb가 30% 수수료 (앱스토어 모델) |
| Freemium | 기본 무료, 프리미엄 기능 유료 — 모듈 내 결제 |
| Subscription | 월정액 모듈 — 자동 갱신, homb 결제 시스템 연동 |
10. 구현 로드맵
Phase 1: Foundation (현재 → +2개월)
목표: 백엔드 API 기반으로 모듈 레지스트리를 동적으로 전환
[ ] membloc-app-engine에 marketplace DB 스키마 추가 (마이그레이션)
[ ] Module CRUD API 구현 (publisher, modules, versions)
[ ] Flutter에서 RemoteModuleRegistry 구현
- 백엔드에서 모듈 목록 fetch
- NativeModuleRegistry와 merge
- 오프라인 캐시
[ ] ModuleHubScreen에 마켓플레이스 탭 추가
[ ] 기존 5개 plugin 모듈을 manifest 형태로 백엔드에도 등록
Phase 2: WebView Runtime (+2~4개월)
목표: 외부 모듈이 WebView에서 실행될 수 있는 환경 구축
[ ] ModuleWebView 컴포넌트 구현
[ ] JS Bridge SDK 개발 (homb.js)
- auth, family, data, storage, ui API
[ ] Module Runtime API 구현 (membloc-app-engine)
- /api/runtime/* 엔드포인트
- 모듈 데이터 CRUD
- 권한 체크 미들웨어
[ ] 권한 동의 플로우 UI
[ ] 샘플 WebView 모듈 1개 개발 (dogfooding)
Phase 3: Publisher Portal (+4~6개월)
목표: 외부 사업체가 모듈을 등록하고 관리할 수 있는 환경
[ ] Publisher 등록/인증 플로우
[ ] Publisher Dashboard (웹)
- 모듈 등록/수정
- 버전 관리
- 설치 통계/분석
- API 키 관리
[ ] 심사 시스템 (Admin Dashboard)
[ ] Module SDK 문서 사이트
[ ] Webhook 시스템 구현
Phase 4: Ecosystem (+6~8개월)
목표: 마켓플레이스 활성화 및 수익화
[ ] 모듈 리뷰/평점 시스템
[ ] 카테고리/태그 기반 검색 및 추천
[ ] 결제 시스템 연동 (유료 모듈)
[ ] Server Module 지원 (Webhook 기반)
[ ] 모듈 간 EntityLink를 통한 연동
[ ] 퍼블리셔 정산 시스템
11. 기술 스택 변화
| 영역 | 현재 | Phase 1~2 | Phase 3~4 |
|---|---|---|---|
| 모듈 레지스트리 | Flutter 하드코딩 | Go API + PostgreSQL | + Redis 캐시 |
| 모듈 실행 | Flutter widget tree | + WebView Runtime | + Server Module |
| 데이터 | Firestore only | + PostgreSQL (marketplace) | + module_data 분리 |
| 인증 | Firebase Auth | + Module API Key | + OAuth2 (퍼블리셔) |
| 파일 | Firebase Storage | 그대로 | + 모듈별 Storage 격리 |
| CI/CD | GitHub Actions | 그대로 | + Publisher CI 연동 |
12. 참고 모델
| 플랫폼 | 모듈 방식 | homb 적용점 |
|---|---|---|
| WeChat Mini Program | WebView + JS Bridge | WebView Module 설계 |
| Slack App Directory | OAuth + API | Server Module, Webhook |
| Shopify Apps | Embedded App (iframe) | WebView 격리, 권한 모델 |
| Notion Integrations | REST API + OAuth | Publisher API 설계 |
| LINE LIFF | WebView + SDK | JS Bridge API 설계 |
부록: 용어 정의
| 용어 | 정의 |
|---|---|
| Module | homb 플랫폼에서 실행되는 기능 단위 |
| Publisher | 모듈을 개발·배포하는 사업체 |
| Module Key | 모듈 고유 식별자 (역도메인: com.example.budget) |
| Module Instance | 하나의 모듈이 가족 내에서 생성한 개별 인스턴스 |
| JS Bridge | WebView 모듈과 membloc-app 간 통신 인터페이스 |
| Module Runtime | 모듈 실행 및 권한 관리를 담당하는 중간 계층 |
| Manifest | 모듈 메타데이터 정의 파일 (homb-module.json) |