Next.js에서 Firebase Authentication(with Context API) 사용

개시하다


자신이 할 서비스는 로그인 기능이 필요합니다.
Firebase Authentication 인증 로그인 기능을 사용하려고 합니다.
Next.js에서Firebase Authentication을 사용할 때Context API를 사용하면 처음 봤을 때 귀찮아서 모아요.

컨디션

  • Next.js v10.1.3
  • Chakra UI
  • TypeScript v4.2.3
  • Firebase v9.1.0
  • 상태 관리에는 Context API 및 Hooks 사용

    프로젝트 작성


    Firebase를 먼저 설정하고 싶으므로 Type Script·Chakra UI의 공식 샘플을 사용합니다.
    $ npx create-next-app firebase-sample --example with-chakra-ui-typescript
    

    Firebase 구성


    Firebase에서 새 항목 만들기


    다음은 콘솔에서 새 프로젝트를 만듭니다.
    https://firebase.google.com/ プロジェクト全般 -> 全般에서 API 키 등의 정보를 확인하고 .env.local에 환경 변수를 설정합니다.

    Firebase 설치


    $ yarn add firebase
    

    Firebase 초기화


    /src/framework/firebase/firebase.ts
    import { initializeApp } from "firebase/app";
    
    const config = {
      apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
      authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
      projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
      storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
      messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSEGING_SENDER_ID,
      appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
      measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
    };
    
    const firebaseApp = initializeApp(config);
    export default firebaseApp;
    
    게다가 NEXT_PUBLIC면 APIKey는 고객측에서 볼 수 있지만 문제가 없을 것 같습니다.
    하지만 안전 규칙으로 잘 대응할 필요가 있다.
    https://qiita.com/mtskhs/items/f2abba5f284dbb446e0f

    사용자 정보 추상화


    UI 계층에 Firebase 정보가 노출되지 않도록 사용자 정보를 일시적으로 추상화합니다.
    /src/framework/common/User.ts
    export type User = {
      displayName: string | null | undefined;
      email: string | null | undefined;
    };
    
    
    이렇게 하면 인증 키보드를 변경할 때 UI 레이어를 변경할 필요가 없으므로 편리합니다.

    로그인 등 기능 설치


    로그인, 로그아웃 등 필요한 기능을 실현하다.
    /src/framework/firebase/auth.ts
    import firebaseApp from "./firebase";
    import {
      getAuth,
      onAuthStateChanged as onFirebaseAuthStateChanged,
      signInWithRedirect,
      GoogleAuthProvider,
      signOut,
    } from "firebase/auth";
    import { User } from "framework/common";
    
    const provider = new GoogleAuthProvider();
    
    export function login(): void {
      const auth = getAuth(firebaseApp);
      signInWithRedirect(auth, provider);
    }
    
    export function logout(): Promise<void> {
      return new Promise((resolve, reject) => {
        const auth = getAuth(firebaseApp);
        signOut(auth)
          .then(() => resolve())
          .catch((error) => reject(error));
      });
    }
    
    export const onAuthStateChanged = (callback: (user: User | null) => void) => {
      const auth = getAuth(firebaseApp);
    
      onFirebaseAuthStateChanged(auth, (user) => {
        const userInfo: User | null = user
          ? {
              displayName: user?.displayName,
              email: user?.email,
            }
          : null;
        callback(userInfo);
      });
    };
    
    

    Context API를 사용하여 AuthProvider 만들기


    Context API 정보


    언어 환경은 데이터 공유를 위해 설계된 것이다. 예를 들어 현재 인증된 사용자 테마, 우선 언어 등이다. 어떤 React 구성 요소의 트리는'전역'으로 여겨질 수 있다.
    https://ja.reactjs.org/docs/context.html
    사용 방법은 대체로 다음과 같다.
  • React.createContext(defaultValue)에 Context
  • 작성
  • <Context.Provider value={data}></Context.Provider> 값을 설정합니다.
  • 사용 측면에서 useContext(1で作ったContext)2에 설정된 데이터를 획득
  • AuthContext 및 Provider 작성


    /src/framework/context/AuthContext.tsx
    import { createContext, useEffect, useState, useContext } from "react";
    import { User } from "framework";
    import { onAuthStateChanged } from "@auth";
    
    type AuthContextProps = {
      currentUser: User | null | undefined;
    };
    
    const AuthContext = createContext<AuthContextProps>({ currentUser: undefined });
    
    export const AuthProvider: React.FC = ({ children }) => {
      const [currentUser, setCurrentUser] = useState<User | null | undefined>(
        undefined
      );
    
      useEffect(() => {
        onAuthStateChanged((user) => {
          // ログイン状態が変化すると呼ばれる
          setCurrentUser(user);
        });
      }, []);
      return (
        <AuthContext.Provider value={{ currentUser: currentUser }}>
          {children}
        </AuthContext.Provider>
      );
    };
    
    export const useAuthContext = () => useContext(AuthContext);
    
    
    /src/pages/_app.tsx
    import { ChakraProvider } from '@chakra-ui/react'
    
    import theme from '../theme'
    import { AppProps } from 'next/app'
    import { AuthProvider } from 'framework';
    
    function MyApp({ Component, pageProps }: AppProps) {
      return (
    +   <AuthProvider>
    +      <ChakraProvider resetCSS theme={theme}>
            <Component {...pageProps} />
          </ChakraProvider>
    +   </AuthProvider>
      )
    }
    
    export default MyApp
    
    

    시험해 보다


    로그인/로그오프 버튼 만들기
    /src/components/Button.tsx
    import { Button } from "@chakra-ui/react";
    import { login, logout } from "@auth";
    
    type BaseButtonProps = {
      text: string;
      onclick(): void;
    };
    const BaseButton: React.FC<BaseButtonProps> = (props) => (
      <Button
        backgroundColor="#48BB78"
        color="#fff"
        width="100px"
        height={{ base: "35px", sm: "35px", md: "35px", lg: "35px" }}
        marginRight={2}
        onClick={props.onclick}
      >
        {props.text}
      </Button>
    );
    
    const LoginButton = () => <BaseButton onclick={() => login()} text="LOGIN" />;
    
    const LogoutButton = () => (
      <BaseButton onclick={() => logout()} text="LOGOUT" />
    );
    
    export { BaseButton, LoginButton, LogoutButton };
    
    
    /src/pages/_app.tsx
    import { Center } from "@chakra-ui/react";
    import { Hero } from "../components/Hero";
    import { Container } from "../components/Container";
    import { Main } from "../components/Main";
    import { DarkModeSwitch } from "../components/DarkModeSwitch";
    import { LoginButton, LogoutButton } from "../components/Button";
    import { useAuthContext } from "framework";
    
    const Index = () => {
      const { currentUser } = useAuthContext();
    
      return (
        <Container height="100vh">
          <Hero />
          <Main>
            <Center>{currentUser ? <LogoutButton /> : <LoginButton />}</Center>
          </Main>
          <DarkModeSwitch />
        </Container>
      );
    };
    
    export default Index;
    
    
    로그인하지 않은 경우 LOGIN 버튼이 표시됩니다.

    로그인할 때 LOGOUT 버튼이 표시됩니다.

    총결산


    Context API는 번거롭지만 대체로 이렇게 사용됩니다.
  • React.createContext(defaultValue)에 Context
  • 작성
  • <Context.Provider value={data}></Context.Provider> 값을 설정합니다.
  • 사용 측면에서 useContext(1で作ったContext)2에 설정된 데이터 가져오기
    Context에 설정된 데이터는 Global에서 사용할 수 있습니다.
  • 이번에 사용한 소스 코드는 아래 창고에push가 있습니다.
    https://github.com/wattanx/nextjs-firebase-sample

    2021/09/26 보충


    Firebasev9이 출시되었기 때문에 v9의 소스 코드를 수정했습니다.
    https://firebase.google.com/docs/web/modular-upgrade

    좋은 웹페이지 즐겨찾기