프론트엔드로 가는 길/portfolio 제작 여정기 👩🏻💻
04. 자살예방웹사이트 - Authentication로 로그인 제작
woody-j
2023. 9. 1. 13:30
로그인 제공
1) Authentication -> 빌드 -> 로그인 제공업체
2) 로그인 제공업체의 이메일/비밀번호 설정
자 그럼 이제 코드를 짜봅시다
1. 기본 설정
1) 로그인 기능을 위한 라이브러리 import
2. 회원가입
import { createUserWithEmailAndPassword, getAuth } from "firebase/auth";
import { getFirestore, doc, setDoc } from "firebase/firestore";
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import styled from "styled-components";
import { Btn, HighlightText, Paragraph } from "../../module/styled/styledFont";
// 이메일 정규식 : 영문자와 숫자만
const regexID = /^[a-zA-Z0-9]{3,16}$/;
// 비밀번호 형식
const regexPass = /^[a-zA-Z가-힣!@#$%^&*()_+|<>?:{}]*.{5,16}$/;
// 닉네임 형식
const regexNickname = /^[가-힣a-zA-Z0-9]{2,10}$/;
const Form = styled.form`
border-radius: 5px;
`;
const Input = styled.input`
width: 100%;
max-width: 400px;
margin-bottom: 10px;
padding: 15px 10px;
border: none;
border: 1px solid ${({ theme }) => theme.color.mainGray};
border-radius: 5px;
outline: none;
&::placeholder {
color: ${({ theme }) => theme.color.mainGray};
}
&:focus {
border-color: ${({ theme }) => theme.color.mainBlack}; /* 포커스된 상태일 때의 선 색상 변경 */
}
`;
const Nicknamebox = styled.div`
margin-top: 40px;
`;
const Idbox = styled.div``;
const Passwordbox = styled.div`
margin-bottom: 20px;
`;
const Register = styled.div`
font-size: 1.2rem;
cursor: pointer;
`;
const SigninBox = styled(HighlightText)`
margin-left: 5px;
text-underline-offset: 4px;
`;
const SignUpBtn = styled(Btn)`
margin-bottom: 20px;
`;
const ErrorTextBox = styled.div`
margin-bottom: 10px;
margin-left: 5px;
text-align: left;
`;
const ErrorText = styled(Paragraph)`
color: ${({ theme }) => theme.color.mainRed};
font-size: 1rem;
`;
const initialIdNotice = {
alert: false,
message: "",
};
const SignUpForm = ({ onLogin, onChange }: any) => {
const [nickName, setNickName] = useState("");
const [userId, setUserId] = useState("");
const [password, setPassword] = useState("");
const [nickNameNotice, setNicknameNotice] = useState(initialIdNotice);
const [idNotice, setIdNotice] = useState(initialIdNotice);
const [passNotice, setPassNotice] = useState(initialIdNotice);
const [error, setError] = useState("");
const navigate = useNavigate();
// nickname input 유효성 검사
const onBlurNicknameHandler = async () => {
if (nickName === "") {
setNicknameNotice({ message: "필수항목입니다.", alert: false });
return;
}
};
const onChangeNicknameHandler = async (e: { target: { value: React.SetStateAction<string> } }) => {
setNickName(e.target.value);
const isValidNickname = regexNickname.test(nickName);
if (!isValidNickname) {
setNicknameNotice({
message: "3 ~ 10자의 한글, 영문, 숫자 조합으로 입력해야 합니다.",
alert: false,
});
return;
}
setNicknameNotice({
message: "",
alert: true,
});
};
// userId input 유효성 검사
const onBlurIdHandler = () => {
if (userId === "") {
setIdNotice({ message: "필수항목입니다.", alert: false });
return;
}
};
const onChangeIdHandler = (e: { target: { value: React.SetStateAction<string> } }) => {
setUserId(e.target.value);
const isValidID = regexID.test(userId);
if (!isValidID) {
setIdNotice({
message: "4 ~ 16자의 영문, 숫자 조합으로 입력해야 합니다.",
alert: false,
});
return;
}
setIdNotice({
message: "",
alert: true,
});
};
// password input 유효성 검사
const onBlurPasswordHandler = () => {
if (password === "") {
setPassNotice({ message: "필수항목입니다.", alert: false });
return;
}
};
const onChangePasswordHandler = (e: { target: { value: React.SetStateAction<string> } }) => {
setPassword(e.target.value);
const isValidPassword = regexPass.test(password);
if (!isValidPassword) {
setPassNotice({
message: "한글을 제외한 6 ~ 16자의 문자로 입력해야 합니다.",
alert: false,
});
return;
}
setPassNotice({
message: "",
alert: true,
});
};
const auth = getAuth();
const db = getFirestore();
const handleSubmitSignUp = async (
e: React.FormEvent,
userId: string,
password: string,
nickName: string
) => {
e.preventDefault();
try {
const userCredential = await createUserWithEmailAndPassword(auth, `${userId}@myapp.com`, password);
const user = userCredential.user;
// 사용자 정보를 Firestore에 저장
await setDoc(doc(db, "nickName", user.uid), {
email: `${userId}@myapp.com`,
nickname: nickName,
});
alert("회원가입이 완료되었습니다.");
navigate("/auth/signIn");
console.log("회원가입 성공:", user);
setError("");
// 회원가입 성공 시에 원하는 동작을 추가해주세요.
} catch (error) {
console.error("회원가입 실패:", error.message);
setError(error.message);
}
};
return (
<Form
onSubmit={(e: { preventDefault: () => void }) => handleSubmitSignUp(e, userId, password, nickName)}
>
<Nicknamebox>
<Input
type="text"
value={nickName}
minLength={3}
maxLength={10}
onChange={onChangeNicknameHandler}
onBlur={onBlurNicknameHandler}
placeholder="닉네임"
/>
<ErrorTextBox>
{nickNameNotice.alert ? null : <ErrorText>{nickNameNotice.message}</ErrorText>}
</ErrorTextBox>
</Nicknamebox>
<Idbox>
<Input
type="text"
id="userId"
value={userId}
minLength={4}
maxLength={16}
onChange={onChangeIdHandler}
onBlur={onBlurIdHandler}
placeholder="아이디"
/>
<ErrorTextBox>{idNotice.alert ? null : <ErrorText>{idNotice.message}</ErrorText>}</ErrorTextBox>
</Idbox>
<Passwordbox>
<Input
type="password"
id="password"
value={password}
minLength={4}
maxLength={16}
onBlur={onBlurPasswordHandler}
onChange={onChangePasswordHandler}
placeholder="비밀번호"
/>
<ErrorTextBox>
{passNotice.alert ? null : <ErrorText>{passNotice.message}</ErrorText>}
</ErrorTextBox>
</Passwordbox>
<SignUpBtn type="submit">회원가입</SignUpBtn>
<Register>
<Link to="/auth/signIn">
이미 회원이신가요?
<SigninBox showunderline>로그인</SigninBox>
</Link>
</Register>
</Form>
);
};
export default SignUpForm;
1) auth 객체를 생성
const auth = getAuth();
Firebase 인증 모듈을 사용하여 객체를 생성합니다.
이 객체를 사용해 사용자의 로그인 및 회원가입을 관리합니다.
2) db 객체를 생성
const db = getFirestore();
Firebase Firestore 데이터베이스 모듈을 사용하여 객체를 생성합니다.
이 객체를 사용해 사용자의 정보를 저장합니다.
await setDoc(doc(db, "nickName", user.uid), {
email: `${userId}@myapp.com`,
nickname: nickName,
});
: 나는 닉네임과 id,pw 3개를 동시에 보내고 싶었는데 auth인증 모듈은 그게 불가능 했다.
그래서 따로 닉네임도 저장시키기 위해 회원가입할 때 같이 db객체를 생성해서 같이 보내줬다.
3) 회원가입 폼의 제출 이벤트 핸들러 함수
const handleSubmitSignUp =
async (e: React.FormEvent, userId: string, password: string, nickName: string) => {
함수의 매개변수로 이벤트 객체 e, 사용자 아이디 userId, 비밀번호 password, 닉네임 nickName이 전달된다.
4) createUserWithEmailAndPassword 함수를 사용하여 회원가입
const userCredential =
await createUserWithEmailAndPassword(auth, ${userId}@myapp.com, password);
auth 객체를 사용하여 이메일 형식의 아이디와 비밀번호를 전달한다.
이때 ${userId}@myapp.com 형식으로 이메일을 만들어 사용한다. -> 나는 이메일형식말고 id로만 만들고 싶었다.
하지만 id형식만 할 수 없어서 사용자가 id만 작성하면 보내줄 때 @-를 붙여서 보내주도록 했다.
3. 로그인
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import styled from "styled-components";
import { loginEmail, signupEmail } from "../../firebaseConfig";
import { Btn, HighlightText } from "../../module/styled/styledFont";
const Form = styled.form``;
const Input = styled.input`
width: 100%;
max-width: 400px;
margin-bottom: 20px;
padding: 15px 10px;
border: none;
border-radius: 5px;
border: 1px solid ${({ theme }) => theme.color.mainGray};
outline: none;
&::placeholder {
color: ${({ theme }) => theme.color.mainGray};
}
&:focus {
border-color: ${({ theme }) => theme.color.mainBlack}; /* 포커스된 상태일 때의 선 색상 변경 */
}
`;
const Idbox = styled.div`
margin-top: 40px;
`;
const Passwordbox = styled.div``;
const ProcessBox = styled.div`
max-width: 400px;
width: 100%;
margin-bottom: 30px;
font-size: 1.3rem;
`;
const CheckboxLabel = styled.label`
display: flex;
gap: 5px;
justify-content: center;
align-items: center;
cursor: pointer;
`;
const CheckboxInput = styled.input``;
const AutoLoginText = styled.div``;
const Register = styled.div`
font-size: 1.2rem;
cursor: pointer;
`;
const SignUpBox = styled(HighlightText)`
margin-left: 5px;
text-underline-offset: 4px;
`;
const SignInBtn = styled(Btn)`
margin-bottom: 20px;
`;
const SignInForm = ({ onLogin, onChange }: any) => {
const [userId, setUserId] = useState("");
const [password, setPassword] = useState("");
const [isChecked, setIsChecked] = useState(false);
const navigate = useNavigate();
const handleCheckboxChange = () => {
const newChecked = !isChecked;
setIsChecked(newChecked);
};
const handleLogin = async (e: { preventDefault: () => void }) => {
e.preventDefault();
try {
const result = await loginEmail(`${userId}@myapp.com`, password);
const user = result.user;
console.log(user);
alert("로그인되었습니다.");
navigate("/");
} catch (error) {
console.error(error);
}
};
return (
<Form onSubmit={handleLogin}>
<Idbox>
<Input
type="text"
id="username"
value={userId}
onChange={(e: { target: { value: React.SetStateAction<string> } }) =>
setUserId(e.target.value)
}
placeholder="아이디"
/>
</Idbox>
<Passwordbox>
<Input
type="password"
id="password"
value={password}
onChange={(e: { target: { value: React.SetStateAction<string> } }) =>
setPassword(e.target.value)
}
placeholder="비밀번호"
/>
</Passwordbox>
<ProcessBox>
<CheckboxLabel>
<CheckboxInput type="checkbox" checked={isChecked} onChange={handleCheckboxChange} />
<AutoLoginText>자동 로그인</AutoLoginText>
</CheckboxLabel>
</ProcessBox>
<SignInBtn type="submit">로그인</SignInBtn>
<Register>
<Link to="/auth/signUp">
회원이 아니신가요?
<SignUpBox showunderline>회원가입</SignUpBox>
</Link>
</Register>
</Form>
);
};
export default SignInForm;