はじめに
この記事でわかる・できること
Amplify Gen2 を使って作成した、1万人規模アンケートサービス『仮想民声』の紹介と、その際のCognito + Googleログイン連携の実装ポイントやハマった点を解説します。
開発には主にこちらのQiita記事を参考にしました。
補足:Amplify Gen2 は情報が少なく、フロントエンドの Amplify JavaScript Library も v5 → v6 に変わったことで使い方が大きく変化しています。実装時に苦戦した部分をまとめました。
この記事の対象者
- 1万人規模アンケートサービス『仮想民声』を利用したい人
- Amplify Gen2 で開発を行っている、もしくは行いたい人
仮想民声の紹介
なぜ仮想民声を作ったのか
アンケートは、1万人規模の回答を集めることや、対象者条件を満たす人を集めることが大変で、実施に多くのコストと労力がかかります。
そこで、AI によって大規模アンケートの回答を自動生成することで、この課題を解決できると考え、本サービスを開発しました。
仮想民声とは
「仮想民声」は、1万人分のアンケート回答を仮想的に生成して統計を出せるサービスです。
使い方
1. ログイン
まずログイン画面が表示され、メールアドレス または Googleアカウント でログインできます。
2. アンケート内容の作成
「作成画面」で 次へ を押すと、ターゲットの基本情報(年齢、性別、国etc)を柔軟に指定できます。
質問形式は以下の6種類から選択可能です。
- 単一選択
- 複数選択
- 自由記述
- 均等目盛
- 日付
- 時刻
3. アンケートの生成
最後にアンケート内容を確認し、送信します。
4. 結果の確認
作成したアンケートは「アンケート一覧」に表示されます。
回答結果は 「回答結果」ボタン から見ることができ、1万人規模の集計結果と、うち20人の詳細な回答一覧の閲覧が可能です。
なお、アンケート結果の作成には 1~2分程度 かかり、その間は「作成中」と表示されます。失敗した場合は「作成失敗」と表示されます。
集計方法:
- 自由記述 → ワードクラウド & 出現頻度グラフで出力
- その他の回答形式 → クロス集計で出力
5. 追加質問
回答一覧の20人には、追加で質問をし、回答を掘り下げることができます。
サービスの特徴
- Googleアカウントでログイン可能
- 簡単に独自のアンケートを作成し、即座に統計を生成
- OpenAIを使って回答や集計を自動生成するため、世間の傾向を取り入れてくれる
- 回答者には追加質問も可能で、対話的にデータを掘り下げられる
📺 デモ動画
👉 実際のサービスはこちらから利用できます: 仮想民声から利用可能です。
実装
次に、実装について説明します。
動作環境・使用するツールや言語
- Amplify Gen2
- Node.js v20 (fnm use 20)
- React + Umi.js + Ant Design Pro
- AWS Cognito, DynamoDB, Lambda
- OpenAI API
アーキテクチャ
-
ログイン
Cognito + Google Federated Login -
アンケート生成
ユーザーがフロントエンドからアンケート「作成」ボタンを押すと DynamoDB にデータ保存 -
データ保存をトリガーに Lambda 実行
DynamoDB Streams 経由で Lambda 起動
Lambda 内で OpenAI API を呼び出し、仮想回答を生成 -
フロントエンドで結果を表示
React (Umi.js + Ant Design Pro) で UI を構築
DynamoDB から取得した回答データを一覧・チャートで表示 -
追加質問
集計結果ページの「質問する」ボタンから別 Lambda を呼び出し
回答データをもとに OpenAI API で追加質問応答を生成
Amplify Gen2 でハマった点と解決策
今回、Amplify Gen2 を使用してコードベースでサービスを実装し、デプロイまで行いました。主に以下の作業を行いました。
- Lambda 関数の生成・呼び出し
- DynamoDB テーブル名の取得
- Cognito と Google ログイン連携
- 認証済みユーザーによる DynamoDB / Lambda 呼び出し権限付与
- アンケート生成用プロンプトの作成
- デプロイ設定(amplify.yml)の作成
特に情報が少なく苦戦した部分は以下です。
- DynamoDB テーブル名の取得
- Cognito と Google 連携
- 認証済みユーザーによる DynamoDB / Lambda 呼び出し権限付与
- デプロイ設定(amplify.yml)
- アンケート生成用プロンプト作成
1. DynamoDB テーブル名の取得
DynamoDB のテーブル名は AWS コンソールから確認できますが、開発中は環境によってテーブル名が毎回変わることがあります。
そのため、コードから動的に取得する方法が便利です。
以下の例では、Amplify Gen2 で定義したバックエンドオブジェクトから DynamoDB テーブル名を取得しています。
const backend = defineBackend({
auth,
data,
myDynamoDBFunction,
myChatResponder,
});
// DynamoDB テーブル取得
const todoTable = backend.data.resources.tables['Survey'];
ポイント
backend.data.resources.tables には、デプロイ時に生成された全テーブル情報が格納されます。
これにより、ハードコーディングせずにテーブル名を参照でき、環境が変わってもコードを修正する必要がありません。
2.cognitoとのgoogle連携
Umi.js アプリで Google ログインを使う際の手順です。
概要
- AWS Cognito User Pool で Google アカウントログインを有効化
- Amplify UI React の Authenticator と useAuthenticator を使用
- useNavigate でログイン後にページ遷移
- ファイル構造の都合で Authenticator.Provider を使い、認証情報を下層コンポーネントに渡す
Cognito + Google 設定例
export const auth = defineAuth({
loginWith: {
email: true,
externalProviders: {
google: {
clientId: secret('GOOGLE_CLIENT_ID'),
clientSecret: secret('GOOGLE_CLIENT_SECRET'),
scopes: ['email'],
},
callbackUrls: ['http:xxxxx', ],
logoutUrls: ['http:xxxxx', ],
}
},
}
app.tsx での Provider 設定
import { Authenticator } from '@aws-amplify/ui-react';
Amplify.configure(outputs)
export const rootContainer = (container: React.ReactNode) => {
return <Authenticator.Provider>{container}</Authenticator.Provider>;
};
ログイン画面の実装例
import { useEffect } from 'react';
import { useNavigate } from '@umijs/max';
import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react';
function LoginContent() {
const navigate = useNavigate();
const { user, route } = useAuthenticator((context) => [context.user, context.route]);
useEffect(() => {
if (route === 'authenticated' && user) {
navigate('/survey/builder', { replace: true });
}
}, [route, user, navigate]);
if (route === 'authenticated') {
return null; // ログイン済みなので表示しない
}
return <div>ログインフォームを表示中...</div>;
}
export default function LoginPage() {
return (
<Authenticator socialProviders={['google']}>
<LoginContent />
</Authenticator>
);
}
3.認証済みユーザーによる DynamoDB / Lambda 呼び出し権限の付与
Amplify v6 では、User Pool 権限を用いてアクセス制御が可能です。
const schema = a.schema({
Survey: a
.model({
...省略...
}),
})
.authorization((allow) => [
allow.owner(), //ログイン済みユーザは自分自身のデータのみアクセス可
]),
})
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'userPool',
},
})
アクセス時には authMode を指定します。
const result = await client.models.Survey.create(formatted, {authMode: 'userPool' });
const client = generateClient<Schema>({ authMode: 'userPool' });
4.デプロイ設定 (amplify.yml)
Amplify でのデプロイには、プロジェクトに合わせた amplify.yml の作成が必要です。
以下は今回のサービス用に作成した例です。今回の環境では、環境変数で Node.js のバージョンを 20 (NODEJS_VERSION=20)に固定しています。
version: 1
backend:
phases:
build:
commands:
- npx ampx pipeline-deploy --branch YOUR_BRANCH --app-id YOUR_APP_ID
- npx ampx generate outputs --branch YOUR_BRANCH --app-id YOUR_APP_ID
frontend:
phases:
preBuild:
commands:
- rm -rf node_modules
- rm -rf package-lock.json
- rm -rf .umi
- npm install --legacy-peer-deps --include=optional
build:
commands:
- npm run build
artifacts:
baseDirectory: dist
files:
- '**/*'
cache:
paths:
- node_modules/**/*
5.アンケート結果生成用プロンプトの作成
プロンプトが1万2千文字以上になってしまったので、分けて送信しデータを生成してもらうことで、精度を向上させることができました。
まとめ
今回は、1万人規模アンケートサービス 「仮想民声」 の開発を通して、Amplify Gen2 を活用しました。
ただし課題もありました。
- Amplify Gen2 に関する情報が少ない
- Amplify JavaScript Library のバージョン更新で使い勝手が大きく変わっていた
- AI にアンケート集計を依頼すると、プロンプトが長くなりすぎる
- 集計結果のパーセンテージが100%にならなかったり、表がきりの良い数に丸められてしまう
今後開発を進める際は、公式ドキュメントを参照しつつ、ChatGPTや各種サイトを併用して進めるのが効果的だと感じました。
参考資料