Never give up

React - Axios common settings 본문

WEB

React - Axios common settings

대기만성 개발자 2022. 12. 21. 16:17
반응형

axios는 fetch에 비해 여러가지 장점이 있습니다

 

  1. response timeout
  2. 구형 브라우저 지원
  3. json 변환과정 생략 가능
  4. 기타 등등

그래서 아마 대부분의 개발자분들이 사용하실텐데

 

공통적으로 셋팅하면 좋은 부분들을 예제와 함께 준비해봤습니다

 

src/service/api.ts

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

export default class Api {
    private instance: AxiosInstance;

    constructor(baseURL = '', timeout = 30000) {
        this.instance = axios.create({
            baseURL,
            timeout,
        });

        this.instance.interceptors.request.use(
            (config) => {
                // const ssn = infoUtil.getSsn()

                // if (ssn !== null) {
                //     config.headers = {
                //         ssn
                //     }
                // }

                return config;
            },
            (error) => {
                console.log({ error });
                return Promise.reject(error);
            },
        );

        this.instance.interceptors.response.use(
            (response) => {
                console.log({ response });
                return response;
            },
            (error) => {
                console.log({ error });
                return new Promise((res) => {
                    res({
                        data: {
                            result: {
                                msg: error.message,
                                code: -1,
                            },
                        },
                    });
                });
            },
        );
    }

    protected async get<T>(url: string, params?: any) {
        return await this.instance.get<T>(url, { params });
    }

    protected async post<T>(url: string, data: any, config?: AxiosRequestConfig) {
        return await this.instance.post<T>(url, data, config);
    }
}

1. constructor 부분에서 axios create를 해주면서 baseUrl, timeout을 셋팅해서

 

메인 서버 혹은 테스트 서버 url을 넣어주고 필요할 때 교체를 합니다

 

그리고 reponse가 너무 길어지는 경우 timeout을 설정해주도록 합니다

 

2. interceptors 설정에서 request 부분에 보면 주석으로 해놓은 부분이 있는데

 

보통 jwt을 클라이언트(브라우저)에 저장해놨다가 api콜 할때마다 가져다가 쓸 수있는 구조로 만들어줍니다

(해당 예제에서 이 부분은 구현을 안해서 일단 이렇게 사용하면 된다 정도로 넘어가주시면 될거 같습니다)

 

response 부분에서는 response에 console찍는 부분을 넣어놨는데

 

필자가 테스트 할 때 사용하는 용도로 굳이 사용할 필요는 없습니다

 

그다음에 response에 error가 발생할 때 별도의 처리를 해줬는데

 

이 부분은 에러 발생시 메시지를 toast로 보여주는 용도로 처리해놨습니다

 

이런식으로 말이죠

< status code 4xx, 5xx인 경우&nbsp; >

 

< 200이지만 파라미터 부족으로 보낼 경우 >

 

3. getpost의 url과 params 혹은 body, config등 셋팅을 간단하게 처리하기 위해

 

메소드로 만들어서 필요한 부분에서 가져다 쓰기 편하게 만들어 놨습니다

 

추가로 return type을 명확하게 하기위해 type을 추가 해봤습니다

 

다음으로는 위에 구현해놓은 공통 api 클래스를 사용하는 부분입니다

 

src/service/api/testApi.ts

import util from "src/util/util";
import Api from "../api";
import { ResultModel } from "src/model/ResultModel";
import { TestModel } from "src/model/TestModel";

export default class TestApi extends Api {
    async getTest(params: any) {
        const res = await super.get<ResultModel<TestModel>>("/api/test/param", params);

        const rm = res.data;

        if (util.checkResult(rm)) {
            return rm.data;
        } else {
            util.toast({ msg: util.checkMsg(rm) });
        }
    }

    async postTest(model: any) {
        const res = await super.post<ResultModel<TestModel>>("/api/test/send/data", model);

        const rm = res.data;

        if (util.checkResult(rm)) {
            return rm.data;
        } else {
            util.toast({ msg: util.checkMsg(rm) });
        }
    }

    async formDataTest(model: any) {
        const formData = new FormData();

        if (model.a !== undefined) {

            formData.append('a', model.a);
        }
        if (model.b !== undefined) {
            formData.append('b', model.b);
        }

        const res = await super.post<ResultModel<TestModel>>("/api/test/send/formdata", formData, { headers: { "Content-Type": "multipart/form-data" }, });

        const rm = res.data;

        if (util.checkResult(rm)) {
            return rm.data;
        } else {
            util.toast({ msg: util.checkMsg(rm) });
        }
    }
}

위에 만들어놓은 api를 상속받아서 super(api class)의 get과 post를 사용해줍니다

 

사용한 api는 3가지로 get, post(일반), post(formdata) 입니다

 

각 메소드에 parameter부분은 fail test도 포함하고 있어서, 일단 any로 뒀는데

 

실제로 사용하실 때는 잘 넣어주시면 됩니다..

 

해당 클래스를 사용하는 부분을 보면

import React, { useState } from 'react';
import CommonBtn from 'src/components/common_btn/common_btn';
import TestApi from 'src/service/api/test_api';
import styles from './index.module.css';

const Main = () => {
    const [result, setResult] = useState();

    const api = new TestApi();

    const testBtn1 = [
        {
            title: 'get',
            bg: '#ef5350',
            onClick: async (failTest = false) => {
                let params;

                if (failTest) {
                    params = { b: 1 };
                } else {
                    params = { a: 1 };
                }

                const res = await api.getTest(params);

                if (res !== undefined) {
                    setResult(res);
                } else if (result !== undefined) {
                    setResult(undefined);
                }
            },
        },
        {
            title: 'post/data',
            bg: '#42a5f5',
            onClick: async (failTest = false) => {
                let model;

                if (failTest) {
                    model = { a: 1 };
                } else {
                    model = {
                        a: 1, b: 2
                    };
                }

                const res = await api.postTest(model);

                if (res !== undefined) {
                    setResult(res);
                } else if (result !== undefined) {
                    setResult(undefined);
                }
            },
        },
        {
            title: 'post/formData',
            bg: '#66bb6a',
            onClick: async (failTest = false) => {
                let model;

                if (failTest) {
                    model = { a: 3 };
                } else {
                    model = {
                        a: 3, b: 4
                    };
                }

                const res = await api.formDataTest(model);

                if (res !== undefined) {
                    setResult(res);
                } else if (result !== undefined) {
                    setResult(undefined);
                }
            }
        },
    ];

    return (
        <div className={styles.div_main}>
            <p>정상 테스트</p>
            <div>
                {testBtn1.map((e, i) => (<CommonBtn key={`btn1-${i}`} backgroundColor={e.bg} margin=
                    "10px" onClick={() => e.onClick()}>
                    {e.title}
                </CommonBtn>))}
            </div>
            <p>비정상 테스트</p>
            <div>
                {testBtn1.map((e, i) => (<CommonBtn key={`btn2-${i}`} backgroundColor={e.bg} margin=
                    "10px" onClick={() => e.onClick(true)}>
                    {e.title}
                </CommonBtn>))}
            </div>
            <div>
                {result !== undefined && JSON.stringify(result)}
            </div>
        </div>
    );
};

export default Main;

testApi를 인스턴스화 하고

 

onClick이벤트에서 가져와서 사용하도록 만들어 봤습니다

 

1. get에 params을 셋팅

url: /test/param , params: {a : 1}

< get 테스트 결과 >

다음과 같이 queryparam에 a가 셋팅되어 넘어가는것을 확인할 수 있습니다

 

2. post에 body를 셋팅해서 보낸 api

url: /test/send/data, model: {a: 1, b: 2}

< post 에 body 셋팅 >

payload에 a,b가 잘 셋팅된것을 확인할 수 있습니다

 

3. post에 formdata를 셋팅해서 보낸 api

url: /test/send/formdata, model: {a: 3, b: 4}

< post에 formdata 셋팅 >

마찬가지로 formdata에 a, b가 잘 셋팅된것을 확인할 수 있습니다

 

이건 제가 만든 예제 기준으로 셋팅한것으로 일부 처리 방법이 바뀔 여지가 있습니다

 

특히 response error부분은 서버에서 주는 데이터를 잘 확인하고 거기에 맞게 잘(?) 처리해주면 됩니다

 

(전체 소스 링크: https://github.com/devmemory/axios_example)

 

추가로 이전에 왜 이렇게 사용하는게 나은가? 라는 질문을 받았습니다

 

그래서 간단히 정리해보자면

 

1. 관리 포인트 제거

axios를 여러군데에서 셋팅해서 쓰다보면 특정 셋팅을 빼먹는 경우도 생길 수도 있죠

 

당연히 관리포인트를 여러군데로 늘리는게 좋은게 아니죠

 

2, 콜하는 부분에서 baseURL, timeout등을 셋팅 가능

특정 url로 요청을 하게 될 때 인스턴스화 하는 부분만 수정하면 간단하게 처리 가능합니다

const api = new TestApi('특정url',50000)

 

3. 같은 pathname을 사용하는것끼리 구분 관리 가능

개발 하다보면 많은 api를 관리하게 될텐데 하나하나 다 찾아다니면서 하기에는 너무 피곤합니다

 

각 api 파일을 생성해서 내부에서 관리하면 조금 더 효과적으로 관리할 수 있습니다

// /api/userApi.tsx
api/user/login
api/user/register

// /api/taskApi.tsx
api/task/add
api/task/edit

 

반응형

'WEB' 카테고리의 다른 글

React - image resize example  (0) 2023.07.16
React, Node - SSE example  (0) 2023.03.04
React - html to doc (feat.quill2)  (0) 2022.11.15
React - Drag & Drop file  (0) 2022.08.20
React - Social login(Kakao, Naver , Facebook, Google)  (18) 2022.07.16
Comments