10日で作るWebサイト(day2)

HTML/CSS

day2の課題は「ログイン機能を実装」です。
この辺の機能はfirebaseにどっぷり浸かる予定なので、指示通りにポチポチしていくことになります。

まずはfirebaseにprojectを作成し、アプリを追加します。

firebase上での操作はほぼ悩むことはなかったので省略。
途中でapi key等の情報が出てくるのでメモします。

nextjs

フロントサイドのコードを追加していきます。
まずは先ほどのKey等を.envファイルに保存します。

nextjsなのでNEXT_PUBLICの接頭辞を付与して変数を作成。フロントの呼び出し側のコードではビルド時にインライン展開されるようになります。

// .env.local

NEXT_PUBLIC_API_KEY=aaa
NEXT_PUBLIC_AUTH_DOMAIN=aaa.firebaseapp.com
NEXT_PUBLIC_PROJECT_ID=aaa
NEXT_PUBLIC_STORAGE_BUCKET=aaa3.appspot.com
NEXT_PUBLIC_MESSAGING_SENDER_ID=111
NEXT_PUBLIC_APP_ID=aaa
NEXT_PUBLIC_MEASUREMENT_ID=aaa

お次はfirebaseSDKの初期化です。
今回はサイト自体は静的サイトをデプロイして、動的な部分は全てCloud Functionsで行う予定なので、サーバサイドでの認証処理はありません。全部フロントでやります。
初期化を実行するメソッドをexportしておいて、nextjsの初期化時に呼び出すことにしました。

// firebase.ts

import { FirebaseApp, initializeApp } from 'firebase/app'

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_APP_ID,
  measurementId: process.env.NEXT_PUBLIC_MEASUREMENT_ID,
}
export const initializeFirebase = () => initializeApp(firebaseConfig)

フロントでログインユーザの情報を利用するためにAuthContextを定義します。
getAuth().currentUserをStateで管理して、Providerでどこからでも呼び出せるようにします。

import { getAuth, User } from 'firebase/auth'
import { ReactNode, createContext, useContext, useEffect, useState } from 'react'

export const AuthContext = createContext<{
  user?: User | null
}>({})

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [user, setUser] = useState(() => getAuth().currentUser)
  useEffect(() => {
    getAuth().onAuthStateChanged(setUser)
  }, [])

  return <AuthContext.Provider value={{ user }}>{children}</AuthContext.Provider>
}

export const useAuth = () => useContext(AuthContext)

ちょっと余計なコードが混ざっていますが、アプリのRootで呼び出す箇所でinitializeFirebaseを呼び出して初期化しつつ、AuthProviderでchildrenをラップします。今回はMUIを利用しているのでthemeProviderもセットです。

'use client'

import { CssBaseline, ThemeProvider, createTheme } from '@mui/material'
import { initializeFirebase } from '../modules/firebase'
import { AuthProvider } from '../hooks/useAuth'

initializeFirebase()

const theme = createTheme({
  palette: {
    primary: {
      main: '#fcba03',
    },
  },
})

export function MyApp({ children }: { children: React.ReactNode }) {
  return (
    <>
      <CssBaseline />
      <AuthProvider>
        <ThemeProvider theme={theme}>{children}</ThemeProvider>
      </AuthProvider>
    </>
  )
}

サインインとサインアウトのボタンは以下のようなコードを書きます。
今回はGoogleアカウントでのサインインのみ対応するため、GoogleAuthProviderを渡します。この辺は必要に応じて変えてください。サインアウトは特にProviderの指定は要りません。

<Button
  variant="outlined"
  onClick={() => signInWithRedirect(getAuth(), new GoogleAuthProvider())}
>
  Sign In
</Button>

<Button
  variant="outlined"
  onClick={async () => {
    await signOut(getAuth())
    location.reload()
  }}
>

ということで出来たサイトがこんな感じ。
MUIのAppBarにログアウトボタンがあるので、最終的にはそちらを利用する形に改修予定。今は機能だけ実装できれば細かいことは言いません。

コメント