평범한 개발자 행복한 가족, 패밀리그램

개발/React

axios 를 이용한 REST API 호출 및 취소

패밀리그램 2021. 9. 26. 15:52
Project 에 axios dependencies 추가 
yarn add axios

사용하는 모듈에서 바로 axios 를 사용하여 호출하는 것 보다는 특정 Service 모듈을 만들어 호출하는 것이 API error, retry 처리에 용이함

import axios, { AxiosRequestConfig, CancelTokenSource } from "axios";

class Service {
    get(url: string, params?: { [key: string]: string | number }): Promise<any> {
		return axiosApi.get(url, {
            params: params,
            cancelToken: cancelSource.token,
        });
    }
}
React 생명주기 또는 sideEffect 생명주기를 위해 API cancel 도 추가
import axios, { AxiosRequestConfig, CancelTokenSource } from "axios";

const defaultConfig: AxiosRequestConfig = {};

const axiosApi = axios.create(defaultConfig);

type Executor<T> = (
    resolve: (value: T | PromiseLike<T>) => void,
    reject: (reason?: any) => void
) => void;

export class ApiPromise<T> extends Promise<T> {
    private $source: CancelTokenSource;
    constructor(executor: Executor<T>, cancelSource: CancelTokenSource) {
        super(executor);
        this.$source = cancelSource;
    }

    public cancel(msg?: string) {
        this.$source.cancel(msg);
    }

    get source(): CancelTokenSource {
        return this.$source;
    }
}

class Service {
    get(url: string, params?: { [key: string]: string | number }): ApiPromise<any> {
        const cancelSource = axios.CancelToken.source();
        const api = axiosApi.get(url, {
            params: params,
            cancelToken: cancelSource.token,
        });
        return new ApiPromise((resolve, reject) => {
            api.then((resp) => {
                resolve(resp);
            }).catch((error) => {
                reject(error);
            });
        }, cancelSource);
    }
}
Promise의 executor 타입을 선언후 ApiPromise class 정의

class constructor 두번째 인자로 axios의 cancelSource 를 받는다

type Executor<T> = (
    resolve: (value: T | PromiseLike<T>) => void,
    reject: (reason?: any) => void
) => void;

export class ApiPromise<T> extends Promise<T> {
    private $source: CancelTokenSource;
    constructor(executor: Executor<T>, cancelSource: CancelTokenSource) {
        super(executor);
        this.$source = cancelSource;
    }

    public cancel(msg?: string) {
        this.$source.cancel(msg);
    }

    get source(): CancelTokenSource {
        return this.$source;
    }
}
기존 Service class 코드에 ApiPromise 로 wappring 하여 전달
class Service {
    get(url: string, params?: { [key: string]: string | number }): ApiPromise<any> {
        const cancelSource = axios.CancelToken.source();
        const api = axiosApi.get(url, {
            params: params,
            cancelToken: cancelSource.token,
        });
        return new ApiPromise((resolve, reject) => {
            api.then((resp) => {
                resolve(resp);
            }).catch((error) => {
                reject(error);
            });
        }, cancelSource);
    }
}
이제 사용
const [user, setUser] = useState<User | null | undefined>();
 
useEffect(() => {
        const api = APIService.get(`user/${id}`);
        api.then((user) => {
            setUser(user);
        }).catch((error) => {
            setUser(null);
        });
        return () => {
            api.cancel();
        };
    }, [id]);

id 가 없데이트 될 때마다 clear up 에서 api cancel 해줌

반응형