Twitter 클론코딩 3.Firebase Authentication① 플랫폼 생성, 계정생성, 로그아웃

<angeLog/>·2024년 3월 27일

REACT

목록 보기
19/25
post-thumbnail

NOTICE

파이어베이스는 방대한 기능을 갖고 있기 때문에 기본적으로 모든 기능들이 비활성화 되어있다.
모든 기능들이 전부 활성화가 된다면 각 구성별로 기능과 설정이 모두 다르기 때문에 정신없을 것이기 때문이다. 따라서 필요한 기능을 하나씩 활성화 하는 방식으로 사용해야한다.

각 기능을 사용할 때 주의할 점은, 먼저 Firebase의 콘솔에서 기능을 활성화 한 다음에 VSCODE에서 initialize하는 것이다.

SETUP

프로젝트개요Project overview에서 Authentication을 선택.
Buil > Authentication로도 선택할 수 있다.

위에서 언급했듯이 파이어베이스의 기능을 사용하기 위해서는 먼저 Firebase의 콘솔에서 기능을 활성화야한다.

시작하기 버튼을 클릭하면 아래와 같은 화면이 나오는데, 여기에서 인증 방법을 선택하면 된다.

토글형식으로 사용여부를 선택할 수 있다.
이메일로 로그인 할 수 있도록 인증 방법을 메일로 선택했다.
익명이나 비밀번호가 없는 로그인을 선택하면 스팸이 터질 수 있으니 비추...

선택한 인증방법은 아래처럼 보여진다.
기존에 선택한 인증방법을 삭제하거나 새로 추가할 수 있다.


파이어베이스에서 활성화한 기능은 프로젝트에서도 활성화해야 한다.
firebase.ts에 Authentication을 사용하겠다는 코드를 작성하여 프로젝트에서 활성화 한다.
👉🏻파이어베이스 공식문서 : 커스텀인증 하기

import { getAuth } from "firebase/auth";
const auth = getAuth();

무엇에 대한 auth?

파이어베이스를 initialization할때 가져온 firebaseConfig에 대한 auth를 말한다.

export?

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);

변수나 함수, 클래스를 선언할 때 맨 앞에 export를 붙이면 선언함과 동시에 내보내기가 가능해진다. 즉 다른 파일에서 import가 가능하다.
const auth는 로그인여부를 확인하기 위해 사용된다.

방향확인

현재 만들고 있는 프로젝트는 Twitter이므로, 필요한 화면은 우선 3가지이다.

  1. 로그인페이지
  2. user페이지(user가 보는 dashboard화면)
  3. 로딩페이지

user가 누구인지 모른다면 로그인이 되어있지 않다는 뜻이므로 로그인페이지를 보여주고, 로그인이 되어있다면 user페이지를 보여준다.
데이터를 모두 불러오기 전까지 준비하는 loading페이지를 보여주어서 페이지가 road될 때 지루함을 좀 덜어낸다.

인증상태 확인(login 확인)

firebase.ts에서 내보낸 auth를 이용하면 다양한 인증함수들을 활용할 수 있다.
사진과 같이 auth.을 하면 사용할 수 있는 함수들이 리스트로 등장한다.

👉🏻auth.로 사용할 수 있는 Firebase interface

페이지가 render할때 인증상태를 바로 확인 할 수 있도록 useEffect를 사용하여 상태확인 함수를 실행했다.

  const init = async () => {
    await auth.authStateReady();
  };
  useEffect(() => {
    init();
  }, []);

authStateReady()
firebase에서 제공하는 인증상태가 준비되었는지 확인하는 함수이며 최초 인증상태가 완료될 때 실행되는 Promise를 return함. 즉, Firebase가 쿠키와 토큰을 읽고 백엔드와 소통해서 로그인여부를 확인하는 동안 기다리는 것을 의미.

Creat Account UI

로그인 할 수 있는 계정을 새로 추가하는 것 이므로 CreatAccount.tsx을 생성하고 정보를 입력할 Form을 만들었다.

styled-components를 이용해 인라인스타일로 CSS를 적용했다.

코드가 길어지기는 하지만 따로 css파일을 만들지 않고 작업하는게 재미있다.
하지만 역시 css는 따로 만드는게 제맛이지...👅

이름, 이메일, 패스워드를 필수값으로 지정하고, 각각의 값들이 세팅되도록 useState를 사용했다.
input의 값이 바뀔 때 각 요소들이 세팅되어야하므로 onChange함수를, 입력한 정보를 전송하기 위해 onSumit함수를 추가했다. 단조롭다는 생각이 들어서 추후에 이모지와 아이콘을 활용하여 예쁘게 바꿀 예정이다.

+추가
form에 입력한 값이 잘못된 값일 때 Error메세지를 출력하도록하고, useState로 상태값을 관리하도록 했다.

 const [error, setError] = useState('');

//form하단
{error !== '' ? <Error>{error}</Error> : null}

이제 필요한 단계는 아래와 같고, 이것을 try할 것이다.

  1. Form에 입력한 정보를 Firebase에 전송하여 Account 생성
  2. 로그인 하고 난 이후 user페이지(Dashboard)로 redirect
  3. Dashboard에서 유저가 누구인지 표기하기 위해 데이터 받아오기

try, catch, finally

👉🏻예외처리 Exception Handling
잘못된 코드를 작성했거나 사용자가 개발자가 원하지 않는 방향으로 프로그램을 사용했을 때 프로그램이 대처할 수 있도록 처리하는 것

try, catch, finally는 예외를 잡아낼 때 사용하는 문법이다.

try{
  //코드실행
} catch(e){
  //try에서 문제가 발생하면 실행
}finally {
  //try에서 발생한 문제를 catch에서 잘 해결했을 때 실행
}

🔥Create Account with Firebase

createUserWithEmailAndPassword는 Firebase에서 제공하는 미친 함수이다.
함수명 그대로 Email과 Password를 이용해 User를 생성하고 성공적으로 User를 생성하고나면 UserCredential(사용자 자격증명)을 받아오며, 성공적으로 사용자 자격증명을 받아오면 User는 애플리케이션에 즉시 로그인이 된다. 도라따...!😲

User를 생성하지 못하는 경우도 존재하는데, 이미 있는 계정이거나 유효하지 않은 Password일 때가 그 경우이다.

이 함수를 사용하기 위해서는 auth, email, password가 필요한데, auth는 Firebase.ts에 붙여놓은 firebaseConfig를 말한다. 이것을 auth라는 이름으로 export했었다.

//auth를 import하고
import { auth } from '../firebase';
try {
  // 미친함수의 전달인자로 auth와 함께 state로 만든 email & password를 넣어준다.
  const credential = await createUserWithEmailAndPassword(
    auth,
    email,
    password
  );
}

변수 credential에 담긴 createUserWithEmailAndPassword를 콘솔로 찍어보면 user정보도 얻을 수 있는 것을 볼 수 있다. Holy Moly😲😲😲
이것을 이용해서 사용자 정보를 update하고, 그에 맞는 Dashboard로 redirect한다.

Dashboard에서는 user 이름 및 프로필사진 등을 표기하므로 user가 누구인지 알려주는 updateProfile 함수를 사용한다.

user의 정보를 가져왔다 = login이 되었다는 뜻이므로, 로그인이 된 이후 user페이지(Dashboard)로 이동하기위해 react-router-dom의 Hook인 useNavigate를 사용했다.

import { useNavigate } from 'react-router-dom';

const navigate = useNavigate();

try {
  setLoading(true);
  // user생성 및 자격증명을 하여 로그인함.
  const credential =
        await createUserWithEmailAndPassword(
          auth,
          email,
          password
        );
  console.log(credential.user);
  // 2. 로그인 하고 난 이후 user페이지(Dashboard)로 redirect
  await updateProfile(credential.user, {
    displayName: name,
  });
  navigate('/');
  // 3. Dashboard에서 유저가 누구인지 표기하기 위해 데이터 받아오기
}

Sign out

CreateAccount이후 로그인까지 자동으로 실행이 되었는데, 그렇다면 로그아웃은 어떻게 할까?
이것도 정말 미쳐버렸다. 단 한 줄의 코드로 끝난다.

auth.signOut();

당연히 firebase의 auth는 import되어있어야하지만 간단한 함수 1줄로 로그아웃까지 구현이 된다.

결과

결과를 확인하기 위해서 몇 가지 작업을 추가했다.
App.tsx에 만든 router중에 로그인을 한 이후에 보여져야하는 화면이 있다.
지금은 아마 로그인을 하지않아도 경로를 입력하면 페이지가 이동될 것이다.
로그인을 한 이후에 보여져야하는 화면을 보호하기 위해서 ProtectRouter.tsx라는 컴포넌트를 만들고 auth.currentUser메소드를 사용했다.

ProtectRouter

로그인도 안돼있는데 바로 Dashboard로 이동할 수는 없다.
조건을 달고 조건에 부합할 때만 Dashboard로 이동하도록 해보자.

//ProtectRouter.tsx가 하는 일
import React from 'react';
import { auth } from '../firebase';
import { Navigate } from 'react-router-dom';
function ProtectRoute({
  children,
}: {
  children: React.ReactNode;
}) {
  const user = auth.currentUser;
  //user가 로그인한 상태가 아니라면
  if (user === null) {
    //login으로 페이지 redirect
    return <Navigate to={'/login'} />;
  }
  //user의 정보가 있으면 자식노드로 이동
  return children;
}
export default ProtectRoute;

현재 상태에서는 자식노드는 존재하지 않는다. 그렇다면 어떻게 사용해야 자식노드가 존재할까?

라우터의 element를 감싸주면 된다.
각각의 element를 감싸도 되지만 사진처럼 부모요소만 감싸주면 중복으로 사용하지 않고 사용할 수 있다.

Create Account

계정이 성공적으로 만들어지고 즉시 user페이지(Dashboard)로 이동했으며, 콘솔에는 의도한대로 user의 정보가 출력되고 있다.

파이어베이스에서도 계정이 성공적으로 추가된 것을 볼 수 있다.

Sign out

성공적으로 로그아웃이 되고, (아직은 수동으로 새로고침을 해야하지만) user의 정보가 없으므로 login 페이지로 redirect되고 있다.
주소창에서 path: '', path: 'profile'로 페이지이동을 시도하더라도 user의 정보가 없기때문에 login 페이지로 redirect된다.

아직은 정신없는데 정말 재미있다!


윈도우 닫았는데 왜 로그인 상태지?

우연히 발견한 문제였다.
윈도우 창을 닫고 다시 프로젝트를 열었는데 로그아웃되지 않고 계속 로그인 상태였다.
여기서 보안에 대한 걱정이 샘솟았다.

  • 만약, 다수 인원이 사용하는 공유 컴퓨터라면?
  • 창을 닫았음에도 로그아웃이 되지 않아서 타인이 내 정보를 보게 된다면?

그리고 곧 깨달았다. 그동안 내가 사용했던 많은 사이트들은 브라우저를 닫았을 때 자동으로 로그아웃을 실행시키는 함수를 적용했겠구나.
브라우저를 닫았을 때 로그아웃이 되는건 생물이 숨쉬는 것과 같이 당연한 일이라고 생각했는데 그게 아니라는 것을 이제야 알게 된 것이다.

개발이란 단순히 화면을 구현하는 것이 아니라 생각하지 못한 부분도 섬세하게 다뤄야 한다는 것을 알게된 계기였다.

어쨌든!

위 문제를 해결하기 위해 3가지 로직을 떠올렸다.

  1. 정보를 가져온 상태라면(isLoading=false).
  2. (isLoading=false)일 때, 창닫기 전에 재확인 confirm.
  3. 창을 닫게 되면 로그아웃 메소드 실행.

방법은 간단했다.

window.onbeforeunload = () => {
  if (!isLoading) {
    return '';
  }
};
window.onunload = () => {
  auth.signOut();
};

로그아웃하지 않고 다시 접속하면 로그인페이지로 화면이 redirect된다.

추후에 앱 개발을 하게 된다면 아래 로직대로 개발해야겠다.

  • 앱종료시 로그아웃
  • 로그인상태 유지하는 메소드 추가
profile
일단 해볼게요!✍🏻

0개의 댓글