[React] Todo-List

oweaj·2023년 7월 17일
0

토이프로젝트

목록 보기
2/3
post-thumbnail

React로 간단하게 todolist를 만든 경험을 바탕으로 Typescript와 Firebase를 추가로 활용해서 조금더 todolist를 업그레이드를 해보았다.

✅ Todo-List 구성

먼저 Todo-List를 만들기 전에 필요한 기본적인 구성을 아래와같이 체크를 해보았다.

  • Firebase 활용한 회원가입, 이메일 로그인 및 구글 소셜로그인

  • 로그인을 통해 사용자별로 todolist를 관리하기

  • todolist의 추가, 가져오기, 수정, 삭제 등 구성


사용자가 로그인을 하면 로그인한 유저정보(email, uid)에 대한 상태를 저장 한다.

// App.tsx
const setUserInfo = useSetRecoilState(user);

  useEffect(() => {
    onAuthStateChanged(authService, async (user) => {
      if (user) {
        setUserInfo({
          email: user.email,
          uid: user.uid,
        });
      }
    });
  }, []);

📚 todolist 추가

const [todoValue, setTodoValue] = useState<string>("");
const isUser = useRecoilValue(user);

const addTodo = async () => {
    if (todoValue === "") {
      alert("빈 문장 입니다. 다시 입력해주세요.");
      return;
    }

    await addDoc(collection(db, `user/${isUser.uid}/todo`), {
      todo: todoValue,
      state: false,
      date: new Date().getTime(),
    });
    setTodoValue("");
  };

list를 적을 input에 onChange로 값을 추적해서 setTodoValue에 담아 사용하고 user 컬렉션에 사용자의 uid별로 문서를 넣고 todo 컬렉션에 todo에 대한 정보들을 담아 추가한다. 만약에 input에 todoValue의 값이 비어있으면 alert 메세지가 보여지고 추가는 되지않는다

❗️문제)

// ❗️ 한글만 입력하면 두 번 실행되는 경우
const addEnter = (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter") {
      addTodo();
    }
  };

추가하기 버튼을 클릭하면 todolist가 추가가 되는데 편리하게 키보드 enter로도 추가가능하게 하기 위해 버튼에 onKeyDown으로 위처럼 작성하고 실행을 한 결과 아래와 같이 한글만 입력하면 두 번씩 실행되었다.

키보드 이벤트에 대해 한글만 두 번 실행되는 경우를 찾아본결과 입력창에서 한글을 입력할 때 글자가 조합 중인지 조합이 끝났는지 확인할 수 없는 경우 발생하는 문제로 크롬 브라우저에서만 문제가 발생했다.(Safari, Firefox 경우 1번 실행)

해결책으로 키보드 이벤트에 입력 조합 문제를 확인하며 boolean 반환하는 프로퍼티로 isComposing를 알게되었다. 그래서 키보드이벤트 console를 찍어본결과 아래와 같이 두 번의 boolean값이 나온다.

✅ 해결)

브라우저의 고유 이벤트가 필요하다면 nativeEvent 어트리뷰트를 참조하세요.
고유 이벤트인 isComposing을 사용하려면 nativeEvent를 참조해야한다. 그래서 아래와 같이 nativeEvent.isComposing에 false를 입력하면 블럭을 추가하도록 되어 마지막 음절이 딸려오는 부분을 해결할 수 있다.

// 두 번 실행되는 부분 해결
const addEnter = (e: KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter" && e.nativeEvent.isComposing === false) {
      addTodo();
    }
  };

📚 todolist 가져오기

onSnapshot을 사용하여 실시간으로 db에서 사용자별 date기준으로 정렬된 데이터를 받아서 todoArr 배열에 추가하고 setTodoList를 호출해 업데이트된 todolist를 실시간으로 데이터를 받아 보여주기 때문에 로그아웃하고 다시 로그인해도 기존 todolist는 보여진다.

// 가져오기 부분 
const [todoList, setTodoList] = useState<todoInfo[]>([]);

useEffect(() => {
    const q = query(collection(db, `user/${isUser.uid}/todo`), orderBy("date", "asc"));
    const unsubscribe = onSnapshot(q, (querySnapshot) => {
      let todoArr: todoInfo[] = [];
      querySnapshot.forEach((doc) => {
        todoArr.push({ id: doc.id, ...doc.data() } as todoInfo);
      });
      setTodoList(todoArr);
    });
    return () => unsubscribe();
  }, [isUser.uid]);
  
return (
    <ul className="w-[30rem] h-[26rem] overflow-y-auto">
      {todoList && todoList.length ? (
        <div className="relative flex flex-row items-center gap-3 text-sm px-1">
          <span>상태</span>
          <span>할 일 제목</span>
          <div className="absolute right-3">
            <span className="mr-3">수정</span>
            <span>삭제</span>
          </div>
        </div>
      ) : null}
      {todoList && todoList.map((item: todoInfo, index) => <TodoItem key={index} item={item} isFinish={isFinish} isDelete={isDelete} />)}
    </ul>
  );


📚 todolist 완료 및 수정 업데이트

  • todolist 완료

완료한 todolist를 체크를 하게되면 해당 todoitem id에 state가 fasle에서 true로 업데이트가 되고 완료를 했기때문에 텍스트에 줄이 생겨 수정버튼 클릭을 할 수 없게 된다.

// todo를 완료한 경우 상태 변경 업데이트
const isFinish = async (item: todoInfo) => {
    await updateDoc(doc(db, `user/${isUser.uid}/todo`, item.id), {
      state: !item.state,
    });
  };

  • todolist 수정

수정 버튼을 클릭하게 될 경우 editMode로 변경되어 새로운 input창이 표시되고 입력할 경우 해당 todo id에 새로입력한 텍스트로 업데이트 된다.

// 수정해야할 todo를 새로운 todo로 입력후 업데이트
const [editMode, setEditMode] = useState<boolean>(false);

const isEdit = async (id: string, newTodo: string) => {
    await updateDoc(doc(db, `user/${isUser.uid}/todo`, id), {
      todo: newTodo,
    });
    setEditMode(false);
  };


📚 todolist 삭제

삭제 버튼을 클릭하게 될 경우 todo id에 맞는 todolist가 삭제가 된다.

const isDelete = async (id: string) => {
    await deleteDoc(doc(db, `user/${isUser.uid}/todo`, id));
  };


  • 7/26일 추가 및 수정으로 보완


🔎 전체 코드 확인하기

🖇️ Reference

profile
데굴데굴데굴데굴데굴

0개의 댓글