Calit 프로젝트 개발 일지 -5 (실시간 데이터 연동 + Recoil)

이영욱·2023년 11월 4일

Calit 개발 일지

목록 보기
5/8
post-thumbnail

세상에서 가장 쉬운 애자일 협업툴 Calit!
깃허브 링크
배포 링크

협업 툴 실시간 데이터

이번 Calit 프로젝트에서 제일 중요한 것 중 하나는 대부분의 협업툴이 갖추고 있는 실시간 통신 기능이였다.

백엔드 개발자 없이 프론트엔드 개발자 3명이서 진행하기 위해 여러 사용가능한 서비스들을 찾아보았고, 마지막에 선택한것이 Firebase의 FireStore.

특히나 Calit ERD 및 데이터 모델링 에서 작성한 것과 같이 Calit 프로젝트는 아래와 같이 NoSQL에서 하위 컬렉션 구조를 이용하고 있다. 실시간 구독을 이용해 하위 컬렉션들을 미리 불러와 화면에 표시하고 즉각적으로 딜레이 없이 데이터들을 넘겨주는것이 중요했다.

라우터 별 저장할 데이터

실시간 구독으로 받아오는 데이터들을 전역 상태관리 라이브러리인 Recoil을 이용해 보관하고 있다면 언제 어디서든 필요한 상황에서 쉽게 꺼내서 사용할 수 있기 때문에 프로젝트 내에서도 어떻게 데이터를 관리할 지 결정하는 것도 중요했다.

따라서 1차로 로그인 라우터에서 유저 정보를, 프로젝트 확인 라우터에서 프로젝트 및 하위 컬렉션인 칸반을 모두 조회하고, 칸반 클릭시 칸반의 하위 컬렉션인 투두를 모두 조회하여 물 흐르듯이 위에서 아래로 내려주도록 설계하고자 하였다.

  • PrivateRoute - 로그인 한 유저 실시간 조회
  • ProjectCheckRoute - 프로젝트 및 칸반 실시간 조회
  • Project - 선택한 칸반 내 투두 실시간 조회

추가로 FireStore의 실시간 통신인 onSnapshot 메서드는 쿼리 결과로 가져온 document가 수정, 추가, 삭제 되었는지 상태 변화를 인지할 수 있어 기존에 받아온 데이터 또한 Recoil 내에서 캐싱하여 사용할 수 있어 데이터를 관리하기에 매우 용이했다.
FireStore onSnapshot 메서드 공식문서

Recoil과 onSnapshot을 이용한 실시간 데이터 캐싱

프로젝트와 같이 문서 하나만을 받아서 데이터를 저장하는 것은 간단하지만,
칸반과 같이 하위 컬렉션 전체를 가져오는것은 배열로 저장하게 되면 시간 복잡도가 O(n)이 되어 불필요하게 시간을 요구하게 된다.

따라서 배열이 아닌 Map 객체를 이용하여 key에는 각 하위컬렉션 문서의 ID, value에는 각 문서를 저장하여 O(1)인 상수 시간으로 접근 가능하게 구현하여 바로바로 Recoil 내 데이터를 추가, 수정, 삭제, 접근할 수 있도록 구현했다.

// kanbanState.ts
import { atom } from "recoil";

const kanbanState = atom({
  key: "kanbanData",
  default: new Map(),
});

export default kanbanState;

// ProjectCheckRoute.tsx
const setKanbanDataState = useSetRecoilState(kanbanState);

useEffect(() => {
  ...
  ...
  const kanbanQuery = query(
    collection(db, "project", pathname, "data"),
    where("is_deleted", "==", false),
  );
  const unsubKanban = onSnapshot(kanbanQuery, (kanbanSnapshot) => {
    const addedMap = new Map();
    kanbanSnapshot.docChanges().forEach((change) => {
      // 최초 Snapshot 생성 혹은 사용자가 직접 칸반을 추가했을 때
      if (change.type === "added") {
        addedMap.set(change.doc.id, change.doc.data());
      }
      // 칸반을 수정할 경우
      if (change.type === "modified") {
        setKanbanDataState((prev) => {
          prev.set(change.doc.id, change.doc.data());
          return new Map([...prev]);
        });
      }
      // 칸반이 삭제된 경우 (is_deleted 수정 시 쿼리 결과 변경)
      if (change.type === "removed") {
        setKanbanDataState((prev) => {
          prev.delete(change.doc.id);
          return new Map([...prev]);
        });
      }
    });
    if (addedMap.size > 0) {
      setKanbanDataState((prev) => new Map([...prev, ...addedMap]));
    }
  });
  ...
  ...
}, [kanbanId])

정리

이번 과정을 통해 Calit 프로젝트에 적합한 NoSQL 데이터 구조 선정 및 onSnapshot 메서드와 Recoil, Map 타입 State를 이용하여 최대한 간결하고 빠른 데이터 구조를 만들 수 있었다.

이렇게 폭포수처럼 위에서 아래로 자연스럽게 흐르는 데이터 구조를 적합하게 이용한다면 이후 전역 데이터를 이용해 손쉽게 기능 개발을 진행할 수 있을것이다!

profile
다양한 경험을 통해 성장하는 개발자, 이영욱 입니다.

0개의 댓글