Context Api를 활용한 자산 목록 페이지 만들기

Molly·2023년 5월 22일
2

React, Next.js

목록 보기
1/7
post-thumbnail

자산 목록 페이지 만들기


테이블: 가상자산, 주식, 지수 등 자산의 재무 및 리스크 정보를 담은 목록

헤더: 사용자들이 원하는 종류의 자산을 찾을 수 있도록 Filter 기능을 제공

테이블과 헤더로 구성되어있는 자산 목록 페이지를 제작해보고자 합니다


Context Api를 활용한 전역 State 관리


Context는 부모 컴포넌트로부터 자식 컴포넌트로 전달되는 데이터의 흐름과는 상관없이 전역적인 데이터를 다룰 때 사용합니다. 전역 데이터를 Context에 저장한 후, 데이터가 필요한 컴포넌트에서 해당 데이터를 불러와 사용할 수 있습니다.

자산 목록 페이지에 Context Api를 활용하려는 이유는 세 가지 있습니다.

1. 페이지 내에서 관리해야 할 State가 많습니다.

-> Type, Level, Weather, Location 등 모든 상태 변수를 페이지내에서 
useState로 선언하면, 코드 가독성이 떨어질 가능성이 높습니다.

2. 모바일 전용 페이지를 따로 만들었고, 같은 상태 변수를 활용합니다.

-> Context를 활용하여, 전역으로 상태 변수를 관리하면 중복 코드를 방지할 수 있습니다. 

3. 뒤로가기로 페이지로 돌아왔을때, 상태 변수를 이전과 같이 유지해야합니다.

-> 사용자가 목록 페이지에서 자산을 클릭하여 상세정보를 확인한 뒤, 뒤로가기로
목록페이지로 돌아왔을 때, 이전의 상태를 유지하고 있어야 합니다.
전역으로 상태를 관리할 경우, 페이지내에서 관리하는 것 보다
이를 효과적으로 해결할 수 있습니다.

Context 생성하기


전역 데이터를 저장하기 위한 Context를 생성해보겠습니다. /src/contexts/ExploreStateContext.jsx 파일을 생성하고 다음과 같이 입력합니다.


import { createContext, useContext, useReducer } from 'react';

// 초기 상태
const initialState = {
  page: 1,
  limit: 20,
  type: 'All',
  riskLv: 'All',
  loc: 'All',
  sect: 'All',
  exchg: 'All',
  weather: 'All',
  search: '',
  currency:'KRW',
  priceOrder: 'neutral',
  lossOrder: 'neutral',
  priceChgOrder: 'neutral',
};

// Reducer 함수 정의
const reducer = (state, action) => {
  switch (action.type) {
    case 'SET_PAGE':
      return { ...state, page: action.payload };
    case 'SET_LIMIT':
      return { ...state, limit: action.payload };
    case 'SET_TYPE':
      return { ...state, type: action.payload };
    case 'SET_RISK_LV':
      return { ...state, riskLv: action.payload };
    case 'SET_LOC':
      return { ...state, loc: action.payload };
    case 'SET_SECT':
      return { ...state, sect: action.payload };
    case 'SET_EXCHG':
      return { ...state, exchg: action.payload };
    case 'SET_WEATHER':
      return { ...state, weather: action.payload };
    case 'SET_SEARCH':
      return { ...state, search: action.payload };
    case 'SET_CURRENCY':
      return { ...state, currency: action.payload };
    case 'SET_PRICE_ORDER':
      return { ...state, priceOrder: action.payload };
    case 'SET_LOSS_ORDER':
      return { ...state, lossOrder: action.payload };
    case 'SET_PRICE_CHG_ORDER':
      return { ...state, priceChgOrder: action.payload };
    case 'RESET_VALUE':
      return initialState;
    default:
      return state;
  }
};


const ExploreStateContext = createContext(initialState);

// Explore 상태 훅
export const useExploreState = () => useContext(ExploreStateContext);

// ExploreStateProvider 컴포넌트
export const ExploreStateProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <ExploreStateContext.Provider value={{state, dispatch }}>
      {children}
    </ExploreStateContext.Provider>
  );
};

React에서 Context를 생성하기 위해서는 createContext를 사용해야 합니다.

Context는 _app.tsx<Component/>를 감싸 전역으로 상태 변수를 관리하는
React 컴포넌트이므로, 컴포넌트안에서 상태를 변경하기 위해 useReducer 혹은 useState를 사용해야 합니다. 이번 실습에서는 useReducer을 활용하였습니다.



App 컴포넌트에 Provider 제공하기


export한 ExploreStateProvider_app.tsx에 import하여 root 컴포넌트들을 감싸게 해줍니다.

    <ExploreStateProvider>
    <Layout {...pageProps}>
      <Component {...pageProps}  />
      <ToastContainer/>
      <Tooltip id="riskLevel" style={{zIndex:289, fontSize:"small" ,fontWeight:"bold"}}/>
      <Tooltip id="indexExplain" style={{zIndex:289, fontSize:"small" ,fontWeight:"bold"}}/>
      <Tooltip id="traitExplain"  style={{zIndex:289, fontSize:"small" ,fontWeight:"bold",  backgroundColor:"transparent" }}>
      <Image src={router.locale=="ko" ? '/images/traits/explanationKr.svg' : '/images/traits/explanation.svg'} alt="" width={350} height={150}/>
      </Tooltip>
    </Layout>
    </ExploreStateProvider>

이제 Context에서 선언한 상태변수를 원하는 페이지에서 활용할 수 있게 됐습니다.



useSWR을 활용한 리얼타임 상태관리


자산 목록 페이지에서는 swr을 통해 DB에서 자산 데이터를 가져오고,
상태 변수를 query parameter로 활용함으로써, 사용자가 필터링한
정보들을 반환할 수 있습니다.

//fetcher은 페이지 컴포넌트 밖에 선언해야합니다.
const fetcher = (url:string) => axios.get(url).then((res) => res.data)

//useSWR은 ClientSide로 data를 fetching하며, 
//query param이 바뀔때마다 실시간으로 자산 목록이 변동됩니다.
const { data, isValidating } = useSWR(
  `/api/allassets?&page=${state.page}&limit=${state.limit}&type=${state.type}&riskLv=${state.riskLv}&loc=${state.loc}&exchg=${state.exchg}&sect=${state.sect}&weather=${state.weather}&search=${state.search}&priceOrder=${state.priceOrder}&lossOrder=${state.lossOrder}&priceChgOrder=${state.priceChgOrder}`,
  fetcher
);


Dispatch로 전역 상태 관리하기


이제 dispatch 함수를 도입하여, 상태를 직접적으로 관리할 수 있는 함수를 작성합니다.


const handleChange = useCallback(
  (type: string) => (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
    const value = e.target.value;
    if (type === 'SET_SEARCH' && value.length > 20) {
      return;
    }
    dispatch({ type, payload: value });
  },
  [dispatch]
);

const handleReset = () => {
  dispatch({ type: 'RESET_VALUE' });
  router.push('/explore');
};

handleChange 함수는 search Input과 Select 필터링을 담당하고
handleReset 함수는 상태 변수를 모두 초기상태로 초기화합니다.



결과


필터링 기능이 정상적으로 작동하며,전역으로 상태가 관리되고 있습니다.
미국 주식 중에, 리스크 수준이 안전한 종목들만 성공적으로 확인할 수 있습니다.

보다 자세한 코드 및 의문점에 대해서는 자유롭게 댓글을 달아주시면 정말 감사드리겠습니다.




참고자료

https://kyounghwan01.github.io/blog/React/react-context-api/#context-api%E1%84%85%E1%85%A1%E1%86%AB
https://dev-yakuza.posstree.com/ko/react/context-api/

profile
Next.js, Typescript, Rust

0개의 댓글