프론트엔드✏️/개인공부

[firebase] Firebase 이메일 중복확인

당근먹는하니 2023. 7. 28. 17:27
728x90
반응형

 

일단 이름 짱 긴 함수 보고 가시죠. 

import {
  getFirestore,
  addDoc,
  collection,
  query,
  where,
  getDocs,
} from "firebase/firestore";

// config 생략

const app = initializeApp(firebaseConfig);

const db = getFirestore(app);

// 이메일 중복확인
export const checkDuplicateEmailWithFirebase = async (email) => {

  try {
    // db 중복 확인
    const q = query(
      collection(db, "users"),
      where("email", "==", "db2@db.com")
    );

    const querySnapshot = await getDocs(q);


    querySnapshot.forEach((doc) => {
      console.log(doc.id, " => ", doc.data());
    });
  } catch (error) {
    console.log("email::", error);
  }
};

이 함수는 src/common/api/firebase.js 에 위치해있다.

물론 어디 위치에 있든 마음대로다,,,

(예전에 블로그 보다보면 내용은 있어도 어디에 써야할지 몰라서 곤란했던 적이 많아서 써봅니다..)

 

나는 이메일 중복 확인을 위해 가져왔지만! 

firebase firestore에서 데이터를 가져오는 방법이다.

 

collection(db, {컬렉션 이름}),

where({찾을 필드}, "==", {찾을 내용})

 

즉 코드의 뜻은, users라는 컬렉션에서 email이 "db2@db.com"인 문서 찾아줘~ 임!!!

 

있으면 콘솔에 이렇게 예쁘게 찍힙니다요. 

여기서 where() 생략하면 모든 문서를 찍어볼 수 있다. 

 

근데,,, 그런 문서가 존재하지 않는다면??

error로 가거나 할 줄 알았는데 아무반응이 없었다.

 

 

그래서,,, chatGPT에게 물어봤습니다.

 

console.log(querySnapshot.empty)하면 true나 false가 나온다. 

 

// 이메일 중복확인
export const checkDuplicateEmailWithFirebase = async (email) => {
  console.log(email);
  try {
    // db 중복 확인
    const q = query(
      collection(db, "users"),
      where("email", "==", "db2@db.com")
    );
    console.log(q);
    const querySnapshot = await getDocs(q);

    console.log(querySnapshot.empty);

    return querySnapshot.empty ? false : true;

  } catch (error) {
    console.log("check email", error);
  }
};

비어있으면? -> 중복이 아님! 그래서 false를 보내기로 했다.

!querySnapshot.empty 해도 될 것이다. 

 

const checkDuplicateEmail = () => {
    const email = getValues().email;

    const isDuplicated = checkDuplicateEmailWithFirebase(email);
    // true : 중복, false : 통과! \

    console.log(isDuplicated);
  };

함수를 갖다쓰는 부분에선 이렇게 쓴다.

근데 아무리봐도 함수 이름이 너무 긴데 ㅋㅋㅋ

 

이제 이 변수를 state에 저장하든가 해서 지지고볶고 하면 되겠지,,, 

헉 이거 완전 ㄹㅅ님 말투네

 

 

 useEffect(() => {
    const email = getValues().email;

    const isDuplicated = checkDuplicateEmailWithFirebase(email);
    // true : 중복, false : 통과!

    console.log("isDU", isDuplicated);
    if (isDuplicated) {
      setError("email", {
        type: "custom",
        message: "중복된 이메일입니다.",
      });
    }
  }, [getValues().email]);

스키마에서... 함수를 가져와서 쓰기가 왠지 거부감이 느껴져서(?)

useEffect로 했는데, isDuplicated에 Object가 들어온다! 

 

 또 chatGPT에게 물어보았다. 

 

checkDuplicateEmailWithFirebase 함수는 async 함수이므로 해당 함수를 호출하면 프로미스(promise)가 반환됩니다. 따라서 isDuplicated 변수에는 프로미스 객체가 저장되며, console.log를 통해 출력하면 프로미스 객체가 보이게 됩니다.
useEffect 함수는 비동기 함수를 직접 호출할 수 없으므로, async/await 구문을 사용하여 checkDuplicateEmailWithFirebase 함수를 호출해야 합니다. 이때, useEffect 함수에는 콜백 함수 내에서 async 키워드를 사용할 수 없습니다. 대신, 콜백 함수 내부에서 비동기 함수를 호출하고, 해당 함수의 반환 값을 처리하는 방식으로 해결할 수 있습니다.

 

useEffect(() => {
  const checkEmailDuplicate = async () => {
    const email = getValues().email;

    try {
      const isDuplicated = await checkDuplicateEmailWithFirebase(email);
      console.log("isDuplicated", isDuplicated);

      if (isDuplicated) {
        setError("email", {
          type: "custom",
          message: "중복된 이메일입니다.",
        });
      }
    } catch (error) {
      console.log("check email", error);
    }
  };

  checkEmailDuplicate();
}, [getValues().email]);

쓰고 싶으면 이렇게 쓰면 되겠다. 

 

나는 스키마 파일로 옮겼다..

 

import * as yup from "yup";
import { checkDuplicateEmailWithFirebase } from "../api/firebase";

// 이메일 중복확인
export const checkDuplicateEmailWithYup = async (email) => {
  try {
    try {
      const isDuplicated = checkDuplicateEmailWithFirebase(email);
      return isDuplicated;
    } catch (error) {
      console.log(error);
    }
  } catch (error) {
    // 유효성 검사 실패 시 에러 처리
    console.log("check email", error);
    throw error.message;
  }
};

export const signUpSchema = yup.object({
  phoneNumber: yup.string().required("필수 입력 항목입니다."),
  email: yup
    .string()
    .required("필수 입력 항목입니다.")
    .test("check-email", "중복된 이메일입니다.", async function (value) {
      return !(await checkDuplicateEmailWithYup(value));
    })
    .matches(
      /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
      "올바르지 않은 이메일 형식입니다."
    ),
  password: yup
    .string()
    .matches(
      /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d~!@#$%^&*()_+\-=\[\]{};':"<>?,./\\]{8,}$/,
      "영문, 숫자를 포함해 최소 8자를 입력해주세요."
    )
    .required("필수 입력 항목입니다."),
  confirmPassword: yup
    .string()
    .oneOf([yup.ref("password"), null], "비밀번호가 일치하지 않습니다.")
    .required("필수 입력 항목입니다."),
  nickname: yup
    .string()
    .matches(
      /^[a-zA-Z가-힣]{1,8}$/,
      "한글, 영문만 사용하여 최대 8자까지 가능합니다."
    )
    .max(8)
    .required("필수 입력 항목입니다."),
});

export const signInSchema = yup.object({
  email: yup.string().required("아이디를 입력해주세요."),
  password: yup.string().required("비밀번호를 입력해주세요."),
});
 .test("check-email", "중복된 이메일입니다.", async function (value) {
      return !(await checkDuplicateEmailWithYup(value));
    })

이렇게 체크하면 된다. 

 

 

근데 아직 한 번 제출하기도 전에 빨간색으로 뜨는 게 약간... 거슬려요.

 

 

  const {
    control,
    handleSubmit,
    getValues,
    formState: { errors, isSubmitted, isSubmitting },
  } = useForm({
    resolver: yupResolver(signUpSchema),
    defaultValues: {
      phoneNumber: "",
      email: "",
      password: "",
      confirmPassword: "",
      nickname: "",
    },
    mode: "onChange",
  });

isSubmitted를 가져와서 (한 번 제출하면 true가 된다)

 

     <Text
            className={`text-[12px] ${
              isSubmitted ? "text-red-600" : "text-gray-400"
            }`}
          >

그럼 한 번 제출하기 전까진 gray로 뜬다! 

728x90
반응형