테이블: 가상자산, 주식, 지수 등 자산의 재무 및 리스크 정보를 담은 목록
헤더: 사용자들이 원하는 종류의 자산을 찾을 수 있도록 Filter 기능을 제공
테이블과 헤더로 구성되어있는 자산 목록 페이지를 제작해보고자 합니다
Context는 부모 컴포넌트로부터 자식 컴포넌트로 전달되는 데이터의 흐름과는 상관없이 전역적인 데이터를 다룰 때 사용합니다. 전역 데이터를 Context에 저장한 후, 데이터가 필요한 컴포넌트에서 해당 데이터를 불러와 사용할 수 있습니다.
자산 목록 페이지에 Context Api를 활용하려는 이유는 세 가지 있습니다.
-> Type, Level, Weather, Location 등 모든 상태 변수를 페이지내에서
useState로 선언하면, 코드 가독성이 떨어질 가능성이 높습니다.
-> 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을 활용하였습니다.
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에서 선언한 상태변수를 원하는 페이지에서 활용할 수 있게 됐습니다.
자산 목록 페이지에서는 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}§=${state.sect}&weather=${state.weather}&search=${state.search}&priceOrder=${state.priceOrder}&lossOrder=${state.lossOrder}&priceChgOrder=${state.priceChgOrder}`,
fetcher
);
이제 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/