[React, Firebase]투두리스트 Create,Read 구현

이은지·2023년 11월 28일
0

사용자의 uid와 할 일이 생성된 날짜를 기준으로 생성된 투두리스트를 불러오고, 할 일을 추가하는 기능을 구현하려 한다.

1. Create 기능 구현

🔆 메인페이지 컴포넌트 - MainPage.js

  • 사용자가 AddButton을 클릭하면 modalIsOpen 상태가 토글되며, AddTodo 컴포넌트가 조건부로 렌더링 되게 함
    • modalIsOpen 상태가 true이면 모달창이 렌더링
function MainPage() {
  const [currentUser, setcurrentUser] = useState();
  const [modalIsOpen, setModalIsOpen] = useState(false);

  const user = useSelector(state=>state.User.userData);

  if(currentUser !== undefined){

  return (
    <m.MainpageLayout>
		 {...생략...}
          <m.AddButton onClick={()=>setModalIsOpen(!modalIsOpen)}>
            <BsPlusCircleDotted className='add_icon'/>
            <p className='add_content'>새로운 할 일 생성하기</p>
          </m.AddButton>
    
      {modalIsOpen && <AddTodo onClose={setModalIsOpen} />}
  </m.MainpageLayout>
  )

}else{
  <div>
    로딩중...
  </div>
}
}

export default auth(MainPage, true);

🔆 AddTodo컴포넌트

  • 모달 창의 레이아웃 및 UI 요소: 모달의 제목, 입력 필드, 마이크 아이콘, "등록하기" 버튼
import React,{useState} from 'react'
import * as a from '../../../../style/AddTodostyle'
import {FaMicrophone} from 'react-icons/fa'
import {ImCancelCircle} from 'react-icons/im'
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux'
import { addTodo } from '../../../../_redux/todo';
import dayjs from 'dayjs';
function AddTodo(props) {
    const user = useSelector(state=>state.User.userData);
    const {onClose} = props;
    const [Todo, setTodo] = useState();

    const dispatch = useDispatch();
    const handleCloseModal = () => {
        setTodo('');
        onClose(false);

    };

    const handleChange = (e)=>{
        setTodo(e.target.value);
    }

    const handleSubmit = ()=>{

        let variable = {
            Time: dayjs().format('YYYY-MM-DD'),
            Content: Todo,
            Uid: user.Uid,
            Completed: false
        } 
        dispatch(addTodo(variable));
        handleCloseModal();

    }
    return (
        <a.ModalLayout>
            <div style={{ width: '95%', display: 'flex', flexDirection: 'row', alignItems: 'center'}}>
                <p className='modaltitle'>할 일 추가하기</p>
                <ImCancelCircle onClick={handleCloseModal} style={{width: '1.8rem', height: '1.8rem', marginLeft: '12rem', color:'#1B1D1F'}}/>
            </div>
            <hr/>
            <a.ContentInput type="text" value={Todo} onChange={handleChange}>

            </a.ContentInput>
            <FaMicrophone className='microphone'/>
            <a.SubmitBtn onClick={handleSubmit}>등록하기</a.SubmitBtn>
        </a.ModalLayout>
    )
}

export default AddTodo

1. handleSubmit 함수

  • variable 객체 생성: 새로운 할 일 데이터를 나타내는 variable 객체를 생성
    • Time: 현재 날짜를 dayjs를 사용하여 "YYYY-MM-DD" 형식으로 포맷
    • Content: 사용자가 입력한 할 일 내용(Todo state)
    • Uid: 현재 사용자의 UID(user.Uid)
      • Uid는 useSelector를 사용해 Redux store로부터 사용자 uid 정보를 가져온다.
    • Completed: 할 일의 완료 상태(기본적으로 false로 설정)
  • dispatch 함수를 통해 addTodo 액션함수를 실행
  • 액션함수 실행을 통해 todo가 저장되면 해당 모달창을 닫는 handleCloseModal 함수를 실행

🔆 리덕스 파일 - todo.js

import { saveTodo, findallTodo } from "../firebase/firebase_todo";

//Actions
const ADD_TODO = 'todo_reducer/addtodo';

// Action Creators
export function addTodo(data){
    saveTodo(data);
    return { 
        type: ADD_TODO,
        payload: data
    };
};

//Reducer
const initialState = {
    todos:[]
}

export default function (state = initialState, action){
    switch (action.type) {
        case ADD_TODO:
            // 현재 todos 배열에 새로운 todo 데이터 추가
            const newTodos = [...state.todos, action.payload];
            return { ...state, todos: newTodos };
        default:
            return state;
    }
}
  1. Action 정의
  • ADD_TODO: 할 일을 추가하는 액션 타입을 상수로 정의
  1. Action Creators 정의
  • addTodo(data): 새로운 할 일 데이터를 saveTodo함수를 통해 Firebase에 저장한 다음, 이 데이터를 Redux 스토어에 추가하는 액션을 생성. 생성된 액션 객체에는 typepayload가 구성되며, payload에는 추가된 할 일 데이터가 포함된다.
  1. Reducer 정의
  • ADD_TODO 액션 타입에 대한 처리: 현재 todos 배열에 새로운 할 일 데이터(action.payload)를 추가한 새로운 배열을 생성하고, 이 배열을 포함하는 새로운 상태 객체를 반환

🔆 파이어베이스 파일 - firebase_todo.js

export async function saveTodo(data){

    const todos = firestore.collection("todos");
    try{
        await todos.doc().set(data)
        
    }catch(error){
        console.log(error);
        throw error;
    }

}

2. Read 기능 구현

🔆 메인페이지 컴포넌트 - MainPage.jsx

function MainPage() {
  const [currentUser, setcurrentUser] = useState();
	const user = useSelector(state=>state.User.userData);

	useEffect(()=>{
	
	    if(user && Object.keys(user).length > 0){
	      console.log(user);
	      setcurrentUser(user);
	    
	    }else{
	      console.log("No user")
	    }
	
		if(currentUser){
		      console.log(selectedDay);
		        dispatch(initializeTodo({
		          uid: currentUser.Uid,
		          time: selectedDay
		        }))
		    }
		
	  }, [user]);
}
  1. 먼저, 로그인한 사용자의 정보를 useSelector를 통해 Redux Store에서 가져온다.
  • 이때, Redux store 업데이트 속도 때문에 그냥 user정보를 사용하려 하면, undefined가 자꾸 뜨는 문제가 발생
    • 해결 방법: if (user && Object.keys(user).length > 0) 조건문을 사용해, 사용자 데이터(user)가 비어있지 않고, 적어도 하나 이상의 속성을 가지고 있다면 currentUser state에 해당 user정보를 넣어주었다.
  1. currentUser가 존재하면, 사용자의 uid와 선택된 날짜를 기준으로 투두리스트를 조회하는 액션 함수 호출

🔆 리덕스 파일 - todo.js

import { saveTodo, findallTodo } from "../firebase/firebase_todo";

//Actions
const INITIALIZE_TODOS = 'todo_reducer/initializetodo'

// Action Creators

export async function initializeTodo(data){
    try{
        const todos = await findallTodo(data);
        console.log(todos);
        return{
            type: INITIALIZE_TODOS,
            payload: todos
        }
    }catch(error){
    console.error("Todo 데이터 초기화 실패:", error);
    throw error;
    }
    
}
   
//Reducer

const initialState = {
    todos:[]
}

export default function (state = initialState, action){
    switch (action.type) {
        case INITIALIZE_TODOS:
            return {...state, todos: action.payload}
        default:
            return state;
    }
}
  1. Action 정의
  • INITIALIZE_TODOS: 할 일 목록을 초기화하는 액션 타입을 상수로 정의
  1. Action Creators 정의
  • initializeTodo(data): 특정 사용자 및 날짜에 해당하는 할 일 목록을 Firebase에서 검색하고, 검색된 할 일 목록을 Redux 스토어에 초기화하는 비동기 액션을 생성. 이 액션은 findallTodo 함수를 사용하여 데이터를 가져와서 가져온 데이터를 payload로 설정하여 리턴
  1. Reducer 정의
  • INITIALIZE_TODOS 액션 타입에 대한 처리: 액션의 payload로 받아온 할 일 목록 데이터를 todos 배열로 설정한 새로운 상태 객체를 반환

🔆 파이어베이스 파일 - firebase_todo.js

  • findallTodo(data): Firebase Firestore를 사용하여 특정 사용자 및 시간에 해당하는 할 일 목록 데이터를 검색하는 함수
export async function findallTodo(data){

    const todos = firestore.collection("todos"); 
		//firestore 객체를 사용하여 Firestore의 'todo'컬렉션을 가져옴 
    const result = [] // 데이터를 저장할 배열 
    try{
		//todos 컬렉션에서 Uid 필드가 data.uid와 일치하고, 
		//Time 필드가 data.time과 일치하는 문서들을 검색하기 위한 Firestore 쿼리를 생성
        const query = todos.where('Uid', '==', data.uid).where('Time', '==', data.time);
        const snapshot = await query.get(); //쿼리 실행 

        if(!snapshot.empty){
            snapshot.forEach((doc)=>{
                const data = doc.data(); //각 문서의 데이터를 doc.data()를 사용하여 가져
                console.log(data);
                result.push(data); //할일 데이터를 result 배열에 추가 
            })
            return result; //배열 리턴
        }else{
            return [];
        }
    }catch(error){
        console.log(error);
        throw error;
    }
}
profile
소통하는 개발자가 꿈입니다!

0개의 댓글