広告配信管理システムの開発事例 — 管理画面設計のベストプラクティス
プロジェクト概要 — 広告配信を一元管理するシステムの構築
デジタル広告市場の拡大に伴い、広告主・メディア・配信ルールを効率的に管理する仕組みへの需要が高まっています。本プロジェクトでは、広告枠の登録から配信設定、パフォーマンス分析までを一元的に行える広告配信管理システムを開発しました。
従来、クライアント企業では広告運用をスプレッドシートとメールベースで管理しており、数百を超える広告枠の管理に月間40時間以上の工数が発生していました。この課題を解決するため、直感的に操作でき、かつ大量データの処理にも耐えうる管理画面システムの構築を目指しました。
システム全体アーキテクチャ
システムは大きく3つのレイヤーで構成されています。
- フロントエンド(管理画面): Next.js 14 + TypeScript + shadcn/ui。App Routerを採用し、Server Componentsでの初期データ取得とClient Componentsでのインタラクションを使い分け
- バックエンド(API・データ管理): Supabase(PostgreSQL + PostgREST + RLS)。カスタムスキーマで広告関連テーブルを管理し、RLSで権限制御
- 分析基盤: 配信ログの集計とKPI算出。PostgreSQLのマテリアライズドビューで高速な集計を実現
管理画面UI設計の5つの原則
管理画面の品質は、運用チームの生産性に直結します。本プロジェクトでは、数百以上の管理画面を設計してきた経験から導き出した5つの原則を適用しました。
原則1: 一覧性 — 必要な情報に一目でアクセスできること
広告枠の一覧画面では、ステータス、配信中のクリエイティブ数、直近のCTR、収益額をカラムとして表示。行の背景色でステータスを視覚的に識別できるようにしています。数値カラムにはスパークラインを配置し、トレンドを一目で把握可能にしました。
原則2: 検索性 — 大量データの中から瞬時に目的のデータにたどり着けること
ファセット検索(複合条件検索)を実装し、ステータス、カテゴリ、日付範囲、広告主名など、複数の条件を組み合わせた絞り込みが可能です。URLパラメータに検索条件を保持するため、フィルタ状態のブックマークや共有もできます。
// フィルタ状態をURLパラメータで管理する例
interface FilterState {
status: ('active' | 'paused' | 'draft')[]
category: string[]
dateRange: { from: string; to: string }
search: string
}
// URLパラメータからフィルタ状態を復元
function useFilterParams(): FilterState {
const searchParams = useSearchParams()
return {
status: searchParams.getAll('status'),
category: searchParams.getAll('category'),
dateRange: {
from: searchParams.get('from') ?? '',
to: searchParams.get('to') ?? ''
},
search: searchParams.get('q') ?? ''
}
}
原則3: バルク操作 — 繰り返し作業を効率化すること
チェックボックスによる複数選択と、一括操作メニューを実装。「一括承認」「一括停止」「一括削除」「CSVエクスポート」といった操作を、選択した広告枠に対してまとめて実行できます。
// バルク操作の実装パターン
async function handleBulkAction(
action: 'approve' | 'reject' | 'pause' | 'delete',
selectedIds: string[]
) {
// 確認ダイアログの表示
const confirmed = await showConfirmDialog({
title: `${selectedIds.length}件を${actionLabels[action]}しますか?`,
description: 'この操作は元に戻せません。'
})
if (!confirmed) return
// バッチ更新の実行
const { error } = await supabase
.from('ad_slots')
.update({ status: actionStatusMap[action] })
.in('id', selectedIds)
if (!error) {
toast.success(`${selectedIds.length}件を${actionLabels[action]}しました`)
router.refresh()
}
}
原則4: リアルタイム更新 — 最新のデータが常に表示されること
Supabaseのリアルタイム機能を活用し、他のユーザーが行った変更がリアルタイムに画面に反映されます。配信中の広告のパフォーマンスデータも、ダッシュボード上で自動更新されます。オプティミスティックUI更新を採用し、操作の即時フィードバックも実現しています。
原則5: 権限管理 — ロールに応じた操作制限
3つのロール(管理者・運用者・閲覧者)を定義し、各ロールに応じてUIの表示・非表示を制御します。閲覧者には編集ボタンを表示せず、運用者には削除機能を無効化するなど、UIレベルでの制限とRLSによるデータレベルでの制限を二重に実装しています。
データテーブル設計 — ソート・フィルタ・ページネーション
大量の広告データを扱うテーブルコンポーネントは、管理画面の核となる要素です。以下の機能を汎用的に実装しました。
サーバーサイドページネーション
数万件のデータをクライアントサイドで処理するのは非現実的です。PostgreSQLのLIMIT/OFFSETを活用し、サーバーサイドでページネーションを処理しています。1ページあたりの表示件数は25/50/100から選択可能です。
マルチカラムソート
複数カラムでの複合ソートに対応。「まずステータスで並べ替え、次にCTRの降順」といった操作が可能です。ソート状態もURLパラメータに保持されます。
インラインフィルタ
各カラムヘッダーにフィルタアイコンを配置し、カラム単位のフィルタリングが可能。テキストカラムは部分一致検索、数値カラムは範囲指定、ENUMカラムはドロップダウン選択と、カラムの型に応じたフィルタUIを自動的に切り替えます。
React + TypeScriptでのコンポーネント設計
管理画面の各UIパーツは、再利用性と型安全性を重視してコンポーネント化しています。
// 汎用テーブルコンポーネントの型定義
interface DataTableProps<T> {
columns: ColumnDef<T>[]
data: T[]
totalCount: number
pageSize: number
currentPage: number
onPageChange: (page: number) => void
onSort: (column: string, direction: 'asc' | 'desc') => void
onFilter: (filters: Record<string, unknown>) => void
bulkActions?: BulkAction[]
isLoading?: boolean
}
// 広告枠テーブルの使用例
<DataTable<AdSlot>
columns={adSlotColumns}
data={adSlots}
totalCount={totalCount}
pageSize={25}
currentPage={page}
onPageChange={setPage}
onSort={handleSort}
onFilter={handleFilter}
bulkActions={[
{ label: '一括承認', action: 'approve', variant: 'default' },
{ label: '一括停止', action: 'pause', variant: 'secondary' },
{ label: '一括削除', action: 'delete', variant: 'destructive' }
]}
/>
shadcn/uiベースのUI構築
UIコンポーネントにはshadcn/uiを採用しました。Headless UIの設計思想により、スタイルの完全なカスタマイズが可能でありながら、アクセシビリティの担保やキーボードナビゲーションは標準で提供されます。
主に使用したコンポーネントは以下の通りです。
- Table: データテーブルの基盤(@tanstack/react-tableと組み合わせ)
- Dialog: 作成・編集フォームのモーダル表示
- Command: コマンドパレット(キーボードショートカットでの操作)
- Sheet: 詳細パネルのサイドドロワー表示
- Toast: 操作結果のフィードバック通知
- DropdownMenu: コンテキストメニュー・バルクアクション
ダッシュボードの指標設計
広告配信管理において、正確なKPIモニタリングは収益最大化の鍵です。ダッシュボードに表示する主要指標を以下のように設計しました。
- CTR(Click Through Rate): クリック数 / インプレッション数 x 100。広告のクリエイティブ品質の指標
- CPM(Cost Per Mille): 1,000インプレッションあたりの広告費。収益効率の指標
- Fill Rate: 広告リクエストに対する配信率。在庫の活用度を示す
- eCPM(Effective CPM): 実効CPM。複数の課金モデルを統一的に比較
- Revenue: 総収益額。日次/週次/月次で推移を表示
- Active Slots: 現在配信中の広告枠数
各指標には前日比・前週比の変動率を表示し、トレンドを矢印アイコンで視覚的に示しています。異常値を検知した場合はアラートバッジで通知する仕組みも実装しました。
成果と振り返り
システム導入後、広告運用チームの作業時間は月間40時間から24時間へ、40%の削減を達成しました。特にバルク操作の導入により、従来1枠ずつ行っていたステータス変更が一括で可能となり、キャンペーン切り替え時の作業時間が10分の1に短縮されました。
リアルタイムモニタリングにより、配信異常の検知が平均30分から5分以内に改善。CPMの低下やFill Rateの急落にも迅速に対応できるようになり、機会損失の抑制にもつながっています。
管理画面の設計で最も重要なのは、「ユーザーが何を達成したいか」を起点に設計することです。機能を増やすことではなく、運用者の日常業務を効率化する導線を設計することが、成功の鍵だと改めて実感しました。