본문 바로가기
공부/프로그래밍

[axios, react, redux] 서버통신 시 로딩바 띄우기

by demonic_ 2021. 1. 13.
반응형

axios를 이용해 Backend와 API통신하면서 중간에 로딩바가 필요했다.

그런데 이걸 매번 호출마다 넣을 순 없으니 axios의 interceptors 옵션을 이용해 넣기로 했다.

 

우선 Loading 화면을 우선 만든다.

해당 로딩화면은 항상 최상위어야 한다. 다음처럼 컴포넌트를 만들어 Root에 추가했다.

 

로딩이미지는 material-ui에 있는 것을 사용했다.

https://material-ui.com/api/circular-progress/

 

CircularProgress API - Material-UI

The API documentation of the CircularProgress React component. Learn more about the props and the CSS customization points.

material-ui.com

 

LoadingComponent.tsx 파일 생성

import {useSelector} from "react-redux";
import CircularProgress from "@material-ui/core/CircularProgress";
import {RootState} from "../reducers";

const LoadingComponent = () => {
    const {loading} = useSelector((state:RootState) => state.global);


    return (
        <>
            {loading === false ? "" :
                <>
                    <div className={"globalLoadingBackground"}>
                    </div>
                    <div className={"globalLoadingIcon"}>
                        <div className={"iconArea"}>
                            <CircularProgress />
                        </div>
                    </div>
                </>
            }
        </>
    )
}

export default LoadingComponent

 

 

그럼 이 컴포넌트를 _app.js 파일에 추가한다

<LoadingComponent /> 추가

import LoadingComponent from "../container/LoadingComponent";

...
    return (
            <Provider store={store}>
                <div>
                    <CookiesProvider>
                        // 이부분에 추가
                        <LoadingComponent />  
                        <AppLayout>
                            <Component {...pageProps}/>
                        </AppLayout>
                    </CookiesProvider>
                </div>
            </Provider>
    )
...

 

 

 

 

 

 

redux에 로딩관련 action 을 등록한다.

(여기선 immer를 적용했기에 draft 명령어를 사용했다.)

import produce from "immer";

export const initialState = {
    loading: false
}

export const GLOBAL_LOADING = "LOADING"
export const GLOBAL_LOADED = "LOADED"

const reducer = (state = initialState, action) => {
    return produce(state, (draft) => {
        switch (action.type) {
            case GLOBAL_LOADING: {
                draft.loading = true
                break
            }
            case GLOBAL_LOADED: {
                draft.loading = false
                break;
            }
        }
    })
}

 

위 설정을 완료한 후 요청할때마다 action을 호출하면 된다.

 

그럼 이제 axios를 설정하자. API를 요청할 때, 로딩바를 호출, 끄면 된다.

내 경우 Layout을 구성하는 페이지에다 담았는데 (_app.js 안에 컴포넌트를 감싸는 부모 컴포넌트) 필요에 따라 다른곳에 넣어도 된다.

 

AppLayout.js

const AppLayout = () => {
...
    const dispatch = useDispatch();
    useEffect(() => {
        axios.interceptors.request.use(function (config) {
            // 로딩 호출
            dispatch({
                type: GLOBAL_LOADING
            })
            return config;
        }, function (error) {
            // 실패 시 로딩창 종료
            dispatch({
                type: GLOBAL_LOADED
            })
            return Promise.reject(error);
        })
        axios.interceptors.response.use((config) => {
            // 완료 시 로딩창 종료
            dispatch({
                type: GLOBAL_LOADED
            })
            return config;
        },(error) => {
            // 실패 시 로딩창 종료
            dispatch({
                type: GLOBAL_LOADED
            })
            return Promise.reject(error)
        })
    }, [])
...
}

 

axios 옵션 중 interceptors 를 보면 request와 response 가 있는데, request는 호출할 때, response는 받을때를 의미한다. 각각을 1번만 설정하면 앞으로 API를 호출할때마다 해당 로딩 action을 호출한다.

 

 

 

끝.

 

반응형

댓글