이 글은 리액트에서 Firebase로 이메일-패스워드 로그인을 구현하는 방법에 대해 정리한 글이다. 소셜로그인 구현은 다른 글로 차후에 작성할 예정이다.
Firebase 계정이 있다고 가정하고 글을 시작한다. 계정이 없다면 아래 링크(Firebase 홈페이지)에서 구글계정으로 계정을 활성화하고 시작하면 된다.
https://firebase.google.com/?hl=ko
Firebase 프로젝트 추가
Firebase 로그인을 완료하고, 시작하기 버튼(혹은 우측 상단의 콘솔로 이동)을 누르면 아래와 같이 본인의 Firebase 프로젝트를 모아볼 수 있는 창이 뜬다. 여기서 프로젝트 추가 버튼을 눌러 새로운 프로젝트를 추가한다.
프로젝트 이름을 입력하고 구글 애널리스틱 사용 여부는 본인의 판단하여(추후에 다시 활성화 할 수 있음, 필자는 우선 활성화하지 않고 시작) 프로젝트 생성을 한다. 프로젝트 생성이 완료되면 생성한 프로젝트의 관리 페이지로 이동한다.
Firebase 내 새로운 프로젝트 생성이 완료되었다. 이제 내 프로젝트에 Firebase를 등록해야 한다. 아래 화면에서 3번째 웹 프로젝트 추가를 누른다.
아래와 같은 화면이 뜨면, 웹 앱 이름을 입력하고 활성화된 앱 등록 버튼을 누른다. 호스팅 여부는 본인의 판단 하에 선택하면 되고, 필자는 우선 체크하지 않고 진행했다.
이 다음은 Firebase SDK를 추가해야 한다. 우선 본인의 리액트 프로젝트에서 아래 명령어를 입력해 Firebase를 프로젝트에 설치한다.
npm install firebase
Firebase 설치가 완료되었으면 앱 등록 후에 볼 수 있는 화면에 출력된 Firebase SDK를 복사해 본인의 프로젝트 src
폴더 아래에 firebase.ts
파일을 만들어 붙여넣는다. 이 정보는 본인 프로젝트의 고유한 정보이기 때문에 외부로 유출되지 않도록 하는 것이 중요하다.
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "...",
authDomain: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "..."
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
firebase.ts 파일을 만들었다면, 로그인을 구현하는 것이 목적이기 때문에 코드 맨 하단에 아래 코드를 추가한다.
...
import { getAuth } from "firebase/auth";
...
export const auth = getAuth(app);
이 과정은 Firebase의 인증 정보를 초기화하고 reference를 가져오는 것이다. 여기까지 완료했으면 프로젝트에 Firebase 추가는 모두 완료되었다.
Authentication 활성화
Firebase에는 기본적으로 많은 기능을 포함하고 있지만 기본적으로는 모두 비활성화 되어 있다. 필요한 기능만 선택해서 하나씩 활성화를 시켜야 한다.
로그인 기능을 구현하기 위해서는 Authentication 기능을 활성화해야 한다. Authentication으로 이동한 후 시작하기 버튼을 눌러서 활성화한다.
Firebase에서 기본적으로 제공하는 로그인 방식이 다양한 것을 확인할 수 있다. 이번 글에서는 이메일/비밀번호를 이용하여 로그인을 구현할 것이기 때문에 이메일/비밀번호를 선택한다.
그럼 아래와 같이 뜨는데, 우선 이메일/비밀번호 로그인을 활성화한다. 이메일/비밀번호는 우리가 흔히 사용하는 이메일과 비밀번호를 이용하여 로그인을 진행하는 방식으며, 이메일 링크는 로그인을 할 때 이메일만 입력하면 입력한 이메일로 메일이 발송되고, 발송된 메일의 링크를 클릭하여 로그인을 진행하는 방식이다.
그럼 아래와 같이 이메일/비밀번호 로그인 방식이 추가된 것을 확인할 수 있다.
Protected Routes 설정
Protected Routes는 로그인하지 않은 사용자의 접근을 막기 위해 사용하는 컴포넌트이다. 만약 로그인 여부와 사용자의 페이지 접근이 상관없다면 설정하지 않아도 된다.
Protected Routes 설정을 위해 protected-route.tsx
파일을 만들어 별도의 컴포넌트로 만들었다.
import { auth } from "@/firebase";
import { Navigate } from "react-router-dom";
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const user = auth.currentUser;
if (user === null) {
return <Navigate to="/login" />;
}
return children;
}
export default ProtectedRoute;
이 컴포넌트는 사용자 정보를 Firebase를 통해 받아와 로그인 여부를 파악한 후, 로그인이 되어 있지 않으면 강제로 로그인 페이지로 이동시키는 역할을 한다.
const user = auth.currentUser;
이 코드는 Firebase에 유저 정보를 요청하여 로그인이 되어 있다면 유저 정보를 받아오고, 로그인이 되어 있지 않으면 null
을 넘겨주는 역할을 한다. user
값이 만약 null
이라면, 로그인이 되어 있지 않기 때문에 Navigate
컴포넌트를 이용하여 로그인 페이지 /login
로 이동시키는 것이다.
ProtectedRoute
컴포넌트를 사용할 때에는 route를 설정한 곳에서 보호할 라우터를 ProtectedRoute
컴포넌트로 감싸주면 된다.
...
import ProtectedRoute from "./components/protected-route";
...
const router = createBrowserRouter([
{
path: "/",
element: (
<ProtectedRoute>
<MainPage />
</ProtectedRoute>
),
},
...
]);
function App() {
return (
<Wrapper>
<GlobalStyles />
<RouterProvider router={router} />
</Wrapper>
);
}
export default App;
위 코드는 /
경로로 접근할 때 ProtectedRoute
컴포넌트에서 먼저 사용자 로그인 여부를 확인하고, 로그인이 되어 있다면 MainPage
로 이동하고, 아니라면 ProtectedRoute
에서 설정한 /login
경로로 이동하게 된다.
로그인 구현
로그인 기능을 구현할 것이다. 로그인 페이지를 구성한 파일에서 onSubmit
에 해당하는 함수 내에서 try-catch
구문을 이용하여 구현한다. onSubmit
함수는 async-await
구문을 활용하여 로그인 진행 상황에 따라 차례로 동작하도록 해야 한다.
...
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); // 새로고침 방지
setError("");
if (email === "" || password === "") {
return;
}
try {
await signInWithEmailAndPassword(auth, email, password);
navigate("/");
} catch (error) {
if (error instanceof FirebaseError) {
setError(error.message);
}
}
};
...
React.FormEvent<HTMLFormElement>
는 해당 이벤트가 Form으로 왔다는 것을 명시하고 있으며, 이 이벤트가 Form 안에 어느 요소로부터 왔는지 HTMLFormElement
를 통해 명시하고 있다. 지금 위 코드에서는 onSubmit
함수가 form
태그 안에서 동작하기 때문에 HTMLFormElement
를 사용했고, 만약 input
태그 안에서 동작한다면 HTMLInputElement
가 사용될 것이다. (적어두고 보니 나도 뭔말인지 모르겠다.. 해당 문법은 React TypeScript에서 이벤트를 받아올 때 사용하는 방식이라고 한다... 추후에 공부해서 이 내용만 따로 다뤄보도록 해야겠다...)
로그인을 책임지는 함수는 signInWithEmailAndPassword
함수이다. 이 함수는 firebase/auth
에 포함되어 있는 함수로, 가져다 사용하면 되는 함수이다. 함수 이름 그대로 이메일과 패스워드를 통해 로그인을 구현할 때 사용한다. 인자로 auth
, email
, password
를 넘겨줘야 한다. auth
는 firebase.ts
파일을 만들 때 작성했던 auth
이다. 로그인이 완료되면 useNavigate
훅을 통해 홈 화면으로 이동한다.
이렇게 구성한 onSubmit
함수를 form
의 onSubmit
이벤트 리스너에 넘겨주면 로그인이 구현된다.
...
return (
<Wrapper className="container">
<Wrapper>
<Form className="loginForm" onSubmit={onSubmit}>
...
만약에 다 구현한 후에 페이지 새로고침은 되는데 페이지가 설정한 페이지로 넘어가지 않는다면, onSubmit
함수의 event.preventDefault()
부분을 확인해보면 좋다. 괄호가 빠지면 로그인이 되지 않는다. 괄호를 잘 입력했는지 확인하자. (필자가 이거 때문에 한참 고민했었다..ㅎ)
로그아웃 구현
로그인을 구현했다면, 이제 로그아웃도 할 수 있어야 한다. 로그아웃은 간단하게 구현할 수 있다.
...
function LogOutButton() {
const navigate = useNavigate();
const logOut = async () => {
await auth.signOut();
navigate("/login");
};
return (
<Wrapper className="container">
<Wrapper>
<Button className="logout" onClick={logOut}>
로그아웃
</Button>
</Wrapper>
</Wrapper>
);
}
...
핵심은 logOut
함수이다. async-await
함수로 만든 후, auth
의 signOut()
메소드를 사용하기만 하면 로그아웃이 구현된다. 로그아웃 전에 팝업 확인창을 통해 확인하는 과정을 거치고 싶다면, confirm
함수를 사용하면 구현할 수 있다.
...
const logOut = async () => {
const ok = confirm("로그아웃을 하시겠습니까?");
if (ok) {
await auth.signOut();
navigate("/login");
}
};
...
'Development > React' 카테고리의 다른 글
리액트 | 상대 경로를 절대 경로로 대체하기 (with. Vite & React ts) (0) | 2024.02.09 |
---|---|
리액트 | React Router를 이용한 페이지 이동 처리 (with. react-router-dom) (0) | 2024.02.04 |
리액트 | styled-components를 이용한 React 스타일링 (with. styled-reset) (0) | 2024.01.29 |
리액트 | Vite를 이용한 React 프로젝트 생성 (설치부터 프로젝트 생성까지) (0) | 2024.01.25 |
리액트 | 리액트 프로젝트 GitHub page로 배포하기 (0) | 2024.01.17 |