RORK LABEN
PRODUCT — Rork Maxがネイティブ Swift アプリを生成。iPhone・iPad・Apple Watch・Apple TV・Vision Pro・iMessageに対応しますNATIVE — Rork MaxはAR/LiDAR・Metalの3Dゲーム・Dynamic Island・Live Activities・HealthKit・Core MLなどを解放しますCLASSIC — 通常のRorkはReact Native(Expo)で、英語の説明だけからiOS/Androidアプリを生成しストア配信できますFUNDING — a16zから$2.8Mを調達(別途$15Mも)。月743,000訪問・成長率85%と伸びていますPRICING — 無料で始められ、有料プランは月25ドル〜。Rork Maxは月200ドルですCHOICE — クロスプラットフォームのRorkか、Apple専用機能まで踏み込むRork Maxか、用途で選び分けられますPRODUCT — Rork Maxがネイティブ Swift アプリを生成。iPhone・iPad・Apple Watch・Apple TV・Vision Pro・iMessageに対応しますNATIVE — Rork MaxはAR/LiDAR・Metalの3Dゲーム・Dynamic Island・Live Activities・HealthKit・Core MLなどを解放しますCLASSIC — 通常のRorkはReact Native(Expo)で、英語の説明だけからiOS/Androidアプリを生成しストア配信できますFUNDING — a16zから$2.8Mを調達(別途$15Mも)。月743,000訪問・成長率85%と伸びていますPRICING — 無料で始められ、有料プランは月25ドル〜。Rork Maxは月200ドルですCHOICE — クロスプラットフォームのRorkか、Apple専用機能まで踏み込むRork Maxか、用途で選び分けられます
記事一覧/開発ツール
開発ツール/2026-06-23上級

ゲーミフィケーションでDAUは増えたのに継続率が動かなかった — Rork運用で効いた設計の立て直し

ポイント・バッジ・リーダーボードを入れるとDAUは上がりますが、継続率は別物です。サーバー権威のポイント台帳、燃え尽きないストリーク、新規を萎えさせないリーグ設計まで、Rork運用で効いた立て直しを実装コード付きで整理します。

ゲーミフィケーションリテンション15Rork441Supabase30React Native177リーダーボード

プレミアム記事

ポイントとバッジとリーダーボードを自分のアプリに入れたとき、デイリーアクティブユーザーは確かに増えました。ログイン直後にポイントが跳ね、バッジが解放され、数字は気持ちよく動きます。ところが翌月、7日後継続率(D7 retention)のコホートを並べてみると、導入前とほとんど変わっていませんでした。むしろ一部のユーザーは、ストリークが途切れた翌日に静かに離れていきました。

DAUが増えたのは「同じ人が習慣的にログインを叩いていた」だけで、新しく定着した人が増えたわけではなかったのです。これは個人開発で癒し系・壁紙系のアプリを運用してきたなかで、私自身が一番手痛く学んだことのひとつでした。ゲーミフィケーションは「楽しさで関係を強くする仕組み」ですが、設計を一段間違えると「虚栄の指標を増やすだけの仕組み」になります。

この記事は、入れたあとに気づいた3つの落とし穴と、それをどう立て直したかの運用メモです。基本的な実装(ポイント加算フックやバッジ判定)はすでに動いている前提で、その先の「効くゲーミフィケーション」に踏み込みます。

ローカル保存のポイントは「測れない」「改ざんできる」

最初の実装では、ポイントもストリークも AsyncStorage にローカル保存していました。手軽で動きは速いのですが、運用に入ってすぐ二つの問題に直面します。

一つは測定できないこと。ポイントが端末の中にしかないと、サーバー側には「どのアクションが本当にリテンションに効いているか」のデータが残りません。バッジAを取った人とBを取った人で、30日後にどちらが残っているか——この比較ができないと、ゲーミフィケーションは改善のループに乗りません。

もう一つは改ざんできること。リーダーボードに送る数値がクライアント発信だと、端末側でいくらでも書き換えられます。一人開発のニッチアプリでも、ランキング上位にいたずらスコアが並ぶと、まじめなユーザーの意欲を一瞬で削ぎます。

結論として、ポイントの「正本」はサーバーに置くべきです。クライアントは「このアクションが起きた」という事実だけを送り、点数の確定とランキングはサーバーが行います。

サーバー権威のポイント台帳を冪等に設計する

ここで一番つまずくのが「同じアクションを二重に加点してしまう」問題です。ネットワークが不安定なモバイルでは、リクエストの再送は日常的に起きます。タップが二回送られれば二回加点される——これを防ぐ鍵が**冪等性(idempotency)**です。

クライアントはアクションごとに一意のIDを生成し、サーバーはそのIDを台帳に記録します。同じIDが再送されても、二度目は加点せずに現在値を返すだけにします。

台帳テーブルとアトミックな加点RPC

-- Supabase SQL Editor で実行
-- 1) ポイント台帳(1行 = 1トランザクション)。idempotency_key で二重加点を防ぐ
create table public.point_ledger (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references auth.users(id) on delete cascade,
  action text not null,
  points integer not null,
  idempotency_key text not null,
  created_at timestamptz not null default now(),
  constraint uq_idem unique (user_id, idempotency_key)
);
 
-- 2) 集計済みスコア(リーダーボード用)
create table public.user_scores (
  user_id uuid primary key references auth.users(id) on delete cascade,
  display_name text not null,
  total_points integer not null default 0,
  updated_at timestamptz not null default now()
);
 
-- 3) サーバー権威の加点関数。点数表はサーバーが持つ(クライアントは送らない)
create or replace function public.award_points(
  p_action text,
  p_idempotency_key text
) returns integer
language plpgsql
security definer
as $$
declare
  v_user uuid := auth.uid();
  v_points integer;
  v_total integer;
begin
  if v_user is null then
    raise exception 'not authenticated';
  end if;
 
  -- 点数はサーバー側のソース・オブ・トゥルース
  v_points := case p_action
    when 'daily_login'   then 10
    when 'complete_task' then 25
    when 'share_content' then 15
    when 'invite_friend' then 50
    else 0
  end;
 
  if v_points = 0 then
    raise exception 'unknown action: %', p_action;
  end if;
 
  -- 冪等性: 同じキーが既にあれば加点せず現在値を返す
  begin
    insert into public.point_ledger(user_id, action, points, idempotency_key)
    values (v_user, p_action, v_points, p_idempotency_key);
  exception when unique_violation then
    select total_points into v_total from public.user_scores where user_id = v_user;
    return coalesce(v_total, 0);
  end;
 
  -- 集計を1文でアトミックに更新(読み取り→書き込みの競合を避ける)
  insert into public.user_scores(user_id, display_name, total_points)
  values (v_user, coalesce((auth.jwt() ->> 'name'), 'Player'), v_points)
  on conflict (user_id)
  do update set total_points = public.user_scores.total_points + excluded.total_points,
                updated_at = now()
  returning total_points into v_total;
 
  return v_total;
end;
$$;

ポイントは RETURNING を使って一文で加算しています。アプリ側で「現在値を読んで、足して、書き戻す」と、二つの端末やリクエストが同時に走ったときに片方の加算が消えます。データベースに計算を委ねるのが安全です。

クライアント側は「事実」と「キー」だけを送る

// lib/awardPoints.ts — 冪等キー付きでサーバーに加点を依頼する
import * as Crypto from 'expo-crypto';
import { supabase } from './supabase';
 
type Action = 'daily_login' | 'complete_task' | 'share_content' | 'invite_friend';
 
export async function awardPoints(action: Action): Promise<number | null> {
  // アクション1回ごとに一意キーを発行(再送されても同じ結果になる)
  const idempotencyKey = Crypto.randomUUID();
 
  const { data, error } = await supabase.rpc('award_points', {
    p_action: action,
    p_idempotency_key: idempotencyKey,
  });
 
  if (error) {
    // 失敗時は同じキーで安全に再試行できる(=二重加点しない)
    console.warn('award_points failed:', error.message);
    return null;
  }
  return data as number; // サーバーが確定した合計ポイント
}
 
// 使用例:
// const total = await awardPoints('complete_task');
// total が更新後の合計。UI はこの値を信頼して描画する

ここで重要なのは、daily_login のキーは「1日に1回」しか有効にならないように、キー生成側で日付を混ぜる手もあるという点です。ランダムUUIDのままだと同じ日に何度もログイン加点できてしまうので、daily_login だけは `daily_login:${todayYYYYMMDD}` のような決定的キーにし、二回目以降は unique_violation で弾く設計にしています。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
ローカル保存のポイントが「測れない・改ざんできる」二重の弱点を持つ理由と、Supabase RPCによる冪等なサーバー権威台帳の実装
ストリークを切らさないための猶予日(streak freeze)をサーバー側で安全に計算するロジック
全体ランキングが新規ユーザーを萎えさせる構造と、相対リーグ・自分の前後だけを見せるウィンドウ表示の設計
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

開発ツール2026-04-19
RorkアプリにオフラインファーストアーキテクチャをSupabase同期で実装する
WatermelonDB + Supabase Realtimeを使ってRorkアプリにオフラインファーストを実装する方法を解説します。ローカルキャッシュ・楽観的更新・コンフリクト解決まで本番設計を徹底的に解説します。
開発ツール2026-04-07
Rork Max × Liveblocks / Yjs:リアルタイム協調編集アプリ開発
Rork MaxアプリにLiveblocksとYjsを統合し、複数ユーザーが同時編集できるリアルタイム協調機能を実装する完全ガイド。CRDTの仕組みから本番運用まで解説します。
開発ツール2026-04-02
Rork でアンケートアプリを作る入門ガイド — フォーム設計・回答収集・集計グラフの実装
Rork を使ってアンケート収集アプリをゼロから作る方法を解説。フォーム設計・複数回答形式・Supabase によるデータ保存・集計グラフ表示まで、初心者でも 1 日で完成できる実践ガイドです。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →