본문 바로가기

리액트 - 클론코딩

트위터 클론코딩 - 로그인, 로그아웃하기

목표 - 회원가입 후 파이어베이스에 즉시 로그인한 다음 브라우저의 indexDB에 저장된 데이터를 Twitter 에 반영해보자

 

 

** 로그인 처리 후 currentUser가 null인 이유? **

 

비동기 처리 과정을 이해해야 함

 

 

1) 컴퓨터와 firebase 서버 사이의 거리는 엄청 멀다

2) 이 때문에 파이어베이스에서 로그인 처리를 마치고 트위터에서 그 신호를 받기까지는 시간 간격이 생김

3) 그 사이에 트위터가 currentUser 값을 확인하게 됨 

4) null

 

====>> 리액트의 생명주기 이용

 

 

 

액션1 - 딜레이 눈으로 확인해 보기

 

setInterval 함수를 사용, 2초마다 실행

회원가입 후 로그인 처리 완료까지 얼마나 시간이 걸리는지 살펴본다.

 

import { useState } from "react";
import AppRouter from "components/Router";
import { authService } from "fbase";

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(authService.currentUser);
  setInterval(() => console.log(authService.currentUser), 2000);
  return (
    <>
      <AppRouter isLoggedIn={isLoggedIn} />
      <footer>&copy; {new Date().getFullYear()} Twitter</footer>
    </>
  );
}

export default App;

 

 

 

null 에서 Im 으로 바뀌었다

 

 

currentUser가 변경되는 시점을 알기 위해 useEffect함수 사용

 

액션2 - useEffect 함수 사용하기

 

useEffect 함수는 특정한 시점에 실행되는 함수.

 

우리가 원하는 특정 시점 -> 파이어베이스 로그인 정보를 받게 되었을 때(파이어베이스가 초기화 되는 시점)

 

이 시점을 useEffect 함수로 잡아낸 다음 이때 로그인 완료 후 보여줄 화면을 렌더링

 

authService에 포함된 함수 중 onAuthStateChanged 는 인증 관련 상태가 바뀌는 것을 감지하는 함수

 

import { useEffect, useState } from "react";
import AppRouter from "components/Router";
import { authService } from "fbase";

function App() {
  const [init, setInit] = useState(false);
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  useEffect(() => {
    authService.onAuthStateChanged((user) => {
      if (user) {
        setIsLoggedIn(user);
      } else {
        setIsLoggedIn(false);
      }
      setInit(true);
    });
  }, []);
  return (
    <>
      {init ? <AppRouter isLoggedIn={isLoggedIn} /> : "initializing..."}
      <footer>&copy; {new Date().getFullYear()} Twitter</footer>
    </>
  );
}

export default App;

authService.currentUser 함수는 처음에 null을 반환 하므로 isLoggedIn 은 null로 시작할 가능성이 높음,

불확실성을 제거하기 위해 isLoggedIn을 user로 설정 -> 이렇게 해야 isLoggedIn를 AppRouter 컴포넌트 프롭스에 제대로 보낼 수 있음

로그인이 이루어지는 과정

 

액션3 - 로그아웃은 어떻게?

 

indexedDB 를 clear한다. (아직은 수동인 상태!!)

 

그전에 에러메세지 적용!!

 

 

액션4 - 에러와 에러 메시지를 파이어베이스로 처리하기

const Auth = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [newAccount, setNewAccount] = useState(true);
  const [error, setError] = useState("");

 

이제 에러가 발생할 지점에 setError를 사용하면 됨

error에 어떤 메세지를 담을지 정해야 함.

 

액션5 - 일부러 중복 회원가입 에러 발생시켜 보기

 

에러 메세지 발생

지금은 그냥 error를 출력하고 있어서 우리가 원하는 메시지가 x

 

 

 

액션6 - error.message 화면에 출력하기

 

 

에러가 발생하면 setError 함수에 error.message를 전달해서 error상태를 변화시키자!

그럼 에러가 발생할 때 화면에 error.message가 출력

import { authService } from "fbase";
import { useState } from "react";

const Auth = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [newAccount, setNewAccount] = useState(true);
  const [error, setError] = useState("");
  const onChange = (event) => {
    const {
      target: { name, value },
    } = event;
    if (name === "email") {
      setEmail(value);
    } else if (name === "password") {
      setPassword(value);
    }
  };
  const onSubmit = async (event) => {
    event.preventDefault();
    try {
      let data;
      if (newAccount) {
        data = await authService.createUserWithEmailAndPassword(
          email,
          password
        );
        //create newAccount
      } else {
        data = await authService.signInWithEmailAndPassword(email, password);
        //log in
      }
      console.log(data);
    } catch (error) {
      setError(error.message);
    }
  };
  return (
    <div>
      <form onSubmit={onSubmit}>
        <input
          name="email"
          type="email"
          placeholder="Email"
          required
          value={email}
          onChange={onChange}
        />
        <input
          name="password"
          type="password"
          placeholder="password"
          required
          value={password}
          onChange={onChange}
        />
        <input type="submit" value={newAccount ? "Create Account" : "Log In"} />
        {error}
      </form>
      <div>
        <button>Continue with Google</button>
        <button>Continue with Github</button>
      </div>
    </div>
  );
};

export default Auth;

 

catch(error)에 setError(error.message); 를 넣어주고

submit 아래 {error} 를 추가하였다.

 

 

 

중복 로그인 시 오류 메세지가 뜬다.

 

 

액션7 - 조금 더 섬세하게! 로그인, 회원가입 토글 버튼 적용

 

 

 

로그인, 회원가입 토글 버튼을 적용해보자.

로그인 여부에 따라 로그인, 회원가입이 전환되도록! -> 이렇게 하면 로그인, 회원가입에 맞춰 화면을 준비할 필요가 x

 

import { authService } from "fbase";
import { useState } from "react";

const Auth = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [newAccount, setNewAccount] = useState(true);
  const [error, setError] = useState("");
  const onChange = (event) => {
    const {
      target: { name, value },
    } = event;
    if (name === "email") {
      setEmail(value);
    } else if (name === "password") {
      setPassword(value);
    }
  };
  const onSubmit = async (event) => {
    event.preventDefault();
    try {
      let data;
      if (newAccount) {
        data = await authService.createUserWithEmailAndPassword(
          email,
          password
        );
        //create newAccount
      } else {
        data = await authService.signInWithEmailAndPassword(email, password);
        //log in
      }
      console.log(data);
    } catch (error) {
      setError(error.message);
    }
  };

  const toggleAccount = () => setNewAccount((prev) => !prev);
  return (
    <div>
      <form onSubmit={onSubmit}>
        <input
          name="email"
          type="email"
          placeholder="Email"
          required
          value={email}
          onChange={onChange}
        />
        <input
          name="password"
          type="password"
          placeholder="password"
          required
          value={password}
          onChange={onChange}
        />
        <input type="submit" value={newAccount ? "Create Account" : "Log In"} />
        {error}
      </form>
      <span onClick={toggleAccount}>
        {newAccount ? "Sign In" : "Create Account"}
      </span>
      <div>
        <button>Continue with Google</button>
        <button>Continue with Github</button>
      </div>
    </div>
  );
};

export default Auth;

모든 UseState 함수들은 바로 이전 상태를 참조 할 수 있음!!

ex))

setNewAccount((prev) => !prev) 와 같이 작성 -> setNewAccount 함수에 (prev) => !(prev) 와 같이 함수를 전달한것

 

이렇게 UseState 함수에 함수를 인자로 전달하면 인자로 전달한 첫번째 인자 이전의 상태가 넘어옴