#01 React와 Firebase로 구현하는 로그인 및 게시글 작성

728x90
반응형

 

#문제

▶ 이미지를 첨부하고 [등록하기] 버튼을 누르면

fireStorage에 사진을 저장 & FireStore DB에 url 저장 기능 구현을 하고 싶은데

FireStore DB에  image_url이 저장되기 전에, 동기 부분이 완료되서 image_url 저장이 안됨.

 

#해결

▶ FireStore에 데이터가 저장되기 전에 이미지 파일 업로드가 우선 실행되도록 위치로 조정

이미지 저장이 끝나고 FireStore에 데이터 저장되도록 async + await 코드로 수정

 [Inputform.jsx]    
 
 //이미지 파일 업로드
  const handleUpload = async () => {
    const imageRef = ref(storage, `${auth.currentUser.uid}/${selectedFile.name}`);
    try {
      await uploadBytes(imageRef, selectedFile);

      // 저장된 image url :getDownloadURL(imageRef)
      return await getDownloadURL(imageRef);
    } catch (erro) {
      console.log('Inputform.jsx (handleUpload): ', erro);
      throw error;
    }
  };
 
   
<form onSubmit={async (event) => {
         
       event.preventDefault();

        try {
            if (window.confirm('새글을 등록하시겠습니까?')) {
              //1. 이미지 파일 업로드
              const uploadImageUrl = await handleUpload();

              //2. 모달창에 입력된 새로운 데이터
              const newData = {
                email: 'test',
                content,
                store,
                date: new Date(),
                title,
                image_url: uploadImageUrl
              };
              setData((prev) => [newData, ...prev]);

              //3. 파이어스토어에 데이터 저장
              const collectionRef = collection(db, 'newData');
              await addDoc(collectionRef, newData);

              setModalOpen(false);
            } else { return;}
       }
     catch (Error) {
            console.log('Inputform.jsx (form Error): ', Error);
               }
      }}
   >
 

 

#Done

1) 모달팝업으로 만들기

2) 로그인 여부 판단 : 로그인한 사람만 새글 작성 간으

3) 파일 업로드 기능 추가하기 (파일 저장 : fireStorage + 파일 url : FireStore DB )

4) [등록하기] 버튼 : 저장 후 팝업닫기

5) [닫기] 버튼 : 확인 후 닫기 진행

6) 모달팝업은 하이라이트 & 배경은 흐리게 설정

7) 글 작성시 버튼 클릭시 테두리 하이라이트 설정

 

#Todo

1) props로 데이터 전달하는 부분 redux로 수정

1) [닫기] & [등록하기] 버튼 클릭시 모달팝업으로 처리

2) 작성자는 이메일말고 작성자 이름 또는 닉네임 처리 ( 미정 )

 

 


1. <InputformLayout.jsx > : 메인 레이아웃 페이지 

-  firestore에 저장된 데이터 가져오기 : await getDocs(query(colleciont(db,'db name'))); 

- 모달(modal) 상태 : const [modalOpen, setModalOpen] = useState(false); 

- 로그인 여부 판단 : <Auth> 컴포넌트 에서 로그인한 아이디 정보 update

 //로그인 정보 -- 로그인한 사용자가 댓글을 달 수 있도록
  const [currentEmail, setCurrentEmail] = useState('');
  const showModal = () => {
    //로그인 여부 체크 (자료형 false 반환 : "",null,undefined,0 NaN)
    if (!currentEmail) {
      alert('로그인하셔야 새글 작성이 가능합니다.');
      return;
    }
    setModalOpen(true);
  };

- <Auth> 컴포넌트 : 회원가입/로그인/로그아웃 기능

- <Button> 컴포넌트: 클릭시 모달창 오픈 기능

- <Modal> 컴포넌트 : 모달 상태값 true (modalOpen) && props : ( currentEmail : 현재로그인된 이메일) 전달


2. <Auth.jsx > : 회원가입 / 로그인 / 로그아웃 기능 + 현재 로그인된 이메일 정보

  //현재 로그인한 사용자 가져오기
  useEffect(() => {
    //(onAuthStateChanged)
    // 인증 객체의 상태변화 감지 리스너 (로그인/로그아웃)
      auth.onAuthStateChanged((user) => {
      setCurrentEmail(user ? auth.currentUser.email : '');
    });
  }, [currentEmail]);

- 현재 로그인된 이메일을 여기서 setCurrentEmail로 상태 업데이트

 

  const signUp = async (event) => {
    event.preventDefault();

    try {
      const userCredential = await createUserWithEmailAndPassword(auth, email, password);
      console.log('user', userCredential.user);
    } catch (error) {
      const errorCode = error.code;
      const errorMessage = error.message;
      console.log('error with signUp', errorCode, errorMessage);
    }
  };

- 회원가입기능 : 이메일과 비밀번호로 회원가입 가능 

 

  const signIn = async (event) => {
    event.preventDefault();
    try {
      const userCredential = await signInWithEmailAndPassword(auth, email, password);
      console.log('user with signIn', userCredential.user);
    } catch (error) {
      const errorCode = error.code;
      const errorMessage = error.message;
      console.log('error with signIn', errorCode, errorMessage);
    }
  };
 
  const logOut = async (event) => {
    event.preventDefault();
    await signOut(auth);
  };

-로그인  & 로그아웃 기능 


3. <Modal.jsx > : 클릭시 모달창 오픈 기능

- 마우스 hover 시 하이라이트 되도록 설정

 &:hover {
    border: 1px solid #7579e7;
    box-shadow: rgba(117, 121, 231, 0.4) 0px 0px 0px 3px;
  }

function Modal({ setModalOpen, setData, currentEmail }) {
  const closeModal = () => {
    setModalOpen(false);
  };

  return (
    <BackGround>
      <Container>
        <Button onClick={closeModal}>닫기</Button>
        <Inputform setData={setData} currentEmail={currentEmail} setModalOpen={setModalOpen} />
      </Container>
    </BackGround>
  );
}

- styled-component 구성

 <BackGround> Div : 모달창 오픈시 뒤에 흐려지는 부분 포인트 (4가지)

:  background-color: rgba(0, 0, 0, 0.5);
:  backdrop-filter: saturate(180%) blur(8px);
:  z-index: 10;
:  position: fixed;

<Container> Div : 메인 모달창 포인트 (2가지)

: z-index: 100; 
: position: absolute;

- <InputForm> 컴포넌트 : 모달창 내용 부분


4. <Inputform.jsx > : 새 글 작성 후 fireStorage에 저장 + 이미지 파일 올리기

 //이미지 파일 업로드
  const handleUpload = async () => {
    const imageRef = ref(storage, `${auth.currentUser.uid}/${selectedFile.name}`);
    try {
      await uploadBytes(imageRef, selectedFile);

      // 저장된 image url :getDownloadURL(imageRef)
      return await getDownloadURL(imageRef);
    } catch (erro) {
      console.log('Inputform.jsx (handleUpload): ', erro);
      throw error;
    }
  }; 
return(
<form onSubmit={async (event) => {
         
       event.preventDefault();

        try {
            if (window.confirm('새글을 등록하시겠습니까?')) {
              //1. 이미지 파일 업로드
              const uploadImageUrl = await handleUpload();

              //2. 모달창에 입력된 새로운 데이터
              const newData = {
                email: 'test',
                content,
                store,
                date: new Date(),
                title,
                image_url: uploadImageUrl
              };
              setData((prev) => [newData, ...prev]);

              //3. 파이어스토어에 데이터 저장
              const collectionRef = collection(db, 'newData');
              await addDoc(collectionRef, newData);

              setModalOpen(false);
            } else { return;}
       }
     catch (Error) {
            console.log('Inputform.jsx (form Error): ', Error);
               }
      }}
   >
)

▶ 맨 위에 #해결 부분

 

 

끝.

반응형