[토이프로젝트] 랜덤 아티클 추천 서비스 04 데이터 페이지 기능 구현

대만이·2023년 5월 26일
0
post-thumbnail

🏋️ Todo

데이터 페이지에서 구현할 기능은 다음과 같다.

  • 데이터 입력 및 조회
    - 링크의 제목이 리스트로 나열됨. 제목이 없는 경우 링크가 나열
    - 제목(또는 링크) 클릭시 해당 페이지로 이동
    - 인풋박스에 링크를 넣고 링크추가클릭시 데이터가 추가됨
  • 읽음데이터로 플래그 변경
    - 사용자가 done에 체크하면 해당데이터는 추천 범위에 들어가지 않음
    - 사용자가 done에 체크했던 기록을 해제하면 다시 추천 가능 범위에 들어감
  • 데이터 삭제
    - 휴지통 아이콘을 클릭시 해당 데이터 삭제
  • 우측 상단 홈 아이콘 클릭시 메인 페이지로 이동



01 데이터 입력 및 조회

백엔드 서비스로 firebase를 사용했다.
토이프로젝트에서 대중적으로 사용하는 개발 플랫폼이기도 하고
기존에 인프런 프론트엔드 강의를 들었을 때 써본 내용을 복습하기 위해서 선택했다.
💬 요즘 supabase관련 글도 많이 보여서 추후에는 해당 플랫폼으로도 구현해볼 것


데이터 입력

const onClickAddArticle = async () => {
    const list = collection(db, 'list'); // 'list'컬렉션 생성(지정)
    const listRef = await addDoc(list, { // 'list'안에 문서 생성
      id: '',
      title: inputs.title,
      link: inputs.link,
    });
    await updateDoc(doc(db, 'list', listRef.id), { id: listRef.id }); // 자동생성된 id를 데이터의 id 필드에 업데이트
    setInputs({ id: '', title: '', link: '' });
  };

firebase에서 문서를 생성하는 방법은 두가지로 setDoc()addDoc()이 있다.

  • setDoc()은 문서의 id를 직접 지정할 수 있다.
  • addDoc()은 임의로 문자열의 id가 생성된다.

여기서는 임의의 아이디를 만들어서 문서의 필드에 넣어놓고 추후 삭제 등의 기능에 활용하기 위해 addDoc()을 사용했다.

💬 이렇게 업데이트해서 필드에 넣는 방식이 아니라 문서가 생성될 때 자동으로 id필드를 만들어놓을 순 없을까? 이런 기능이 firebase내에 있을 것 같은데 찾아볼 것.


데이터 조회

 useEffect(() => {
    const list = collection(getFirestore(firebaseApp), 'list');
    const newListsData = onSnapshot(list, (snapshot) => {
      const datas = snapshot.docs.map((doc) => doc.data());
      console.log(datas);
      setListsData(datas);
    });
    return () => {
      newListsData();
    };
  }, []);

페이지가 렌더링 된 후 바로 데이터리스트가 보이도록 하기 위해 useEffect()안에 해당 함수를 넣었다.
firebase에서 전체 문서를 조회하는 방법에는 getDocs()onSnapshot()이 있는데

  • getDocs()는 호출시 데이터를 한번 가져오고
  • onSnapshot()은 실시간으로 데이터의 업데이트를 감지하고 데이터가 업데이트 될 때마다 새로 가져온다.

여기서는 사용자가 새 데이터를 추가했을 때 바로 하단의 리스트가 업데이트 되어야하므로 onSnapshot()을 사용했다.

🤬 에러일지 useEffect 무한렌더링

const [listsData, setListsData] = useState<any[]>([]);
...
useEffect(() => {
  const list = collection(getFirestore(firebaseApp), 'list');
  const fetchLists = async () => {
    const result = await getDocs(list);
    const datas = result.docs.map((el) => el.data());
    setListsData(datas);
  };
  fetchLists();
}, [listData]);

처음에는 getDocs()를 사용한 뒤 useEffect()에서 listData가 변경될 때 리렌더링을 하면 되지 않을까 했는데 무한렌더링의 늪에 빠졌다.
이유인즉슨..
useEffect() 안에서 setState()로 변경되는 stateuseEffect()의 변경조건으로 넣으면([state]) 아래 두 과정이 무한히 반복되는 것과 같기 때문이다..🤯

😇 onSnapshot() 기능 사용으로 데이터 변경시 자동 업데이트 되도록 변경 완료


02 읽음데이터로 플래그 변경

📄 presenter.tsx

<s.IsRead
  checked={el.isRead}
  onChange={props.onChangeRead}
  id={el.id}
  >
  Done
</s.IsRead>
📄 container.tsx

const onChangeRead = async (e: CheckboxChangeEvent) => {
  const data = listsData.find((el) => el.id === e.target.id);
  if (data) {
    const updatedIsRead = !data.isRead;
    await updateDoc(doc(db, 'list', data.id), { isRead: updatedIsRead });
    }
  };

checkbox(isRead컴포넌트) 클릭시 onChangeRead함수가 실행된다. 작동방식은 아래와 같다.

  • 앞서 updateDoc()으로 id필드에 넣어놓은 id값과 일치하는 문서를 find()메소드로 특정한다.
  • 해당 문서의 isRead필드를 이전과 반대로 변경한다.
  • 변경된 값을 updateDoc()을 활용해 db의 문서 정보로 업데이트한다.

03 데이터 삭제

📄 presenter.tsx

{props.listsData.map((el, index) => (
  <s.List key={el.id}>
    <s.Title href={el.link} target='_blank'>
      {el.title}
    </s.Title>
    <s.ListMenu>
      <s.IsRead>Done</s.IsRead>
      <DeleteOutlined
        onClick={props.onClickDeleteArticle} // 클릭시 delete 실행
        id={el.id}
        />
    </s.ListMenu>
  </s.List>
))}
📄 container.tsx

   const onClickDeleteArticle = async (e: MouseEvent<HTMLSpanElement>) => {
    await deleteDoc(doc(db, 'list', e.currentTarget.id)); // id 받아와서 해당 문서 삭제
  };

icon클릭시 onClickDeleteArticle함수가 실행된다. 작동방식은 아래와 같다.

  • 앞서 updateDoc()으로 id필드에 넣어놓은 id값으로 문서를 특정한다.
  • deleteDoc()메소드를 이용해 해당 문서를 삭제한다.

04 홈 아이콘 클릭시 메인 페이지로 이동

<const router = useRouter();
...
const onClickMoveToHome = () => {
  router.push('/');
};

useRouter()push()메소드를 사용해 아이콘 클릭시 main 페이지로 이동하도록 구현했다.




역시 직접 만들어봐야 머릿속에 개념이 잡히는 것 같다.
인프런 프론트엔드 강의로 익혔던 개념들을 하나하나 다시 활용해보면서 복습하게 된다.
이제 메인페이지도 후딱 만들어봐야지.
나의 작고 소중한 토이프로젝트..

profile
느려도 오래 걷습니다👟

0개의 댓글