데이터 로딩 상태관리 - 리뷰 탭

지렁·2024년 2월 29일

JD(회사) 상세페이지에 현재 상세정보, 리뷰(리뷰개수) 탭이 존재한다
현재 아래와 같이 구현해놓았다

  • 리뷰 수가 9개 이하면 reviewCount, 9개 이상이면 9+ 보여주기
  • 페이지 접속 시에는 상세정보 탭 활성화, 그리고 리뷰 탭을 클릭해야 리뷰 데이터 받아오기

문제는 두가지였다

  1. reviewCount 데이터가 아직 불러와지지 않았을 때는 9+로 잠시 뜨는 것
  2. 리뷰 수정 탭에서 리뷰를 추가 혹은 삭제했을 때 리뷰 탭에 리뷰 수가 즉각적으로 반영되지 않는 것

위의 문제는 둘 다 상태관리의 문제였다
프론트엔드에서는 다양한 상태관리법이 존재하는데 나는 state을 이용해서만 해보려고 한다

1. reviewCount 데이터가 아직 불러와지지 않았을 때는 9+로 잠시 뜨는 것

export function CategoryTab() {
  const [value, setValue] = useState("1");
  const [jdData, setJdData] = useState({});
  const [reviewNum, setReviewNum] = useState(); // 리뷰 개수 state

  const { id } = useParams();

  const handleTabChange = (e, newValue) => {
    setValue(newValue);
  };

  useEffect(() => {
    (async () => {
      const res = await getJdDetail(id);
      setJdData(res);
      setReviewNum(res.reviewCount); // api 응답 데이터.reviewCount를 저장
    })();
  }, [id, jdData.reviewCount]);

  return (
    <Box>
      <TabContext value={value}>
        <TabList
          onChange={handleTabChange}
          sx={{ pt: 2 }}
          TabIndicatorProps={{ style: MainStyles.TabIndicator }}
        >
          <Tab label="상세 정보" value="1" sx={MainStyles.Tab} />
          <Tab
            label={`리뷰(${
              jdData.reviewCount < 9 ? jdData.reviewCount : "9+"
            })`}
            value="2"
            sx={MainStyles.Tab}
          />
        </TabList>
        <TabPanelItem value="1">
          <TabForInfo jdData={jdData} />
        </TabPanelItem>
        <TabPanelItem value="2">
          <TabForReview id={id} />
        </TabPanelItem>
      </TabContext>
    </Box>
  );
}

데이터 로딩 전에 "9+"가 잠깐 보이는 문제는 reviewNum 상태의 초기값 설정 때문에 발생하는 것이다
초기 상태가 undefined로 설정되어 있고, 조건부 렌더링에서 undefined < 9로 설정하면 undefined 이기 때문에 false가 되어 "9+"가 잠시 표시되는 것이다 ㅠㅠ
이 문제를 해결하기 위해 reviewNum의 초기값을 명시적으로 0으로 설정하거나, 데이터 로딩 상태를 나타내는 별도의 상태 값을 사용하여 로딩 중과 로딩 완료 상태를 구분해야한다!

다음과 같이 reviewNum의 초기값을 0으로 설정하면 로딩 되기 전 9+ 가 뜨는 것을 방지할 수 있다

  const [reviewNum, setReviewNum] = useState(0);

추가적으로 로딩 state도 추가하였다

(이 부분은 리팩토링 과정에서 리액트 쿼리로 대체할 것이다)

export function CategoryTab() {
  const [value, setValue] = useState("1");
  const [jdData, setJdData] = useState({});
  const [reviewNum, setReviewNum] = useState(0);
  const [isLoading, setIsLoading] = useState(true);

...

  useEffect(() => {
    (async () => {
      setIsLoading(true); // 데이터 로딩 시작
      const res = await getJdDetail(id);
      setJdData(res);
      setReviewNum(res.reviewCount);
      setIsLoading(false); // 데이터 로딩 완료
    })();
  }, [id]);

  return (
    <Box>
      <TabContext value={value}>
        <TabList
          onChange={handleTabChange}
          sx={{ pt: 2 }}
          TabIndicatorProps={{ style: MainStyles.TabIndicator }}
        >
          <Tab label="상세 정보" value="1" sx={MainStyles.Tab} />
          <Tab
            label={
              isLoading
                ? "리뷰 로딩 중..."
                : `리뷰(${reviewNum < 9 ? reviewNum : "9+"})`
            }
            value="2"
            sx={MainStyles.Tab}
          />
        </TabList>

이렇게 isLoading state을 추가하여 label 도 isLoading 의 조건을 추가하여 구현하였다

로딩상태일 때는 "리뷰 로딩 중..." 을 뜨도록 구현함으로써 더 친절한 UI가 되었다

2. 리뷰 수정 탭에서 리뷰를 추가 혹은 삭제했을 때 리뷰 탭에 리뷰 수가 즉각적으로 반영되지 않는 것

리액트는 state 변경이 될때마다 재렌더링이 된다

위의 조건을 이용하여 reviewNum setter 함수를 prop으로 내려줌으로써 해결할 수 있었다

CategoryTab.tsx

 <TabPanelItem value="2">
 	 <TabForReview id={id} setReviewNum={setReviewNum} />
  </TabPanelItem>
  </TabContext>

TabForReview

export function TabForReview({ id, setReviewNum }) {
  const { isOpen, openPopup, closePopup } = usePopup();
  const loginState = useRecoilValue(isLoggedInState);
  const [reviewData, setReviewData] = useState({ content: [], pageInfo: {} });

  ...

  const addReviewAndUpdate = async (newReview) => {
    await addReivew({ jdId: Number(id), content: newReview.content });
    fetchReviewData(0);
    alert("리뷰가 등록되었습니다");
    setReviewNum((prev) => prev + 1);
    closePopup();
  };

  const deleteReviewAndUpdate = async (reviewId) => {
    await delReivew(reviewId);
    fetchReviewData(0);
    alert("리뷰가 정상적으로 삭제되었습니다");
    setReviewNum((prev) => prev - 1);
  };

이렇게 리뷰 추가, 리뷰 삭제 함수에서 reViewNum을 리액트 상에서 변경을 하면서 UI 적으로 동작이 잘 되도록 구현할 수 있었다

profile
공부 기록 공간 🎈💻

0개의 댓글