前回は チケット販売システムを作る①を使って、正確なカウントダウン機能を実装しました。
今回は、実際のチケット販売サイトである「インターパーク」の販売フローを簡単に整理し、Botチェックの部分を簡易的に再現してみました。

インターパークのチケット販売は、販売開始時間になると「販売開始」ボタンが有効になり、クリックすると待機ページ(仮想待合室)へ移動します。順番が来ると座席選択へ進むことができ、その前にBotチェックが表示されます。認証が完了すると、座席や日付を選択し、決済まで進むことで予約が確定します。
本来のBotチェックでは、文字が画像として表示され、コピー&ペーストができない仕組みになっています。しかし今回は、あくまで「タイミング練習」が目的のため、構造理解を優先し、シンプルな形で実装しています。

今回は簡易的なBot対策として、
- ランダムな英大文字6文字を生成
- ユーザー入力と一致するか判定
- 一致すれば次の処理へ進む
という流れを実装しました。
Array.from()の使い方Math.random()の使い方- ReactでのState管理
- 簡単なバリデーション処理の流れ
CaptchaGateコンポーネント
CaptchaGateコンポーネントを生成し、Botチェックのために必要な機能をまとめました。
- A〜Z の文字列を用意
-
Array.from()で長さ6の配列を作る -
Math.random()でランダムなインデックスを取得 - 6文字を結合して文字列にする
- state に保存
- ユーザー入力と比較
- 正しければ
onSuccess()実行
コード説明
generateCaptcha()関数を生成し、ランダム英字の生成ロジックを実装します。
generateCaptcha()の全体コード
const generateCaptcha = (): string => {
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return Array.from({ length: 6 }, () =>
characters.charAt(
Math.floor(Math.random() * characters.length)
)
).join("");
};
characters
A〜Zまでの文字を入れる変数を宣言
コードを見る
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Array.from()
- 長さ6の配列を作る
- 第二引数に関数を書くと、各要素を生成できる
- 最後に
.join("")で文字列化
コードを見る
Array.from({ length: 6 }, () =>
characters.charAt(
Math.floor(Math.random() * characters.length)
)
).join("");Math.random()
- 0以上1未満の小数を返す
- そのままだと小数なので
Math.floor()で整数化
コードを見る
//0 〜 25 の整数が作られます。
Math.floor(Math.random() * characters.length)State変数宣言
それぞれを管理するState変数を宣言します。
- code → ランダムに生成された正解コード
- input → ユーザ入力
- error → エラー状態の判定
コードを見る
const [code, setCode] = useState(() => generateCaptcha());
const [input, setInput] = useState("");
const [error, setError] = useState(false);入力欄のポイント
小文字で入力しても大文字で変換してくれます。
コードを見る
onChange={(e) => {
setInput(e.target.value.toUpperCase())
}}handleSubmit():判定処理
入力した文字とランダム文字が一致するかを確認します。
一致しない場合は、もう一度Botチェックを行います。

コードを見る
useEffect(()=>{
inputRef.current?.focus();
},[])
//判定処理
function handleSubmit(){
if(input == code){
console.log("OK");
onSuccess();
}else{
console.log("X");
setError(true);
setCode(generateCaptcha());
setInput("");
}
}全体コード
全体コードはこちらになります。
コードを見る
"use client"
import { useEffect, useRef, useState } from "react"
type Props = {
onSuccess:()=>void;
}
export default function CaptchaGate({ onSuccess }: Props){
const generateCaptcha = (): string => {
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return Array.from({ length: 6 }, () =>
characters.charAt(Math.floor(Math.random() * characters.length))
).join("");
};
// bot captcha code
const [code, setCode] = useState(() => generateCaptcha());
// user input
const [input, setInput] = useState("");
// validation error
const [error, setError] = useState(false);
// current input ref
const inputRef = useRef<HTMLInputElement>(null);
useEffect(()=>{
inputRef.current?.focus();
},[])
function handleSubmit(){
if(input == code){
console.log("OK");
onSuccess();
}else{
console.log("X");
setError(true);
setCode(generateCaptcha());
setInput("");
}
}
return(
<div className="flex flex-col items-center justify-center text-center ">
<div>
{code}
</div>
<div>
<input
ref={inputRef}
value={input}
maxLength={6}
onChange={(e)=>{ setInput(e.target.value.toUpperCase())}}
onKeyDown={(e)=> e.key === "Enter" && handleSubmit()}
className="border-1 block outline-none text-center"
>
</input>
<button
type="button"
onClick={()=>handleSubmit()}
className="mt-4 pr-2"
>
Submit
</button>
</div>
</div>
)
}Array.from()の使い方
Array.from() は配列のようなオブジェクトや反復可能な値から、新しい配列を作成するメソッドです。
今までは for文 で繰り返し処理を書いていましたが、Array.from() を使うことで、配列の生成と要素の作成を1行で記述できます。
String→配列
Array.from("おはよう")
//["お","は","よ","う"]set→配列
const set = new Set([1, 2, 3]);
Array.from(set)
// [1, 2, 3]map→配列
const map = new Map([
["a", 1],
["b", 2],
]);
Array.from(map)
// [["a", 1], ["b", 2]]
Array.from(map.values());
// ['1', '2'];
Array.from(map.keys());
// ['a', 'b'];長さを指定して配列を作る(関数+インデックス)
length を指定することで、繰り返し回数を決められます。
Array.from({ length: 5 }, (_, i) => i);
// [0, 1, 2, 3, 4]二次関数→配列
Array.from({ length: 5 }, (_, x) => x * x);
// [0, 1, 4, 9, 16]
console.log(Array.from([1, 2, 3], x => x * x));
// [1, 4, 9]なぜ Array.from() が便利?
以前までよく使ったコードとArray .from()を使ったコードを比較しました。
for文
const result = [];
for (let x = 0; x < 5; x++) {
result.push(x * x);
}Array.from()
Array.from({ length: 5 }, (_, x) => x * x);終わりに
今日もこちらの記事を読んでいただき、ありがとうございます!次回はいよいよ座席をGridレイアウトで実装し、実際に選択できる仕組みまで紹介します。
※ 本記事のBotチェックは学習目的の簡易実装です。本番環境ではreCAPTCHAなどの専門的な認証サービスを利用する必要があります。

