docs.membloc.com

membloc docs

제품, 플랫폼, 운영 문서를 한곳에서 읽는 Membloc 공식 문서 포털입니다.

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설치/정렬/즐겨찾기/QuickLaunchCloud Functions + Firestore

등록된 모듈 (9개)

구분모듈moduleKey
Core (4)Family Core, 채널, 추억, 아카이브family_core, channels, memories, archives
Plugin (5)여행, 대화방, 종교, 장바구니, 집안일travel, chat, religion, shopping, chores

한계

  1. 모듈 = 앱 번들 — 모든 모듈 코드가 Flutter 앱에 포함, 배포 없이 추가 불가
  2. 레지스트리 = 하드코딩 — 외부 모듈 등록 경로 없음
  3. 단일 개발자 — 모듈 개발이 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)
CSPWebView 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~2Phase 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/CDGitHub Actions그대로+ Publisher CI 연동

12. 참고 모델

플랫폼모듈 방식homb 적용점
WeChat Mini ProgramWebView + JS BridgeWebView Module 설계
Slack App DirectoryOAuth + APIServer Module, Webhook
Shopify AppsEmbedded App (iframe)WebView 격리, 권한 모델
Notion IntegrationsREST API + OAuthPublisher API 설계
LINE LIFFWebView + SDKJS Bridge API 설계

부록: 용어 정의

용어정의
Modulehomb 플랫폼에서 실행되는 기능 단위
Publisher모듈을 개발·배포하는 사업체
Module Key모듈 고유 식별자 (역도메인: com.example.budget)
Module Instance하나의 모듈이 가족 내에서 생성한 개별 인스턴스
JS BridgeWebView 모듈과 membloc-app 간 통신 인터페이스
Module Runtime모듈 실행 및 권한 관리를 담당하는 중간 계층
Manifest모듈 메타데이터 정의 파일 (homb-module.json)