[#ContextApi, #useContext #Firebase-login] (React-Typescript) Firebase에서 로그인정보, contextApi를 통해 가져오는 중에 겪은 에러와 해결한소스

calm·2022년 2월 14일
0

타입스크립트,,,... 정말.. 한땀한땀 이란 표현처럼, 가다 서다 가...가니?.가자..가...다..서.다. 간다!ㅋㅋ 쉽지 않는 여정이네요


"firebase": "^9.6.6", 를 사용했습니다

파일의 구조는 다음과 같습니다.

아래 순서대로 파일이 사용됩니다.

Firebase ConFig

: 파이어베이스 콘솔 세팅은 인터넷 세팅 및 유튜브에서 자세히 설명하고 있습니다.
: 나의 파이어베이스 프로젝트의 config 파일을 만들어주세요

UseAuth.tsx

: context를 만드는 파일입니다
: 파이어베이스에서 받은 값을 자식?들에게 전달해주는 컨텍스트를 만드는 파일입니다
에러가 많았던 파일
context 타입체크를 신경써야하는 파일
value에는 usersetUser가 포함되어 있습니다.
user가 전달하고자 하는 핵심(파이어베이스 정보) 내용입니다.

export const UserContext = React.createContext<UserCxt | null>(null);

: 컨텍스트를 정의하는 부분입니다
: UserCxt는 인터페이스 입니다

interface UserCxt {
  user: User;
  setUser: (user: User) => void;
}

:user는 User 타입입니다. User는 firebase에서 import 한 것 입니다.

import { getAuth, onAuthStateChanged, User } from '@firebase/auth';

타입스크립트에서 해당 변수의 "정해진" 타입을 써야하고, 라이브러리가 사용하는 변수의 타입을 '추측'하는게 너무 어려웠습니다.

Layout.tsx

: 컨텍스트를 사용하기 위해 기초작업을 하는 파일입니다.
: useAuth가 감싸는 행위를 당하는? 파일입니다
: 감싼다는 의미는, useAuth가 value를 제공하는 Provider이기 때문에 감싼다는 표현을 사용했습니다

FunctionBtn.tsx

: 실제로 컨텍스트 value를 사용하는 파일입니다.
: UseAuth에서 전달한 값user을 실제적으로 사용하는 파일입니다.


1. Firebase ConFig 파일

Firebase config 파일입니다.

import { initializeApp } from 'firebase/app';

interface lFire {
  [key: string]: string | undefined;
}

const firebaseConfig: lFire = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID,
};

const app = initializeApp(firebaseConfig);

export { app };

2. conText를 만드는 파일

useAuth.tsx

: 사용자가 Firebase console에서 만든 프로젝트의 config값을 가져와서 이를 context로 만드는 파일

import React, { useEffect, useState } from 'react';
import { getAuth, onAuthStateChanged, User } from '@firebase/auth';
import { app } from './FirebaseConfig';
import { useNavigate } from 'react-router-dom';

interface UserCxt {
  user: User;
  setUser: (user: User) => void;
}

export const UserContext = React.createContext<UserCxt | null>(null);
export const defaultHeaders: any = {
  'Content-Type': 'application/json',
  Accept: 'application/json',
};

const hours = new Date().getHours();
const muninutes = new Date().getMinutes();

const UseAuth = ({ children }: { children: React.ReactNode }) => {
  const navigate = useNavigate();
  const auth = getAuth(app);
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => {
      console.log('firebaseUser', firebaseUser);
      if (firebaseUser) {
        try {
          const token = await firebaseUser.getIdToken();
          defaultHeaders.Authorization = `Bearer ${token}`;

          setUser(firebaseUser);
        } catch (error) {
          console.log('At UseAuth.tsx, Error is found', error);
        }
      } else {
        delete defaultHeaders.Authorizations;
        setUser(null);
      }
    });
    return () => {
      unsubscribe();
    };
  }, [auth, navigate]);

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

export default UseAuth;

3. conText를 사용하기 위해 설정하는 파일

Layout.tsx

=> UseAuth를 가지고 와서 Layout.tsx에 import 했습니다!

import { Outlet } from 'react-router';
import UseAuth from '../../Server/UseAuth';
import Logo from './Header/Logo/Logo';
import Navbar from './Header/Navbar/Navbar';
import FunctionsBtn from './Header/Utiles/FunctionBtn';

import {
  Content,
  Header,
  HeaderSection,
  HLeftSection,
  HRightSection,
} from './Layout.style';

function Layout() {
  return (
    <>
      <UseAuth>
        <HeaderSection>
          <Header>
            <Content>
              <HLeftSection>
                <Logo />
                <Navbar />
              </HLeftSection>
              <HRightSection>
                <FunctionsBtn />
              </HRightSection>
            </Content>
          </Header>
        </HeaderSection>
        <main>
          <Content>
            <Outlet />
          </Content>
        </main>
      </UseAuth>
    </>
  );
}

export default Layout;

4. conText를 사용하는 파일

FunctionBtn.tsx

=>useContext 훅을 통해서 가져온 값, user의 값을 사용하고 있습니다

const { user } = useContext(UserContext);
import { RNav, RnItem, RnLi, RnList } from './FunctionsBtn.style';
import { useContext } from 'react';
import { UserContext } from '../../../../Server/UseAuth';

function FunctionsBtn() {
  const { user } = useContext(UserContext);
  console.log('뾰로롱~', user);
  return (
    <>
      <RNav>
        <RnList>
          <RnLi>
            {!user ? (
              <RnItem to="/login" role="button">
                Login
              </RnItem>
            ) : (
              <RnItem to="/logout" role="button">
                Logout
              </RnItem>
            )}
          </RnLi>
        </RnList>
      </RNav>
    </>
  );
}

export default FunctionsBtn;

로그인(LOGIN) 전

Firebase에서 받아오는 값을 console로 확인하기 위해, 찍은 "뾰로롱~"과 "firebaseUser"가 null 입니다

스크린샷 우측 상단, 로그아웃 상태임으로 로그인 버튼이 활성화 됩니다.

로그인(LOGOUT) 후

"뾰로롱~"과 "firebaseUser"가 null에서 값이 생겼습니다.

스크린샷 우측 상단, 로그인 상태임으로 로그아웃 버튼이 활성화 됩니다.


에러

conText를 만드는 파일

useAuth.tsx

1.

does not exist on type 'jsx.intrinsicelements'

해결: 파일명의 첫글자(u)를 대문자로 변경함

useAuth.tsx(변경 전) ->  UseAuth.tsx (변경 후)

2.

type 'IntrinsicAttributes & { children: any...

2번의 에러는
Logout.tsx 구조처럼

 <UseAuth>
   ...중략...
 </UseAuth>

UseAuth 가 context 이기 때문에 사용하기 위해서는 필요한 곳을 감싸야 한다, 위와 같은 구조로 덮은 상태에서

const UseAuth = () => {

이렇게 하니 발생한 에러입니다.

검색 중, 여기를 보고 힌트를 얻었습니다.

그래서 다음처럼 변경했습니다.

관련 부분

const UseAuth = ({ children }: { children: React.ReactNode }) => {

children의 대한 타입정의를 내려야 한다는 것입니다.
useAuth를 적용한 곳에서는 children을 사용하는데, children에 대한 정의가 없었습니다.

3.

Cannot find namespace ...

당시, useAuth파일의 확장자를 .ts로 하고 있었다.
.tsx로 변경하니 해결됐다

관련 에러 해결은 여기에서 힌트를 얻어 해결했다.

4.

{ children }: { children: React.ReactNode }

typescript로 컨텍스트시, children에게 value를 전달하기 때문에, children의 대한 타입을 정의해줘야 한다.

위의 형식은 여기 강좌와 여기, 벨로퍼트 님 블로그에서 힌트를 얻어 해결했습니다.

5.

null 체크 해결,

user는 Firebase의 값이 들어?오는 부분이라 초기에는 값이 null이다. 기가 막히게? null 이라고 하는 에러?다

사실, 나는
tsconfig.json에

"strictNullChecks": true,

를 하고 있었다.

초기에 null 이 되는게 정상?이라 생각해서, null을 살려?둬야 하기 때문에 null 체크 부분을 true로 해결했다.

"strictNullChecks": false,
profile
공부한 내용을 기록합니다

0개의 댓글