docs.membloc.com

membloc docs

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

Phase 4: Ecosystem — 마켓플레이스 활성화 및 수익화

마켓플레이스에 리뷰/평점, 검색/추천, 결제 시스템, Server Module 지원, 모듈 간 연동, 퍼블리셔 정산을 추가하여 자생적 생태계를 완성한다.


1. 목표

  • 모듈 리뷰/평점 시스템
  • 검색/추천 엔진
  • 유료 모듈 결제 시스템
  • Server Module 지원 (Webhook 기반 headless)
  • 모듈 간 EntityLink 연동
  • 퍼블리셔 정산 시스템
  • 플랫폼 거버넌스 (정책, 분쟁 처리)

2. 리뷰/평점 시스템

2.1 데이터베이스

migrations/006_reviews.up.sql

CREATE TABLE module_reviews (
    id          TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
    module_key  TEXT NOT NULL,
    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 '',
    status      TEXT NOT NULL DEFAULT 'visible'
                CHECK (status IN ('visible','hidden','flagged')),
    helpful_count INTEGER NOT NULL DEFAULT 0,
    publisher_reply TEXT,
    publisher_reply_at TIMESTAMPTZ,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE(module_key, family_id, user_uid)
);

CREATE INDEX idx_reviews_module ON module_reviews(module_key);
CREATE INDEX idx_reviews_rating ON module_reviews(module_key, rating);

-- 리뷰 유용성 투표
CREATE TABLE review_votes (
    review_id   TEXT NOT NULL REFERENCES module_reviews(id) ON DELETE CASCADE,
    user_uid    TEXT NOT NULL,
    is_helpful  BOOLEAN NOT NULL,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (review_id, user_uid)
);

2.2 API

# 리뷰 조회
GET    /api/marketplace/modules/:key/reviews
       ?sort=recent|helpful|rating_high|rating_low&page=1

# 리뷰 작성 (인증 필요, 설치한 사용자만)
POST   /api/families/:fid/modules/:key/reviews
       Body: { "rating": 5, "title": "...", "body": "..." }

# 리뷰 수정
PUT    /api/families/:fid/modules/:key/reviews/:id

# 리뷰 유용성 투표
POST   /api/marketplace/reviews/:id/vote
       Body: { "helpful": true }

# 퍼블리셔 답변
POST   /api/publisher/modules/:key/reviews/:id/reply
       Body: { "reply": "감사합니다..." }

2.3 평점 집계

모듈 설치 후 7일 경과 시 → 앱 내 리뷰 요청 다이얼로그 (1회만)

평점 계산:
  modules.rating_avg = AVG(module_reviews.rating)
  modules.rating_count = COUNT(module_reviews)

  → 리뷰 INSERT/UPDATE/DELETE 시 트리거로 자동 갱신

2.4 Flutter UI

ModuleDetailScreen
└── 리뷰 섹션
    ├── 평점 요약 (별점 분포 바 차트)
    ├── 리뷰 목록
    │   ├── 작성자 (익명: "가족A")
    │   ├── 별점 + 날짜
    │   ├── 본문
    │   ├── 퍼블리셔 답변 (있으면)
    │   └── [유용해요] 버튼
    └── [리뷰 쓰기] 버튼 (설치한 사용자만)

3. 검색/추천 엔진

3.1 검색

현재 (Phase 1): display_name ILIKE '%query%' — 충분

Phase 4 확장:

-- PostgreSQL Full-Text Search (한국어)
ALTER TABLE modules ADD COLUMN search_vector tsvector;

-- 트리거로 자동 업데이트
CREATE OR REPLACE FUNCTION update_module_search_vector() RETURNS trigger AS $$
BEGIN
  NEW.search_vector :=
    setweight(to_tsvector('simple', coalesce(NEW.display_name, '')), 'A') ||
    setweight(to_tsvector('simple', coalesce(NEW.description, '')), 'B') ||
    setweight(to_tsvector('simple', coalesce(array_to_string(
      (SELECT array_agg(t) FROM jsonb_array_elements_text(
        coalesce(NEW.manifest->'tags', '[]'::jsonb)
      ) t), ' '), '')), 'C');
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_module_search_vector
  BEFORE INSERT OR UPDATE ON modules
  FOR EACH ROW EXECUTE FUNCTION update_module_search_vector();

CREATE INDEX idx_modules_search ON modules USING gin(search_vector);

검색 API 확장:

GET /api/marketplace/modules?q=가계부&sort=relevance

sort 옵션:
  - relevance (FTS 랭킹)
  - popular (install_count DESC)
  - rating (rating_avg DESC, rating_count DESC)
  - recent (created_at DESC)

3.2 추천

추천 알고리즘 (규칙 기반 → ML 이전 단계):

1. 인기 모듈: install_count 상위 + rating_avg >= 4.0
2. 카테고리 기반: 사용자가 설치한 모듈의 카테고리와 같은 카테고리
3. 함께 설치: "이 모듈을 설치한 가족이 함께 사용하는 모듈"
4. 신규 모듈: 최근 2주 내 published, rating_avg >= 4.0
5. 에디터 추천: modules.manifest->'featured' = true (수동 선정)

추천 API:

GET /api/marketplace/featured
Response:
{
  "banner": [ModuleSummary],             # 에디터 추천 (최대 5개)
  "popular": [ModuleSummary],            # 인기 TOP 10
  "newAndNoteworthy": [ModuleSummary],   # 신규 + 고평점
  "forYou": [ModuleSummary],             # 개인화 (설치 기반)
  "byCategory": {
    "lifestyle": [ModuleSummary],
    "finance": [ModuleSummary],
    ...
  }
}

3.3 Flutter 마켓플레이스 UI 확장

MarketplaceTab (Phase 1)
    ↓ Phase 4 확장
├── 추천 배너 (PageView 슬라이더)
├── 검색바 (자동완성)
├── 카테고리 가로 스크롤 칩
├── "인기 모듈" 가로 카드 스크롤
├── "새로 나온 모듈" 가로 카드 스크롤
├── "당신의 가족에게 추천" 가로 카드 스크롤
└── 전체 모듈 그리드 (무한 스크롤)

4. 결제 시스템

4.1 구조

사용자 ──[결제]──▶ 앱스토어 (IAP)  ──▶ homb 서버 (영수증 검증)
                   또는                    │
                   PG (토스페이먼츠)        │
                                          ▼
                                   ┌──────────────┐
                                   │ 정산 시스템    │
                                   │ 70% 퍼블리셔  │
                                   │ 30% homb     │
                                   └──────────────┘

4.2 결제 모델

모델설명결제 방식
Free무료-
Paid1회 구매IAP 또는 PG
Freemium기본 무료, 프리미엄 기능 유료모듈 내 IAP
Subscription월정액IAP 구독 또는 PG 정기결제

4.3 데이터베이스

migrations/007_billing.up.sql

-- 결제 내역
CREATE TABLE payments (
    id              TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
    family_id       TEXT NOT NULL,
    module_key      TEXT NOT NULL,
    user_uid        TEXT NOT NULL,
    amount_krw      INTEGER NOT NULL,
    currency        TEXT NOT NULL DEFAULT 'KRW',
    payment_method  TEXT NOT NULL CHECK (payment_method IN ('iap_apple','iap_google','pg_toss')),
    transaction_id  TEXT NOT NULL UNIQUE,
    receipt_data    TEXT,
    status          TEXT NOT NULL DEFAULT 'completed'
                    CHECK (status IN ('pending','completed','refunded','failed')),
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_payments_family ON payments(family_id);
CREATE INDEX idx_payments_module ON payments(module_key);
CREATE INDEX idx_payments_transaction ON payments(transaction_id);

-- 구독
CREATE TABLE subscriptions (
    id              TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
    family_id       TEXT NOT NULL,
    module_key      TEXT NOT NULL,
    user_uid        TEXT NOT NULL,
    plan            TEXT NOT NULL,
    amount_krw      INTEGER NOT NULL,
    billing_cycle   TEXT NOT NULL CHECK (billing_cycle IN ('monthly','yearly')),
    status          TEXT NOT NULL DEFAULT 'active'
                    CHECK (status IN ('active','cancelled','expired','past_due')),
    current_period_start TIMESTAMPTZ NOT NULL,
    current_period_end   TIMESTAMPTZ NOT NULL,
    cancelled_at    TIMESTAMPTZ,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE(family_id, module_key)
);

-- 퍼블리셔 정산
CREATE TABLE publisher_payouts (
    id              TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
    publisher_id    TEXT NOT NULL REFERENCES publishers(id),
    period_start    DATE NOT NULL,
    period_end      DATE NOT NULL,
    gross_amount    INTEGER NOT NULL,       -- 총 매출
    platform_fee    INTEGER NOT NULL,       -- homb 수수료 (30%)
    net_amount      INTEGER NOT NULL,       -- 정산 금액 (70%)
    status          TEXT NOT NULL DEFAULT 'pending'
                    CHECK (status IN ('pending','processing','completed','failed')),
    paid_at         TIMESTAMPTZ,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_payouts_publisher ON publisher_payouts(publisher_id);
CREATE INDEX idx_payouts_period ON publisher_payouts(period_start, period_end);

-- 퍼블리셔 계좌 정보
CREATE TABLE publisher_bank_accounts (
    id              TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
    publisher_id    TEXT NOT NULL REFERENCES publishers(id) UNIQUE,
    bank_code       TEXT NOT NULL,
    account_number  TEXT NOT NULL,          -- 암호화 저장
    account_holder  TEXT NOT NULL,
    verified        BOOLEAN NOT NULL DEFAULT false,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

4.4 API

# 결제
POST   /api/families/:fid/modules/:key/purchase
       Body: { "paymentMethod": "pg_toss", "amount": 3900 }

POST   /api/families/:fid/modules/:key/verify-receipt
       Body: { "platform": "apple", "receiptData": "..." }

# 구독
POST   /api/families/:fid/modules/:key/subscribe
       Body: { "plan": "premium", "billingCycle": "monthly" }
POST   /api/families/:fid/modules/:key/cancel-subscription

# 퍼블리셔 정산
GET    /api/publisher/payouts                      # 정산 내역
GET    /api/publisher/payouts/:id                  # 정산 상세
GET    /api/publisher/revenue                      # 수익 요약
       ?from=2026-01&to=2026-04
PUT    /api/publisher/bank-account                 # 계좌 등록/수정

4.5 Flutter 결제 UI

ModuleDetailScreen
└── 가격 표시
    ├── "무료" → [설치] 버튼
    ├── "₩3,900" → [구매] 버튼
    │   └── 결제 시트 (토스페이먼츠 SDK 또는 IAP)
    └── "월 ₩1,900" → [구독] 버튼
        └── 구독 안내 + 결제

설정 > 구독 관리
├── 활성 구독 목록
├── 결제 내역
└── [해지] 버튼

5. Server Module 지원

5.1 개요

UI 없이 백엔드에서만 동작하는 모듈. 외부 사업체가 자체 서버를 운영하며, homb의 Webhook을 수신하고 REST API로 데이터를 읽고 쓴다.

5.2 인증

Server Module → homb Runtime API
  Authorization: Bearer <module_api_key>

homb → Server Module (Webhook)
  X-Homb-Signature: sha256={HMAC(payload, webhook_secret)}

5.3 동작 예시: AI 사진 정리 모듈

1. 가족이 "AI 사진 정리" 모듈 설치
2. homb → Webhook: module.installed
3. 모듈 서버: API 키로 homb Runtime API 접근
4. 새 사진 업로드 시 → Webhook: memory.created (향후 이벤트 확장)
5. 모듈 서버: 사진 분석 → 태그/앨범 자동 분류
6. 모듈 서버 → homb Runtime API: activity.post("사진 20장을 자동 분류했어요")
7. 모듈 서버 → homb Runtime API: notifications.send("새 앨범이 생성되었어요")

5.4 추가 Webhook 이벤트 (Phase 4 확장)

이벤트트리거
memory.created새 추억(사진) 생성
memory.updated추억 수정
calendar.event_created캘린더 이벤트 생성
channel.message_sent채널 메시지 전송
module.data_changed모듈 데이터 변경

5.5 모듈 건강 상태 모니터링

매 5분: homb → GET {module_health_check_url}
  - 200 → healthy
  - 그 외 → unhealthy (3회 연속 시 사용자에게 알림)

모듈 manifest에 추가:
  "health_check_url": "https://api.example.com/health"

6. 모듈 간 EntityLink 연동

6.1 개요

모듈 간 데이터를 안전하게 공유하는 메커니즘. 직접 데이터 접근이 아닌, 참조(링크) 기반.

6.2 시나리오

여행 모듈 ←→ 가계부 모듈

1. 여행 모듈에서 "제주도 여행" trip 생성
2. 가계부 모듈에서 지출 기록 시 "관련 여행" 선택
3. EntityLink 생성:
   source: { moduleKey: "com.homb.budget", entityType: "transaction", entityId: "tx_001" }
   target: { moduleKey: "travel", entityType: "trip", entityId: "trip_001" }
   relationType: "expense_of"
4. 여행 모듈에서 "이 여행의 총 지출" 표시 가능

6.3 API

# EntityLink 생성 (양쪽 모듈 모두 설치되어 있어야 함)
POST   /api/runtime/:moduleKey/families/:fid/entity-links
       Body: {
         "targetModuleKey": "travel",
         "targetEntityType": "trip",
         "targetEntityId": "trip_001",
         "sourceEntityType": "transaction",
         "sourceEntityId": "tx_001",
         "relationType": "expense_of"
       }

# 내 모듈 엔티티에 연결된 링크 조회
GET    /api/runtime/:moduleKey/families/:fid/entity-links
       ?entityType=transaction&entityId=tx_001

# 다른 모듈의 링크 가능한 엔티티 목록 (읽기 전용)
GET    /api/runtime/:moduleKey/families/:fid/linkable-entities
       ?targetModuleKey=travel&targetEntityType=trip

6.4 권한

  • EntityLink 생성: 양쪽 모듈이 모두 entity_link.write 권한을 manifest에 선언
  • 링크 대상 엔티티 조회: entity_link.read 권한
  • 실제 데이터 접근: 불가 (메타데이터만 — entityId, entityType, label)

7. 플랫폼 거버넌스

7.1 개발자 정책

1. 모듈은 명시된 권한 범위 내에서만 데이터를 수집/처리해야 한다
2. 사용자 데이터를 외부로 전송할 경우 개인정보처리방침에 명시해야 한다
3. 광고를 포함하는 모듈은 manifest에 "contains_ads": true 표시해야 한다
4. 악의적 행위(스팸, 피싱, 악성코드) 발견 시 즉시 정지 처리
5. 불법 콘텐츠, 차별, 혐오 표현을 포함하는 모듈은 반려/정지
6. API Rate Limit을 우회하려는 시도 시 API 키 폐기

7.2 분쟁 처리

사용자 신고
    ↓
homb 팀 확인 (48시간 이내)
    ├── 정당한 신고 → 모듈 정지 + 퍼블리셔에 시정 요구
    │   ├── 시정 완료 → 정지 해제
    │   └── 미시정 (7일) → 영구 삭제
    └── 부당한 신고 → 기각 + 사용자에 안내

7.3 환불 정책

- 구매 후 48시간 이내: 무조건 환불
- 구매 후 48시간 ~ 7일: 모듈 미사용 시 환불
- 구매 후 7일 이후: 환불 불가 (퍼블리셔 직접 협의)
- 구독: 현재 결제 기간 종료 시 해지, 비례 환불 없음

8. 작업 목록

8.1 리뷰/평점

#작업예상
R1006_reviews.up.sql 마이그레이션0.5d
R2리뷰 CRUD API (model, repo, service, handler)2d
R3평점 트리거 (INSERT/UPDATE → modules.rating_avg 갱신)0.5d
R4유용성 투표 API0.5d
R5퍼블리셔 답변 API0.5d
R6Flutter 리뷰 섹션 UI1.5d
R7Flutter 리뷰 작성 다이얼로그0.5d
R8리뷰 요청 다이얼로그 (설치 7일 후)0.5d

8.2 검색/추천

#작업예상
S1PostgreSQL FTS 설정 + search_vector0.5d
S2검색 API 확장 (relevance 정렬)0.5d
S3추천 알고리즘 구현 (규칙 기반)1.5d
S4/api/marketplace/featured 확장0.5d
S5Flutter 마켓플레이스 탭 리디자인 (배너, 섹션)2d
S6Flutter 검색 자동완성0.5d

8.3 결제

#작업예상
P1007_billing.up.sql 마이그레이션0.5d
P2결제 모델/서비스 구현2d
P3토스페이먼츠 PG 연동2d
P4Apple IAP 영수증 검증1.5d
P5Google Play IAP 영수증 검증1.5d
P6구독 관리 (생성, 갱신, 해지)2d
P7정산 시스템 (월별 집계, 정산서 생성)2d
P8퍼블리셔 계좌 등록/검증1d
P9정산 대시보드 (Publisher Dashboard)1d
P10Flutter 결제 UI2d
P11Flutter 구독 관리 UI1d

8.4 Server Module

#작업예상
SM1추가 Webhook 이벤트 정의 + 발송 연동1.5d
SM2모듈 건강 상태 모니터링 (cron)1d
SM3Server Module 전용 manifest 필드0.5d
SM4샘플 Server Module 개발 (AI 사진 태깅 등)3d

8.5 EntityLink

#작업예상
E1EntityLink API (생성, 조회)1d
E2linkable-entities 조회 API0.5d
E3Flutter 엔티티 링크 선택 UI1d
E4JS Bridge에 entityLink API 추가0.5d

8.6 거버넌스

#작업예상
G1신고 API + Admin 신고 관리1d
G2환불 API + 처리 로직1d
G3개발자 정책 문서 작성0.5d
G4퍼블리셔 약관 문서 작성0.5d

9. 완료 기준

  • 사용자가 모듈에 리뷰/평점을 남기고, 모듈 상세에 표시됨
  • 마켓플레이스에서 키워드 검색 시 관련도 기반 결과 반환
  • 추천 섹션에 인기/신규/개인화 모듈 표시
  • 유료 모듈 구매 → 결제 → 설치 전체 플로우 동작
  • 구독 모듈 가입/해지 동작
  • 퍼블리셔가 월별 정산 내역을 대시보드에서 확인
  • Server Module이 Webhook 수신 → API 호출 → 결과 반영 동작
  • 두 모듈 간 EntityLink 생성/조회 동작
  • 사용자 신고 → Admin 처리 → 모듈 정지 플로우 동작
  • 환불 요청 → 자동/수동 처리 동작

10. Phase 4 이후 — 장기 로드맵

항목설명
ML 추천사용 패턴 기반 개인화 추천 (collaborative filtering)
모듈 번들여러 모듈을 묶어 할인 판매 ("신혼부부 패키지")
퍼블리셔 등급Bronze → Silver → Gold (실적 기반, 수수료 차등)
A/B 테스트모듈 스크린샷/설명 A/B 테스트 도구
국제화다국어 모듈 manifest, 현지화된 마켓플레이스
파트너십대형 서비스 연동 (은행, 학교, 병원 등)
Open API v2GraphQL 지원, 실시간 구독 (SSE/WebSocket)