ChatGPTを使用したアプリ開発記【ログイン機能の実装】

## (1)Firebaseプロジェクトを作る
1.[Firebase Console](https://console.firebase.google.com/) でプロジェクト作成
2.「Authentication」→「ログイン方法」→以下を有効化：
- メール/パスワード
- Google

3.ReactアプリにFirebaseをインストール
![スクリーンショット 2025-05-02 7.18.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1058366/f6016317-b349-486f-a7df-9414028aaf41.png)

## (2) Firebase設定ファイル作成（firebase.js）
```firebase.js
// Firebase アプリの初期化に必要な関数をインポートします。
// これにより、Firebase プロジェクトと React アプリが接続されます。
import { initializeApp } from "firebase/app";

// irebaseの認証機能に関する関数やクラスをインポートします。
// getAuth → Firebase認証システムを使用するための関数
// GoogleAuthProvider → Googleログインのためのプロバイダー（設定）
import { getAuth, GoogleAuthProvider } from 'firebase/auth';

// あなたのFirebaseプロジェクトと通信するための設定情報です。
// Firebaseのコンソールからコピーした構成情報がここに入ります。
const firebaseConfig = {
  apiKey: "",
  authDomain: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: "",
  appId: "",
  measurementId: ""
};

// Firebaseアプリを初期化します。
// このコードにより、上記の構成情報で Firebase を使う準備が整います。
const app = initializeApp(firebaseConfig);

// 認証システムのインスタンスを生成し、他ファイルでも使えるように「export（外部公開）」します。
// 他のファイル（たとえば Login.js）からこの auth を使ってログイン操作ができます。
export const auth = getAuth(app);

// Googleログインを行うための「プロバイダーオブジェクト」を生成し、他ファイルでも使えるように公開します。
// このオブジェクトを使って、Googleログインができるようになります。
export const googleProvider = new GoogleAuthProvider();
```


## (3)ログイン画面コンポーネントの作成
```login.js
// Reactの基本機能と、コンポーネント内で状態（state）を持つための useState フックを読み込んでいます。
import React, { useState } from 'react';

//  firebase.js でエクスポートした Firebase 認証インスタンスと Googleログイン用プロバイダーを読み込みます。
import { auth, googleProvider } from './firebase';

//  Firebase Authentication のログイン/登録機能に必要な関数を読み込んでいます。
import {
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
  signInWithPopup,
} from 'firebase/auth';

//  Login コンポーネントの定義です。
// 親コンポーネント（App.js）から onLogin 関数を受け取っています。ログイン成功後に実行します。
function Login({ onLogin }) {

// メールアドレスとパスワードの入力状態（state）を管理しています。
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const loginEmail = async () => {
    try {
      await signInWithEmailAndPassword(auth, email, password);
      onLogin();
    } catch (e) {
      alert(e.message);
    }
  };

  const signupEmail = async () => {
    try {
      await createUserWithEmailAndPassword(auth, email, password);
      onLogin();
    } catch (e) {
      alert(e.message);
    }
  };

  const loginGoogle = async () => {
    try {
      await signInWithPopup(auth, googleProvider);
      onLogin();
    } catch (e) {
      alert(e.message);
    }
  };

  return (
    <div>
      <h2>ログイン/登録</h2>
      <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="メール" />
      <p><input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="パスワード" /></p>
      <div>
        <button onClick={loginEmail}>ログイン</button> &nbsp;&nbsp;
        <button onClick={signupEmail}>登録</button> &nbsp;&nbsp;
        <button onClick={loginGoogle}>Googleでログイン</button>
      </div>
    </div>
  );
}

export default Login;
```
## (4)Appコンポーネントに認証統合
```App.js
// Reactの状態管理（useState）と副作用処理（useEffect）を使う準備です。
// useEffectは、関数コンポーネントで副作用（DOM操作、API通信など、UI以外で処理を行うこと）を管理するためのフックです。
import React, { useState , useEffect} from 'react';

// Firebase の認証状態監視とログアウト処理をする関数を読み込みます。
import { auth } from './firebase';

// firebase.js から Firebase 認証インスタンスを読み込みます。
import { onAuthStateChanged, signOut } from 'firebase/auth';

// 自作の Login.js コンポーネントを読み込みます（ログイン画面）。
import Login from './login';


function App() {

//  現在ログインしているユーザーを保持するための state です。未ログイン時は null。
  const [user, setUser] = useState(null);
  
  const [text, setText] = useState('');
  const [posts, setPosts] = useState([]);

// Firebase のログイン状態を常に監視します。
  useEffect(() => {

// user がログインしていれば setUser(user) で状態更新
  const unsub = onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
    return () => unsub();
  }, []);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (text.trim() === '') return;

    const now = new Date();
    const formattedTime = now.toLocaleString('ja-JP', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
    });


    const newPost = {
      text: text,
      time: formattedTime,
      likes: 0,
      user: user?.email,
    };

    setPosts([newPost, ...posts])
    setText('');
  };

  const handleLike = (index) => {
    const updated = [...posts];
    updated[index].likes += 1;
    setPosts(updated);
  }

  const handleDelete = (index) => {
    setPosts(posts.filter((_, i) => i !== index));
  };

// ユーザーが未ログインなら、ログイン画面を表示します。
// ログイン成功時に setUser を実行することで、投稿画面に遷移します。
  if (!user) {
    return <Login onLogin={() => { }} />;
  }

  return (
    <div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
      <h1>SNS</h1>
      <p>ようこそ、{user.email} さん <button onClick={() => signOut(auth)}>ログアウト</button></p>

      <form onSubmit={handleSubmit}>
        <textarea
          value={text}
          onChange={(e) => setText(e.target.value)}
          placeholder="今なにしてる?"
          rows="4"
          style={{ display: 'block', width: '100%', marginBottom: '1rem' }}
        />
        <button type="submit">投稿する</button>
      </form>

      <div style={{ marginTop: '2rem' }}>
        <h2>投稿一覧</h2>
        {posts.map((post, index) => (
          <div key={index} style={{ padding: '1rem', borderBottom: '1px solid #ccc' }}>
            <div style={{ fontSize: '0.8rem', color: '#666' }}>{post.time} - {post.user}</div>
            <div>{post.text}</div>
            <button onClick={() => handleLike(index)}>❤️ いいね ({post.likes})</button>
            <button onClick={() => handleDelete(index)} style={{ marginLeft: '1rem', color: 'red' }}>🗑️ 削除</button>
          </div>
        ))}
      </div>
    </div >
  );
}

export default App;

```
## (5)操作画面
![画面収録-2025-05-02-8.27.41.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1058366/f20dc95e-4de9-4800-b79a-6dc8e7b0648c.gif)