0204 TIL

Bor·2022년 2월 4일
0

라우팅

이제는 눈감고도 해봅시다.

근데 진짜 눈을 감고 하면 아래와 같은 실수를 할 수도 있네요.

어색한 부분이 없으신가요? 따로 떼어놓고 보니. 잘 보인다. 회원가입으로 페이지 이동이 되지 않아서. 또 다시 라우트 페이지를 하나씩 주석처리 해보면서 찾았는데. 한참을 찾다 보니 path가 컴포넌트에 있더군요.

이제는 익숙해진

<BrowserRouter>
 <Switch>
   <Route exact path="/">

브라우저 라우터 - 스위치- 라우터s

눈 감은 김에 하나 더 해봅니다.

이렇게 유저에게서 form을 통해서 정보를 받아봅시다.

const Login = () => {
 const [email, setEmail] = useState("");
 const [password, setPassword] = useState("");

 const handleSubmit = (e) => {
   e.preventDefault();
   console.log(email, password);
 };

 return (
   <form className={styles["login-form"]} onSubmit={handleSubmit}>
     <h2>로그인</h2>
     <label>
       <p>이메일:</p>
       <input
         type="email"
         required
         onChange={(e) => setEmail(e.target.value)}
         value={email}
       />
     </label>
     <label>
       <span>비밀번호</span>
       <input type="password" onChange={(e) => setPassword(e.target.value)} />
     </label>

     <button className="btn"> 로그인 </button>
   </form>
 );

챙겨야할 부분은 value를 통해서 two-way communication이 가능하도록 하는 것과 e.preventDefault를 통해서 새로고침을 막아주는 것. 같은 방식으로 회원가입도 폼을 만들었습니다. 그렇지만 언제까지 console로 확인만 할 수 없으니 firebase로 ㄱㄱ


어쓰! 구현하기

예전에 스터디에서 어쓰어쓰,, 해서 잘 몰랐던...! 인증을 구현해봅시다.

인증 vs 인가
인증은 신원을 확인하는 과정정지! 라이트 꺼! 시동 꺼! 우리는 보통 어떤 인증요소를 증거로 제시하여 자신을 인증한다. 출입증 패스라던가. 민증이라던가. 인증과 달리 인가는 어떤 리소스에 접근할 수 있는지 또는 어떤 동작을 수행할 수 있는지를 검증하는 것으로 권한을 얻는 일. 그래서 인증은 인가로 이어질 수 있지만 인가는 인증으로 바로 이어지지 는 않는다.

코딩하기 전부터 궁금했었는데 오늘 배워볼 수 있었다. 물론 파이어베이스를 통해서!

const firebaseConfig = {
*******************
}

콜솔로 프로젝트르 만들고 요런 파일을 어서 찾아와서 config.js로 만들고 사용할 기능들을 빨리 import하고 실행시켜주었습니다.

import firebase from "firebase/app";
import "firebase/firestore";
import "firebase/auth";

...

firebase.initilizeApp(firebaseConfig); //시작하겠습니다~
const projectFirestore = firebase.firestore(); // 파이어 스토어를 사용하겠다
const projectAuth = firebase.auth(); // 인증은 auth로

export { projectFirestore, projectAuth }; // 위 둘을 어디서든 사용할 수 있도록 빼내겠다.

그렇다면 이제 제대로 회원가입을 마무리해봅시다. 이를 처리할 hook을 만들어서 재사용성을 높여봅시다.

import { useState } from "react";
import { projectAuth } from "../firebase/config";

export const useSignup = () => {
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

우선은 파이어베이스에서 projectAuth를 받아오고 에러와 진행중을 띄우기 위해서 useState를 통해서 error와 isPending을 만들어주었습니다.

const signup = async (email, password, username) => {
    setError(null);
    setIsPending(true); // 로딩 시작

    try {
      // 유저 로그인
      const res = await projectAuth.createUserWithEmailAndPassword(
        email,
        password
        // 인증을 이메일과 패스워드로 하겠다.
      );  
      console.log(res);
      console.log(res.user);
      if (!res) {
        throw new Error("유효한 이메일 혹은 비밀번호가 아닙니다");
      }
      //   성공시
      // 유저의 프로필을 업데이트하며 네임 추가하기
      await res.user.updateProfile({ displayName: username });

      setIsPending(false);
      setError(null);
    } catch (err) {
      console.log(err.message);
      setError(err.message);
      setIsPending(false); //어쨋든 로딩은 끝
    }
  };
   return { error, isPending, signup };
};

나를 키운 것은 팔 할이 에러다.

그런데 위와 같이 에러가 난다. 얼른 구글링해보자. 우선 찾아보면 예상치 못한 공백으로 인해서 .trim()을 붙여줘야 한다고 나온다. 그렇지만 그런 쉬운 것으로 해결되지 않았다. 그럴 때는 코드를 다시 보자. 다시 보니

요렇게 비동기도 받아주는 부분과 또한 sign-up으로 넘겨주는 곳의 순서가 달랐다. 그래서 이후에 객체 형식으로 넘겨줄 수 있도록 {}을 넣어주니 해결이 되었다.

그래서 이후에 테스트를 해보니 {}를 빼면, 죠렇게 순서가 맞지 않으면 에러가 났다.

이후에 firebase에 들어가면 이렇게 우리가 여러 번 삽질을 한 흔적이 남아있다.


로그인 & 로그아웃 구현하기

useContext를 활용해서 전역으로 로그인과 로그아웃을 관리하고 만들어보자!

import { createContext, useReducer } from "react";

export const AuthContext = createContext();

이렇게 useReducer를 불러오고 AuthContext 라는 컨텍스트를 만들어준다.

export const authReducer = (state, action) => {
  switch (action.type) {
    case "LOGIN":
      return { ...state, user: action.payload };
    case "LOGOUT":
      return { ...state, user: null };
    default:
      return state;
  }
};

다음 부분에서는 state와 action을 이용해서 LOGIN과 LOGOUT 기능을 만들어준다. return 해주는 값은 현재 상태를 스프레드 형식으로 우선 받아주고 뒤에 payload를 통해서 유저의 상태를 글로벌하게 관리한다.

export const AuthContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(authReducer, {
    user: null,
  });

  //   dispatch({type : 'LOG_IN'})

  return (
    <AuthContext.Provider value={{ ...state, dispatch }}>
      {children}
      {/* 무엇이든 이후에 감쌀 모든 것을 의미 */}
    </AuthContext.Provider>
  );
};

위에서 children은 이후에 감쌀 모든 것을 의미하며 이 앱에서는 App을 감쌌다.

#useAuth 

import { AuthContext } from "../context/AuthContext";
import { useContext } from "react";

export const useAuthContext = () => {
  const context = useContext(AuthContext);

  if (!context) {
    throw Error("useContext가 AuthProvider 내부에 있어야 합니다!");
  }

  return context;
};

그래서 위와 같은 useAuth는 지금은 App을 감쌌기 때문에 에러가 일어날 일이 없지만! 그래도 앞으로 항상 이렇게 App을 감싸는 컨텍스트만 만들 것이 아니기에 만들어주었다.

마지막으로 로그아웃 부분이다. 필요한 컴포넌트 어디서나 로그아웃 버튼을 눌렀을 때만 실행될 수 있도록 hook을 통해서 재사용성을 높였다.

#useLogout
const useLogout = () => {
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);
  const { dispatch } = useAuthContext(); //dispatch the logout 기능

  //   특정 컴포넌트에서 로그아웃을 눌렀을 경우에만 로그아웃을 실행할 수 있도록
  const logout = async () => {
    setError(null);
    setIsPending(true);

    // 로그아웃
    try {
      await projectAuth.signOut();
      //   파이어 베이스의 signOut을 이용한다.
      //   projectAuth.signOut(); 가 진행된 이후에 
      이후의 것들이 실행될 수 있도록 await를 사용

      // dispatch 로그아웃
      dispatch({ type: "LOGOUT" });
      
      // 로그아웃은 별도로 유저에게 무언가 조치를 더 할 것이 
      없으므로 payload를 전달하지 않아도 된다
      
      setIsPending(false);
      setError(null);
    } catch (err) {
      console.log(err.message);
      setError(err.message);
      setIsPending(false);
    }
  };

  return { error, isPending, useLogout };
};

우선은 이제 익숙하게 error와 isPending을 선언해주고. dispatch하기 위해서 디스트럭처링을 통해서 구현해준다.

const { dispatch } = useAuthContext(); //dispatch the logout 기능
const logout = async () => {
    setError(null);
    setIsPending(true);

을 통해서 에러 스위치 내리고 isPending 스위치를 올린다

try {
      await projectAuth.signOut();
        // dispatch 로그아웃
      dispatch({ type: "LOGOUT" });
      setIsPending(false);
      setError(null);
    } catch (err) {
      console.log(err.message);
      setError(err.message);
      setIsPending(false);
    }

로그아웃은 어떻게 할까? 생각했는데 파이어 베이스의 signOut을 이용한다. 이와 더불어, projectAuth.signOut(); 가 진행된 이후에 이후의 것들이 실행될 수 있도록 await를 사용한다. 로그아웃은 별도로 유저에게 무언가 조치를 더 할 것이 없으므로 payload를 전달하지 않아도 된다. 각 과정이 끝난 뒤에는 우선 isPending 스위치를 내리고 Error를 null 혹은 출력해준다.

0개의 댓글