サインインページの作成
定数の定義
src/frontend/src/constants/messages.ts
...(略)...
/**
* エラーメッセージの定数
*/
export const ERROR_MESSAGES = {
...(略)...
// 認証関連
EMAIL_AND_PASSWORD_REQUIRED: 'メールアドレスとパスワードを入力してください。',
SIGN_UP_FAILED: 'サインアップに失敗しました。時間をおいて再度お試しください。',
SIGN_IN_FAILED: 'サインインに失敗しました。メールアドレスとパスワードを確認してください。', // <- 追加
} as const;
サインインページ
src/frontend/src/app/signin/page.tsx
"use client";
import React, { useState } from "react";
import { ERROR_MESSAGES, STYLES } from "@/constants";
import { getFunctionName } from "@/utils";
import signIn from "@/api/auth/signIn";
const SignInPage = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSignIn = async (e: React.SyntheticEvent<HTMLFormElement>) => {
e.preventDefault();
setError(null);
if (!email || !password) {
setError(ERROR_MESSAGES.EMAIL_AND_PASSWORD_REQUIRED);
return;
}
setLoading(true);
try {
const { access, refresh } = await signIn(email, password);
if (typeof window !== "undefined") {
localStorage.setItem("access", access);
localStorage.setItem("refresh", refresh);
}
window.location.href = "/";
} catch (error: any) {
console.error(ERROR_MESSAGES.ERROR_IN_METHOD(getFunctionName()), error);
setError(ERROR_MESSAGES.SIGN_IN_FAILED);
} finally {
setLoading(false);
}
};
return (
<div className={`${STYLES.SIGN_PAGE}`}>
<div className={`${STYLES.SIGN_PAGE_CARD}`}>
<h1 className={`${STYLES.SIGN_PAGE_TITLE}`}>Sign In</h1>
<form onSubmit={handleSignIn} className={`${STYLES.SIGN_FORM}`}>
<div className={`${STYLES.SIGN_FORM_LABEL_INPUT}`}>
<label className={`${STYLES.SIGN_FORM_LABEL}`} htmlFor="email">
Email
</label>
<input
id="email"
type="email"
autoComplete="email"
placeholder="you@example.com"
className={`${STYLES.SIGN_FORM_INPUT}`}
value={email}
onChange={(e) => setEmail(e.target.value)}
disabled={loading}
required
/>
</div>
<div className={`${STYLES.SIGN_FORM_LABEL_INPUT}`}>
<label className={`${STYLES.SIGN_FORM_LABEL}`} htmlFor="password">
Password
</label>
<input
id="password"
type="password"
autoComplete="current-password"
placeholder="••••••••"
className={`${STYLES.SIGN_FORM_INPUT}`}
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={loading}
required
/>
</div>
{error && <p className={`${STYLES.SIGN_FORM_ERROR}`}>{error}</p>}
<button
type="submit"
className={`${STYLES.SIGN_FORM_BUTTON}`}
disabled={loading}
>
{loading ? "Signing in…" : "Sign In"}
</button>
</form>
<p className={`${STYLES.SIGN_FORM_HINT}`}>
アカウントをお持ちでない場合は{" "}
<a href="/signup" className={`${STYLES.LINK}`}>
Sign Up
</a>{" "}
してください。
</p>
</div>
</div>
);
};
export default SignInPage;
バックエンドサーバを起動した状態で、ブラウザで下記 URL にアクセスしてください。
サインアップで登録した、例えば下記のデータを入力し、Sign Inボタンをクリックしてください。
- Email:
bar@example.com - Password:
password123
自動的にトップページに移動すれば成功です。また、下記からアクセストークンとリフレッシュトークンがそれぞれ設定されていることを確認してください。
- Google Chrome のデベロッパーツールを開く。
- Applicationを開く。
- Storage > Local storage > http://localhost:3000 を開く。
- 下記の Key に値が設定されていることを確認する。
- access
- refresh
- Applicationを開く。
未登録のデータでサインインすると、「サインインに失敗しました。メールアドレスとパスワードを確認してください。」とエラーが表示されることも確認してください。