회고/TIL
TIL - 20230209
k1mwnjn
2023. 2. 9. 23:00
React - Firebase Authentication 이메일 회원가입 & 로그인
로그인과 회원가입 기능을 프로젝트를 진행하며 처음 맡아 간단하게 정리해본다.
Firebase 는 여러 소셜 로그인도 제공하지만 튜터님 피드백을 따라 기본적으로 이메일 회원가입/로그인을 구현해보자.
프로젝트에 Firebase 패키지 설치 후 기본 세팅까진 완료된 상태다.
// login page
import {
browserSessionPersistence,
setPersistence,
signInWithEmailAndPassword,
} from "firebase/auth";
import { useRef, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import AuthForm from "../../components/Auth/AuthForm";
import { authService } from "../../config/firebase";
const LoginPage = () => {
const navigate = useNavigate();
const { state } = useLocation();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const emailRef = useRef(null);
const passwordRef = useRef(null);
// 이메일 입력
const changeEmail = (event) => {
setEmail(event.target.value);
};
// 비밀번호 입력
const changePassword = (event) => {
setPassword(event.target.value);
};
// 이메일, 비밀번호 유효성 검사
const checkValidation = () => {
const emailRegex = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/g;
const passwordRegex =
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$/;
const checkEmailValidation = email.match(emailRegex);
const checkPasswordValidation = password.match(passwordRegex);
if (!email || !checkEmailValidation) {
if (!email) {
alert("이메일을 입력해주세요.");
emailRef?.current?.focus();
return false;
} else {
alert("이메일 형식을 올바르게 입력해주세요.");
emailRef?.current?.focus();
return false;
}
}
if (!password || !checkPasswordValidation) {
if (!password) {
alert("비밀번호를 입력해주세요.");
passwordRef?.current?.focus();
return false;
} else {
alert(
"비밀번호는 대소문자, 특수문자를 포함하여 8자리 이상이어야 합니다."
);
passwordRef?.current?.focus();
setPassword("");
return false;
}
}
return true;
};
// 로그인
const submitLogin = () => {
// 이메일, 비밀번호 유효성 검사 확인
if (!checkValidation()) return;
// setPersistence => 세션스토리지에 유저 정보 저장
setPersistence(authService, browserSessionPersistence)
.then(() => signInWithEmailAndPassword(authService, email, password))
.then(() => {
alert("환영합니다!");
setEmail("");
setPassword("");
if (state) {
navigate(state);
} else {
navigate("/", { replace: true });
}
})
.catch((err) => {
if (err.message.includes("user-not-found")) {
alert("가입 정보가 없습니다. 회원가입을 먼저 진행해 주세요.");
// navigate("/signup", { state });
emailRef?.current?.focus();
setEmail("");
setPassword("");
}
if (err.message.includes("wrong-password")) {
alert("잘못된 비밀번호 입니다.");
passwordRef?.current?.focus();
setPassword("");
}
});
};
// const socialBtn = [
// { title: "카카오", img: require("../../assets/kakaotalk.png") },
// { title: "네이버", img: require("../../assets/naver.png") },
// { title: "구글", img: require("../../assets/google.png") },
// { title: "페이스북", img: require("../../assets/facebook.png") },
// { title: "애플", img: require("../../assets/apple.png") },
// ];
return (
<AuthForm
title="예·적금이 필요한 순간, 목돈"
text="아직 회원이 아니신가요?"
linkText="회원가입하기"
email={email}
changeEmail={changeEmail}
emailRef={emailRef}
password={password}
changePassword={changePassword}
passwordRef={passwordRef}
// socialBtn={socialBtn}
submitLogin={submitLogin}
/>
);
};
export default LoginPage;
// signup page
import {
browserSessionPersistence,
createUserWithEmailAndPassword,
setPersistence,
} from "firebase/auth";
import React, { useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import AuthForm from "../../components/Auth/AuthForm";
import { authService } from "../../config/firebase";
const SignUpPage = () => {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const emailRef = useRef(null);
const passwordRef = useRef(null);
const confirmPasswordRef = useRef(null);
// 이메일 입력
const changeEmail = (event) => {
setEmail(event.target.value);
};
// 비밀번호 입력
const changePassword = (event) => {
setPassword(event.target.value);
};
// 비밀번호 재입력
const changeConfirmPassword = (event) => {
setConfirmPassword(event.target.value);
};
// 이메일, 비밀번호 유효성 검사
const checkValidation = () => {
const emailRegex = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/g;
const passwordRegex =
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$/;
const checkEmailValidation = email.match(emailRegex);
const checkPasswordValidation = password.match(passwordRegex);
if (!email || !checkEmailValidation) {
if (!email) {
alert("이메일을 입력해주세요.");
emailRef?.current?.focus();
return false;
} else {
alert("이메일 형식을 올바르게 입력해주세요.");
emailRef?.current?.focus();
return false;
}
}
if (!password || !checkPasswordValidation) {
if (!password) {
alert("비밀번호를 입력해주세요.");
passwordRef?.current?.focus();
return false;
} else {
alert(
"비밀번호는 대소문자, 특수문자를 포함하여 8자리 이상이어야 합니다."
);
passwordRef?.current?.focus();
setPassword("");
return false;
}
}
return true;
};
// 비밀번호 일치 여부
const checkValidationForSignUp = () => {
if (!confirmPassword) {
alert("비밀번호를 다시 한번 더 입력해주세요.");
return false;
}
if (password !== confirmPassword) {
alert("비밀번호가 일치하지 않습니다.");
confirmPasswordRef?.current?.focus();
// setPassword("");
setConfirmPassword("");
return false;
}
return true;
};
// 회원가입
const submitSignUp = () => {
// 이메일, 비밀번호 유효성 검사 확인
if (!checkValidation()) return;
// 비밀번호 일치여부 확인
if (!checkValidationForSignUp()) return;
// setPersistence => 세션스토리지에 유저 정보 저장
setPersistence(authService, browserSessionPersistence)
.then(() => createUserWithEmailAndPassword(authService, email, password))
.then(() => {
alert("회원가입이 완료 되었습니다.");
setEmail("");
setPassword("");
setConfirmPassword("");
navigate("/");
})
.catch((err) => {
if (err.message.includes("already-in-use")) {
alert("이미 가입된 계정입니다.");
setEmail("");
setPassword("");
setConfirmPassword("");
navigate("/login");
}
});
};
return (
<AuthForm
title="회원 가입 정보 입력"
text="회원이신가요?"
linkText="로그인하기"
email={email}
changeEmail={changeEmail}
emailRef={emailRef}
password={password}
changePassword={changePassword}
passwordRef={passwordRef}
confirmPassword={confirmPassword}
changeConfirmPassword={changeConfirmPassword}
confirmPasswordRef={confirmPasswordRef}
submitSignUp={submitSignUp}
/>
);
};
export default SignUpPage;
// auth form
import React from "react";
import { Link } from "react-router-dom";
import { GoogleLogin } from "./GoogleLogin";
import { KakaoLogin } from "./KakaoLogin";
import { NaverLogin } from "./NaverLogin";
import {
AuthBackground,
AuthButton,
AuthInput,
AuthInputWrapper,
AuthLabel,
AuthLogo,
AuthLogoImg,
AuthText,
LinkText,
AuthTitle,
AuthWrapper,
DefaultLoginForm,
SocialLoginForm,
SocialLoginTitle,
SocialLoginList,
} from "./style";
const AuthForm = ({
title,
text,
linkText,
email,
changeEmail,
emailRef,
password,
changePassword,
passwordRef,
confirmPassword,
changeConfirmPassword,
confirmPasswordRef,
submitSignUp,
submitLogin,
}) => {
const signUp = title === "회원 가입 정보 입력";
return (
<AuthBackground>
<AuthWrapper>
<AuthLogo>
<Link to="/">
<AuthLogoImg src={require("../../assets/star.png")} />
</Link>
</AuthLogo>
<AuthTitle>{title}</AuthTitle>
<AuthText>
{text}
<Link to={`${signUp ? "/login" : "/signup"}`}>
<LinkText>{linkText}</LinkText>
</Link>
</AuthText>
<DefaultLoginForm>
<AuthInputWrapper>
<AuthLabel>이메일</AuthLabel>
<AuthInput
id="email"
type="email"
placeholder="example.gmail.com"
value={email}
onChange={changeEmail}
ref={emailRef}
/>
<AuthLabel>비밀번호</AuthLabel>
<AuthInput
id="password"
type="password"
placeholder="비밀번호 입력"
value={password}
onChange={changePassword}
ref={passwordRef}
/>
{signUp ? (
<>
<AuthLabel>비밀번호 재입력</AuthLabel>
<AuthInput
id="confirm-password"
type="password"
placeholder="비밀번호 재입력"
value={confirmPassword}
onChange={changeConfirmPassword}
ref={confirmPasswordRef}
/>
</>
) : (
""
)}
</AuthInputWrapper>
{signUp ? (
<AuthButton onClick={submitSignUp}>회원가입</AuthButton>
) : (
<AuthButton onClick={submitLogin}>로그인</AuthButton>
)}
</DefaultLoginForm>
{!signUp && (
<SocialLoginForm>
<SocialLoginTitle>또는</SocialLoginTitle>
<SocialLoginList>
<KakaoLogin />
<NaverLogin />
<GoogleLogin />
</SocialLoginList>
</SocialLoginForm>
)}
</AuthWrapper>
</AuthBackground>
);
};
export default AuthForm;
로그인과 회원가입 페이지를 먼저 만들고 공통되는 부분은 어스폼에 리팩토링했지만 아직 가야할 길이 멀다.