ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 리액트의 데이터관리 - 피드백 바탕
    카테고리 없음 2023. 6. 9. 16:50

    https://seojisoosoo.tistory.com/12

     

    리액트의 데이터 관리 React Query vs Asynchronous Recoil

    들어가며 리액트의 서버 데이터 관리란? 리액트의 데이터 관리 방법에 대해 알아보기 이전에, '데이터 관리'란 무엇인지 짚고 넘어갈 필요가 있다. 데이터 관리란 변화하는 데이터들을 관리하는

    seojisoosoo.tistory.com

    -
    ✅ Context와 Redux 부분에서, "로딩/에러 처리를 할 때 useEffect 를 사용하기 때문에 코드양이 방대해진다"라고 이해했어요. 
    컴포넌트 단에서는 쿼리와 같이 early return이나 조건부 렌더링을 할 수는 없나요? 
    그렇게 상태를 설계할 수는 없을까요?
    컴포넌트 단에서의 처리가 줄어든다면, 문제점이 계속해서 유효한가요?
    (저는 코드를 줄여준다 라는 부분도 정말 잘 되어있지만, 산발적인 상태를 관리함보다 선언적으로 관리함이 더 중요하다고 생각해요. https://snupi.tistory.com/217 이 글도 읽어보시고 생각을 넓혀보면 더 좋을 것 같아요!)

     

    사실 useEffect를 사용해서 코드양이 방대해진다는 의미보다, (1) isError, isLoading을 setState로 true, flase로 변경해주는 코드  (2) useEffect로 로딩/에러 처리하는 코드 가 유사하게 반복된다는 의미를 전하고 싶었어요! useEffect를 사용하지 않고도, 아래와 같이 쿼리와 동일하게 early return혹은 조건부 렌더링을 할 수 있을 거라 생각해요, isLoading이 true가 될 때마다 로딩페이지를, isError가 true가 될 때마다 에러페이지를 보여줄 수 있으니까요. 

    useEffect(() => {  
    // isLoading이 변경될 때마다 Loading 처리  
    }, [isLoading]);
    
    if (isLoading){
    	return <LoadingPage/>;
    }

    하지만 useEffect로 처리하는 로직이 줄어들어도, 데이터를 패칭할 때마다 아래와 같이 state값을 변경해주어야하는 점은 남아있어요. 쿼리처럼 로딩이나 에러를 직접적으로 알려주는 프로퍼티가 없기 때문에 state로 로딩과 에러 여부를 저장하게 되면, (1) 데이터 패칭을 시작할 때 isLoading true로 변경 (2) 에러가 감지되면 isError true로 변경 (3) 에러 없이 데이터 패칭이 잘 이루어졌다면 isLoading false로 변경 의 로직을 거칠 텐데요, 그럼 하나의 api 당 1~2 번의 state 변경이 있다는 뜻이고, 그렇다면 데이터 패칭 한 번에 리렌더링이 1~2회 발생한다는 의미가 됩니다. 그래서 사실 useEffect를 사용하지 않아도, state로 로딩과 에러를 처리하게 되면 리렌더링이 많이 일어날 것이고, 이러한 문제는 state를 변경하는 코드가 유사하게 반복된다는 문제점뿐만 아니라 불필요한 리렌더링을 발생시키는 문제라고 생각했습니다!

      setData((prev) => ({  
        ...prev,  
        error: undefined,  
        isError: false,  
        isLoading: true,  
      }));

     

    주신 글도 잘 읽어보았는데요, 유효한 상태를 지향해야한다는 점이 흥미로웠어요, isLoading과 error가 동시에 존재하는 경우 등의 사례는 생각해보지 못했는데, 단순히 try-catch로 에러가 있는지 없는지 파악하는 것에서 더 나아가 현재의 상태가 어떤 상태인지를 명확히 할 수 있어야겠다는 생각을 했어요. 감사합니다!!!

    -
     Suspense를 이용하는 것에서, 한 컴포넌트 내에서 다수의 API에 사용하는 등 남용한다는 표현이 이해가 되지 않아요. 
    이 문제가 Suspense의 문제인가요? 설계의 문제인 건가요? 
    좀 더 설명해봐주시면 감사하겠습니다!

     

    제가 "Suspense를 이용하는 것이 굉장히 편리하기 때문에 한 컴포넌트 내에서 두 개의 API에 사용하는 등 남용할 수도 있다. 이렇게 되면 네트워크 병목 현상이 생길 수 있기 때문에, suspense를 남용하지 않는 것이 좋다."라는 이야기를 작성했는데요, https://happysisyphe.tistory.com/54 이 글을 읽고 suspense의 남용에 대한 이야기를 접했어요! Suspense는 Promise를 catch하는 방식인데, 한 컴포넌트에 두 개 이상의 쿼리를 사용하게 되면, 첫번째 쿼리의 loading이 끝나고 데이터패칭이 완료되어야 두번째 쿼리의 데이터패칭이 시작된다는 사실을 알게 되었습니다. 하지만 아래와 같이 쿼리문을 동시에 부르는 useQueries를 사용하면 병렬적으로 두 쿼리의 데이터패칭이된다고 해요, 그래서 이 문제는 Suspense의 문제라기보다 설계의 문제라고 생각합니다. pending인지 settled인지의 값을 가지는 Promise를 catch하는 Suspense의 특징을  생각하여, 한 컴포넌트에서 여러 쿼리를 부르지 않고, 아래와 같이 useQueries로 한 번에 부르게 된다면 해결될 문제라고 생각합니다!

     

    const [{ data: todoList }, { data: postList }] = useQueries([
        {
          queryKey: ["todo"],
          queryFn: () => client.get<Todo[]>("/todos"),
          suspense: true,
        },
        {
          queryKey: ["post"],
          queryFn: () => client.get<Post[]>("/posts"),
          suspense: true,
        },
      ]);



     Recoil에서 suspense와 error boundary를 꼭 사용해야 하나요? 
    fetch-on-render 방식으로 isLoading, isError를 사용해 직접 관리할 수는 없을까요?

     

    Recoil에서 suspense와 error boundary를 꼭 사용해야할 필요는 없다고 생각해요. 셀렉터에서 데이터를 가져오는 과정에서 try catch문을 사용해서 isError가 true false인지를 판단하고, 데이터패칭이 시작되기 전에는 isLoading을 true, 종료되고나면 isLoading을 false로 변경하는 과정을 거칠 수 있다고 생각합니다. 하지만 이 경우도 위와 같이 불필요한 리렌더링을 발생시킬 수도 있을 것이라 생각해요. 그리고 fetch-on-render방식의 경우는 비동기 작업들이 순차적으로 진행된다고 해요, 그렇다면 한 컴포넌트 내에서 로딩이 되는 부분이 두 개 존재한다면 동시에 진행되지 않고 순차적으로 진행되어서 결국 로딩시간이 더 길어지는 문제가 생긴다고 생각합니다. 반면, Suspense와 ErrorBoundary를 사용하게 되면, 비동기 작업이 순차적으로 진행되는 것이 아니라 한 번에 같이 진행되게 되고, 선언적인 이용을 할 수 있기 때문에, suspense와 error boundary를 사용하면 좋겠다는 생각을 했어요! 

     

    관련글 : https://fe-developers.kakaoent.com/2021/211127-211209-suspense/

     

    -
     Asynchronous Recoil 방식이 SWR, RQ와 같은 동작을 하나요?
    어떻게 다르고 어떤 기준으로 사용하면 좋을지 설명해주시면 좋을 것 같아요!

     

    RQ가... React Query인가요?

    세 가지 개념을 각각 공부하고 혼자 생각한 내용이라 오류가 있을 수도 있지만, 비교를 하자면, SWR과 React Query는 유사하게 동작하지만, Recoil은 조금 다르다고 생각했어요. SWR, RQ 모두 api, fetcher, options을 불러오는 로직이지만, Reocil은 selector를 이용하는 방식이기 때문이에요. 

     

    //SWR
    const { data, error } = useSWR("/api/user", fetcher, options);
    
    //React query
    const result = useQuery(`/user/${id}`, fetcher, options);
    
    //Asynchronous Recoil
    const currentUserNameQuery = selector({
      key: 'CurrentUserName',
      get: async ({get}) => {
        const response = await myDBQuery({
          userID: get(currentUserIDState),
        });
        return response.name;
      },
    });
    
    function CurrentUserInfo() {
      const userName = useRecoilValue(currentUserNameQuery);
      return <div>{userName}</div>;
    }

     

    우선 세 방식 모두 데이터 관리를 위한 라이브러리이지만, 사용 목적이 좀 다르다고 생각했어요.

    React Query

    React Query ... makes fetching, caching, synchronizing and updating server state in your React applications a breeze.
    ...React Query는 리액트 어플리케이션에서 서버 상태를 가져오고, 캐싱하고, 동기화하며, 업데이트하는 작업을 손쉽게 만들어주는 라이브러리입니다.

    React Query의 경우에는 data프로퍼티를 바로 사용할 수 있기 때문에, 서버의 데이터를 별도로 가공하지 않고 바로 사용하는데에 적합하다고 생각했어요. 즉 server state 데이터를 관리하기 적합하다고 생각했습니다. 데이터를 한 번 패칭하고 그 뒤로 패칭하지 않을 수도 있고, 계속해서 패칭되어야하는 데이터가 있을 수도 있는데, 그러한 과정을 리액트 쿼리를 이용해서 관리할 수 있다고 생각했어요.

     

    SWR

    SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.
    SWR은 stale: 캐시에서 데이터를 찾아 먼저 반환하고, revalidate: 데이터 가져오기 요청을 보내어, 결과적으로 최신의 데이터를 사용하기 위한 전략입니다.

    SWR은 리액트쿼리와 유사하지만 최신의 데이터로 업데이트되는데에 집중한다고 해요. 즉, 최신의 데이터로 업데이트가 필요한 '페이지가 많을 때' 사용하기 좋다고 생각했어요.

     

    Asynchronous Recoil

    If the user names were stored in some database we need to query, all we need to do is return a 
    Promise  or use an async  function. If any dependencies change, the selector will be re-evaluated and execute a new query. The results are cached, so the query will only execute once per unique input.

    Asynchronous Recoil방식은 서버의 데이터를 패칭해와서 가공을 하여 return하기 좋다고 생각했습니다. 그리고 recoil selector에서는 SWR, RQ와 다르게 데이터 리패칭이 불가능하다고 하더라고요, 그런 면을 살펴보면 계속 변경되는 데이터보다는 한번 불러와서 계속 사용하는 데이터를 관리하는 데 적합하다고 생각했습니다!

     

     

    관련 글 : https://yanggoon.dev/showcase/swr-query

     

    +

    https://velog.io/@teo/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-MV-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80%EC%9A%94

    댓글

Designed by Tistory.