본문 바로가기
FrontEnd/React.js

[React.js] to-do-app, fetch => Axios 마이그레이션 과정

by Chaedie 2022. 10. 20.
728x90

Axios 마이그레이션

다른 사람이 만든 to-do-app을 보니?!

react.js로 만든 to-do-app을 TypeScript로 성공적으로(?) 마이그레이션했다. 무수한 에러를 이겨내고 마침내 정상 동작하는 App을 보는 기쁨을 충분히 만끽하며 즐거워 하고 있었다.

그러나… 다른 사람들의 코드를 보다 보니 통신 부분이 너무나도 깔끔한것이었다. 비결이 무엇일지 살펴보니 깔끔함의 비결은 이것도 저것도 아닌 AXIOS 라이브러리를 사용하고 있기 때문이었다~!!!!!

나도 한번 써볼까 ⁉️

사실 이전에 프로그래머스에서 과제 테스트를 할 때 “Axios가 아닌 기본 fetch 함수를 사용해주세요.” 라는 문구를 본적이 있었다. 그래서 “axios를 사용하지 말고 기본 fetch만으로 통신을 잘 해내자!” 라는 생각이 있었다. 하지만 이제 fetch문은 익숙해졌고, 조금 더 좋은 코드를 위해 axios를 받아 들일 때가 되지 않았나? 생각한다.

Axios 설치 및 시작!

// npm 사용 시!
$ npm install axios

// yarn 사용 시!
$ yarn add axios 

Axios 사용법

axios.get(url).then(data => console.log(data))
axios.post(url, bodyObject).then(data => console.log(data))
  • Axios는 기본적으로 json 타입으로 통신이 되는게 디폴트이며, Request Body는 그냥 객체 리터럴으로 정보를 담아 보내면 된다. 또한 response.json() 과정이 없어도 된다.
  • 현재로썬 알아낸 것이 여기까지 밖에 없다. 더 사용해보면서 정리 해 볼 예정이다.

Post SignIn, SignUp (AxiosResponse<T,D> 의 타입 지정)

fetch의 경우 data가 any로 지정이 되어 있어서 data에 어떤값이 들어와도 상관없다고 되어 있다. 그러나 Axios의 경우에 data가 AxiosResponse<T,D>로 지정 되어 있다. 아직은 모르지만 아마도 axios.config 같은 곳에서 타입 지정을 해주는 것 같다.

axios.post(authUrl, { email, password }).then((data: any) => {
      if (data.access_token) {
        token = data?.access_token;
        localStorage.setItem('token', token);
        navigate('/todo');
      } else if (data.statusCode === 400) {
        alert(data.message);
        navigate('/');
      } else {
        alert('로그인 정보를 확인해주세요.');
        navigate('/');
      }
    });
  • 👀 fetch에서 axios.post로 바꾸고 인자값만 변경했을 뿐인데, data의 타입이 맞지 않다고 떠서 data를 any로 지정해주었다. 이게 맞는게 아닐텐대… 아무래도 config 같은 곳에서 미리 지정해주던가, interface로 data타입들을 다 지정해주던가 해야 하는것 같은데… 일단은 any로 박고 넘어가기로 한다.
  • ✅ Axios 마이그레이션이 완료 된 후 보니까 이게 fetch문과 axios문의 response 구조가 달라서 그랬다. any박은 걸 없애주고, data를 res로 변경 res.data가 원래 의도했던 data 였었다.

Get Todo

// 기존 코드
useEffect(() => {
    if (token) {
      const fetchData = async () => {
        const data = await API.getTodoList(token);
        setTodoList([...data]);
      };
      fetchData();
    }
    if (!token) {
      navigate('/');
    }
  }, [token, navigate]);

//@API
getTodoList: async function (token: string | null) {
  const uri = `${baseUrl}/todos`;
  const options = {
    headers: { Authorization: `Bearer ${token}` },
  };

  const response = await request(uri, options);
  return response;
},
  • 위 코드를 axios를 이용해서 다시 만들어야 한다.
  • Axios 문서를 보면 get의 경우 url만 넣으면 간단하게 끝이다. Let’s Try !!
axios.get(`${baseUrl}/todos`).then((data: any) => setTodoList([...data]));
  • 이게 맞는지 모르겠는데 일단 그냥 Try Catch 같은거 다 빼면 이렇게 한 줄 나온다.
  • 너무 .. 간단 .. 하니까 더 이상하네요… 흠 ..? 다시 살펴 봐야겠습니다.
  • 일단 원래 코드랑 비슷하게 try-catch로 묶고, async-await 도 좀 넣어야겠습니다.

resonse의 생김새가 다르다니?!

  • 위가 fetch문의 response이고, 아래는 axios의 response입니다.
  • fetch문을 사용할 당시엔 try-catch문 안에 if(response.ok) return; 을 넣었었는데, ok가 undefined가 뜬다해서 확인해보니 axios의 response에는 ok라는 키값이 없었다. 그럼 status로만 해결해야 하는건데… status가 꼭 200일 때만 성공 한건 아닐것 같은데… 좀 더 자세히 알아 봐야겠다.
  • 근데 그러고 보면 저번에 delete성공 하고 나서 204로 나왔떤거 보면 200일 때만 response에 데이터가 담겨 오는게 아닐까? 하는 추론이 생기는데, 이는 한번 확인해봐야 할것 같다.
  • Get일 땐 200, post일 떈 201이, delete일 땐 204가 성공 status code이다.
import { http } from './api';

export const todoAPI = {
  getTodoList: async function (token: string | null) {
    try {
      const res = await http.get('/todos', {
        headers: { Authorization: `Bearer ${token}` },
      });
      if (res.status === 200) {
        return res.data;
      }
      throw new Error('API통신 실패');
    } catch (error: any) {
      console.error(error.message);
    }
  },

  postTodo: async (token: string | null, bodyData: { todo: string }) => {
    try {
      const res = await http.post('/todos', bodyData, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });
      if (res.status === 201) {
        return res.data;
      }
      throw new Error('API통신 실패');
    } catch (error: any) {
      console.error(error.message);
    }
  },

  putTodo: async (token: string | null, bodyData: { id: number; todo: string; isCompleted: boolean }) => {
    try {
      const res = await http.put(`todos/${bodyData?.id}`, bodyData, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });
      if (res.status === 200) {
        return res.data;
      }
      throw new Error('API통신 실패');
    } catch (error: any) {
      console.error(error.message);
    }
  },

  deleteTodo: async (token: string | null, bodyData: { id: number }) => {
    try {
      const res = await http.delete(`/todos/${bodyData?.id}`, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });
      if (res.status === 204) {
        return res.data;
      }
      throw new Error('API통신 실패');
    } catch (error: any) {
      console.error(error.message);
    }
  },
};

마치며…

  • 생각보다 금방 끝나서 적을게 많이 없었습니다.
  • Axios 장점 (개인적 느낌)
    • axios.create를 사용해서 default 값으로 options등을 설정할 수 있다는 건 편했습니다.
    • axios.get, axios.post 등 method별로 함수가 정의 되어 있어서 method를 적어주는 한 줄이 없어서 좋았습니다.
    • request body에 내용을 담을 때 JSON.stringify()해주지 않고, 그냥 객체 형태 그대로 던져 주어도 되서 편했습니다. 이는 기본적으로 axios에서 json 형태로 직렬화 (serialize) 해주어 던져주는 것으로 default 설정이 되어 있어서 그렇다고 합니다.
  • 단점
    • fetch문을 사용할 때는 utils에 request 함수를 작성 해두었고, 해당 함수에 try-catch문을 작성해 두어서 어디서나 에러처리가 될 수 있도록 만들수 있었습니다.
    • 하지만 axios를 사용하고, axios.create를 통해 인스턴스를 생성해서 axios.get, axios.post 등을 하다보니 각 메서드 마다 try-catch문을 별도로 지정해줘야 되는 문제가 생겼습니다.
    • response.ok가 없고 response.status 가 있어서 status 코드 별로 분기 처리를 해줘야 했습니다. 이건 좀 더 확실한 에러처리를 위해 당연히 해야할 것을 ok로 퉁쳤던걸 보완하게 된거라 단점은 아닐 수 있겠네요.
  • 느낀점
    • TypeScript 마이그레이션 과정에서도 느꼈듯이 문서를 보거나 블로그를 보거나 영상을 보는 것만으로는 머리에 안들어 오고, 휘발성이 굉장히 강했지만, 직접 이렇게 뭔갈 바꿔보면서 느낀점도 생기고, 장 단점도 분류해보고, 오해도 해보고 오해를 이해로 바꾸는 과정도 해보니 재미도 있고 머릿속에서 자연스레 정리되는 것도 있습니다.
    • 확실히 개발 공부는 직접 해보는게 짱인것 같아요.
    • 여기에 더해 이론적인 부분까지 살을 추가한다면 좋은 개발자가 될 수 있지 않을까? 기대가 됩니다. ㅎㅎ

댓글