ET네 만물상 프로젝트 복기 - 상태관리

DD·2021년 9월 6일
0

우아한 테크캠프

목록 보기
12/14
post-thumbnail

📣 이 시리즈는...

  • 우아한 테크캠프 4기에서 마지막으로 진행한 프로젝트 전체를 복기해본 문서 시리즈입니다.
  • 제가 작성하지 않은 코드도 포함해서 복기했기에, 오류가 있을 수도 있는 개인적인 학습 기록을 위한 문서입니다!

ET네 만물상 - GitHub Repository / 배포 링크

  • 현재 배포 링크는 내부 문제로 API서버가 동작하지 않습니다.. 조만간 해결할 예정..



상태관리

  • 리액트의 시작과 끝이라 볼 수 있는, 가장 중요한 상태관리.
  • 이번 프로젝트에서는 react-query를 처음 사용해보았고, recoil 살짝 + React hooks로 상태를 관리했다.

react-query

  • API로 받아오는 비동기 데이터에 대한 처리를 위해 사용했다.

선택이유

    1. 한 번 써 보고 싶어서가 가장 크다.. 얘기는 들어봤고 이 기회에 한 번 써보자! 는 마음으로 선정했다.
    1. redux는 3주짜리 프로젝트에 적용하기엔 보일러 플레이트가 너무 크다.
    1. 마찬가지로 API로 다룰 데이터가 복잡하지 않기 때문에 redux의 보일러 플레이트를 사용하는 건 여러모로 배보다 배꼽이 큰 경우라고 판단했다.

사용하면서 느낀 점

const getMyReviews = (): Promise<ReviewType[]> => GET("/my/reviews");
export const useMyReviews = () => useQuery(["reviews"], () => getMyReviews());

const Review = () => {
 const { status, data, refetch } = useMyReviews();
  return {
    status !== 'loading'
    ? <div>
      {
        data.map((el)=><div>el</div>)
      }
      </div>
    : <div>스켈레톤 UI</div>
  }
}

1. 컴포넌트 함수 내에서 API 응답 전/후 처리가 깔끔하다

  • useQuery의 반환값에 있는 status를 사용해서 현재 API 요청 상태가 loading인지 success인지 등을 판단해서 상태에 맞는 렌더링이 가능하다

  • 일반적인 API 요청에 대한 로직은 default state로 렌더링을 하고, useEffect로 API 응답을 받으면 setState하는 방식인데 API를 일반 hooks처럼 사용할 수 있어서 편리하고, 깔끔하다.

2. 불필요한 클라이언트 상태가 생기지 않는다.

  • 위에서 얘기한 것 처럼 클라이언트 상태에 API에서 받아온 데이터를 set하는 과정 없이, 바로 data를 사용하면 된다.

  • 컴포넌트 함수인 만큼 렌더링에 해당 data를 사용하고, 휘발되면 그만이기에 더없이 깔끔하다고 생각함 !

3. refetch를 사용해서 원하는 시점에 갱신이 가능하다

  • useQuery는 기본으로 5분 동안 데이터를 캐싱한다. 그 사이에 강제로 캐싱 데이터를 갱신시키고, 리렌더를 일으키고 싶다면 refetch를 원하는 시점에 사용하면 된다.

사용법

1. QueryClientProvider, QueryClient 세팅

import { QueryClientProvider, QueryClient } from "react-query";

const queryClient = new QueryClient();

ReactDOM.render(
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  document.getElementById("app")
);
  • QueryClientProvider로 react-query를 사용할 영역을 지정하는데, 앱 전체에서 사용할 예정이므로 App 컴포넌트를 감싼다

  • QueryClient로 인스턴스를 생성해서 client prop 값으로 전달한다

  • QueryClient로 생성한 인스턴스는 캐시를 관리하기 위해 사용된다.

  • useQuery가 훅 안에서 해당 인스턴스에 접근할 수 있도록 QueryClientProvider가 필요하다.

2. useQuery

const { status, data, error, refetch } = useQuery(
  key,
  requestAPICallback,
  options
);
  • useQuery는 첫 번째 인자로 캐싱 key 배열을, 두 번째로 API요청 callback을 전달해서 호출한다.

  • 당연한 얘기지만 requestAPICallback은 Promise 객체를 반환하는 함수여야한다.

  • options에는 cacheTime 같은 옵션을 추가할 수 있다.




recoil

  • 클라이언트에 전역 상태, 예를 들어 현재 로그인 유뮤, alert, location 상태 등을 다루는데 사용했다.

선택이유

  • 컴포넌트에서 좁은 범위로 사용하는 상태는 모두 useState를 사용했지만 위에서 언급한 상태들은 어플리케이션 전반에서 사용 되는 상태들이다

  • 따라서 root에서 관리할 필요가 있으며 이 경우 prop drilling 이슈가 발생하기에 전역 관리가 필요했다.

  • contextAPI를 사용할 수도 있었지만 보일러 플레이트를 직접 구성해야하고, 주로 useReducer와 함께 사용되는 패턴이지만 다루는 상태가 단순한 형태이기에 어울리지 않았다.

  • 따라서 쉽게 적용 가능하고, 사용하기도 간편한 recoil을 선택했다.


사용하면서 느낀 점

  • 사실 제대로 사용한게 아니라서 이렇다 평할 수는 없다.. selector라던가, 특히 비동기 처리에 사용하지 않아서 반쪽짜리 사용인 느낌?

  • 다음 프로젝트에서 기회가 된다면 recoil을 사용한 비동기 데이터 처리를 해봐야겠다.


사용법

1. RecoilRoot

/// index.tsx

import { RecoilRoot } from "recoil";

ReactDOM.render(
  <RecoilRoot>
    <App />
  </RecoilRoot>,
  document.getElementById("app")
);
  • RecoilRoot로 전역 상태를 공유할 범위를 지정한다. 우리팀의 경우 어플리케이션 전체에서 공유할 상태만 관리할 것이기에 App 컴포넌트를 감쌌다.

  • RecoilRoot는 중복 사용할 수 있으며, 이 경우 부모방향으로 가장 가까운 RecoilRoot가 기준이 된다.

2. RecoilRoot

import { atom } from "recoil";

export const alertState = atom({
  key: "isOpen",
  default: {
    isOpened: false,
    message: "",
  },
});
  • atom, selector 등을 이용해서 상태를 선언한다.

  • 참고로 atom, selector는 key를 반환한다. 그렇기에 위의 예시에서 alertState를 export하고 사용하는 곳에서 키로 사용하는 것!

3. useRecoilState / useRecoilValue / useSetRecoilState

import { alertState } from "@/store/state";
import { useRecoilState } from "recoil";

const Alert = () => {
  const [alert, setAlert] = useRecoilState(alertState);
  return ..
}
  • 사용할 model의 key를 가져오고, recoil hook 중을 용도에 맞게 가져와서 사용한다

  • useRecoilState : state와 setState를 모두 사용한다. 컴포넌트는 해당 상태가 변경되었을 때 리렌더링 대상이 된다.

  • useRecoilValue : state만 사용한다. 이 경우에도 컴포넌트는 상태가 변했을 때 리렌더링 대상이 된다.

  • useSetRecoilState : setState만 사용한다. 컴포넌트는 해당 상태가 변경되었을 때 리렌더링 대상이 되지 않는다.

profile
기억보단 기록을 / TIL 전용 => https://velog.io/@jjuny546

0개의 댓글