React | Axios란? (feat. Fetch API) — Koras02코딩웹

notion imagenotion image
notion imagenotion image
notion imagenotion image
동일한 기능을 수행하는 코드이며, 간단하게 작성된 코드입니다.
위코드의 차이점을 비교해보면
  • Fetch()는 body 프로퍼티를 사용하고, axios는 data 프로퍼티를 사용합니다.
  • Fetch의 url이 Fetch()함수의 인자로 들어가고, axios에서는 url이 option객체로 들어갑니다.
  • Fetch에서 body부분은 stringify()로 되어집니다.
이처럼
axios는 HTTP 통신간에 요구사항을 Compact한 패키지로써 사용하기 쉽게 구성되어 있다.
위와 같은 내용들을 요약하면, 우리가 axios를 왜 배워야하고 써야하는 이유를 알게 될것이고, 이번에는 axios에 대해 알아보도록 합시다.
Axios는 브라우저, Node.js를 위해서 만들어진 Promise API를 활요하는 HTTP 비동기 통신 라이브러리 입니다.
(백엔드와 프론드엔드간에 통신을 위해서 만들어진 AJAX도 더불어 사용하기도 합니다.)
  • Axios는 운영환경에 따라서 브라우저간 XMLHttpRequest 객체 또는 Node.js의 HTTP API를 사용한다.
  • Promise(ES6) API를 사용
  • 요청(Request) 응답 (reply)을 JSON 형태로 자동 변경
  • Axios 다운로드
  • HTTP Methods (GET,POST,DELETE,PUT)
  • Axios 사용해보기
yarn add axios & npm i axios // 생성한 프로젝트 상단에 import로 추가 import axios from "axios"
클아이언트가 웹서버에게 사용자 요청의 목적/종류를 알려주는 수단
이 Method중에서 Axios로 통신하면서 가장 많이 사용되는 메소드들을 정리해보았습니다.
문법 코드
axios.get(url,[,config])
Q) Get 방식은 데이터를 받아오는 방식인데, 로그인 구현할때도 Get을 사용하는가?
GET 방식으로 로그인을 구현했을때 웹 사이트 주소창의 형태를 보면 이러한 형태가 나옴
www.server.com/login?id=Jung&pw=12345 // 실제로 없는 서버이고 예로 추가한 사이트
웹 사이트 뒤에 쿼리스트링이 붙여진 것을 확인할 수 이싿.
GET방식은 서버에서 어떤 데이터를 가져와서 보여줄것인가를 정하는 용도로 쓴다.
주소에 있는 쿼리스트링을 활용해 정보를 전달하는 것이고 GET 메서드는 값이나 상태등을 직접 바꿀수 없다.
// 예로 만든 axios 구조 import axios from "axios" axios.get('https://localhost:3000/login/user') .then((Response)=>{console.log(Response.data)}) .catch((Error)=>{console.log(Error)})
[ { id: 1, pw: '12345', name: 'jungho' }, { id: 2, pw: '12345', name: 'sungJun' }, { id: 3, pw: '12345', name: 'hyunJi' }, ]
응답은 json 형태로 넘어온다.
문법
axios.post("url주소",{ data객체 },[,config])
POST 메서드의 두 번째 인자는 본문으로 보낼 데이터를 설정한 객체 리터럴을 전달한다.
Q) POST 방식은 데이터를 받아오는 방식인데, 로그인 구현할때도 Get을 사용하는가?
로그인, 회원가입 등 사용자가 생성한 파일을 서버에다가 업로드할때 사용한다.
Post를 사용하면 주소창에 쿼리스트링이 남지 않고 GET보다 안전하다.
예제 코드
axios.post( 'url', { contact: 'JungHo', email: 'wjdgh0727@gmail.com' }, { headers: { 'Content-type': 'application/json', 'Accept': 'application/json' } } ) .then((response) => {console.log(response.data); }) .catch((response) => {console.log('Error!') });
문법
axios.delete(url,[,config]);
Delete메서드는 HTML Form태그에서 기본적으로 지원되는 HTTP 메서드가 아니다.
Delete메서드는 서버에 있는 데이터베이스의 내용을 삭제하는 것을 주 목적으로 하기에 두 번째 인자를 아예 전달하지 않는다.
예제 코드
axios.create('/thisisExample/list/30").then(function(response){ console.log(response); }).catch(function(ex) { throw new Error(ex) }
문법
axios.put(url[, data[, config]])
PUT 메서드는 HTML Form 태그에 기본적으로 지원하는 HTTP 메서드는 아니다.
PUT 메서드는 서버에 있는 데이터베이스의 내용을 변경하는 것을 주 목적으로 한다.
import React from "react"; import Users from "./Users"; function App() { return <Users/>; } export default App;
App Component는 단순히 Users라는 Component를 호출한다.
그리고 User.js는 useAsync라는 커스텀 hook을 사용하는데 이것부터 구현해보겠다. useReducer를 이용해서 구현
먼저 reducer를 구현합니다.
import React, { useReducer, useEffect, useCallback} from 'react' function reducer(state,action) { switch(action.type) { case 'LOADING': return { loading: true, data:null, error: null, }; case 'SUCCESS': return { loading: false, data: action.data, error: null, }; case 'ERROR': return { loading: false, data: null, error: action.error, }; default: throw new Error(`Unhandled action type: ${action.type}`); } }
  • LOADING은 현재 API로 부터 데이터를 읽어들이고 있을때를 말하고 화면에 로딩 중... 이라고 출력됩니다.
  • SUCCESS는 API로 데이터를 읽어들이고, 받아오는데 성공한다면 dispatch 를 통해 호출됩니다.
  • ERROR의 경우 API로 부터 데이터를 수집하는데 실패한다면 호출됩니다.
export function useAsync(callback, deps = [], skip = false) { const [state, dispatch] = useReducer(reducer, { loading: false, data: null, error: null, }); const fetchData = useCallback(async () => { dispatch({ type: 'LOADING' }); try { const data = await callback(); dispatch({ type: 'SUCCESS', data }); } catch (e) { dispatch({ type: 'ERROR', error: e }); } }, [callback]); useEffect(() => { if (skip) return; fetchData(); //eslint-disable-next-line }, deps); return [state, fetchData]; }
먼저 useReducer 부분을 설명드리자면
const [state, dispatch] = useReducer(reducer, { loading: false, data: null, error: null, });
state의 초기값으로 객체를 넣어주고, loading은 false 나머지 data,error는 null로 초기화 하고 만들어준 reducer 함수에 넣어줍니다.
const fetchData = useCallback(async () => { dispatch({ type: 'LOADING' }); try { const data = await callback(); dispatch({ type: 'SUCCESS', data }); } catch (e) { dispatch({ type: 'ERROR', error: e }); } }, [callback]);
async와 await가 사용되었는데, async함수에서 await가 실행되면, 그것이 끝날 때까지는 다른것들이 들어오지 못함니다.
여기서 callback함수는 useAsnycHook의 매게변수로 들어있는데, 이게 API와 통신하는 부분이며, Error가 발생하지 않는다
하면 무사히 const data 부분에 데이터를 받아오고, 그걸 dispatch를 통해, 객체 형태로 값을 받아옵니다.
useEffect(() => { if (skip) return; fetchData(); //eslint-disable-next-line }, deps);
처음 부터 웹페이지를 킬때 바로 회원정보를 불러오는 것이 아니고 버튼을 통해서 불러오도록 하고 싶은데, skip이 true면 위의
fetchData를 실행하지 않고 false에서만 실행합니다.
이떄 deps에 밑줄이 그려지면서 뭐가 잘못되었다고 나오는데 위에
// eslint-disable-next-line을 넣어주면 됩니다.
useEffect도 아직 다루지 못했는데 이글이 끝나면 다뤄볼 예정
전부 완료 되었다면 마지막으로 [state, fetchData]를 반환해줍니다.
다음으로는 Users.js 파일을 생성합니다.
import React, {useState} from 'react' import axios from 'axios'; import useAsync from './useAsync'; import User from "./User"; async function getUsers() { const response = await axios.get( 'https://jsonplaceholder.typicode.com/users', ); return response.data; } function Users() { const [state, refetch] = useAsync(getUsers, [], true); const [userId, setUserId] = useState(null); const { loading, data: users, error } = state; if (loading) return <div>로딩중...</div>; if (error) return <div>에러발생</div>; if (!users) return <buttononClick={refetch}>불러오기</button> return ( <> <ul> {users.map(user => ( <likey={user.id}onclick={() => setUserId(user.id)}> {user.username} ({user.name}) </li> ))} </ul> <buttononClick={refetch}>다시 불러오기</button> {userId && <Userid={userId} />} </> ) } export default Users;
하나하나씩 설명해드리겠습니다.
async function getUsers() { const response = await axios.get( 'https://jsonplaceholder.typicode.com/users', ); return response.data; }
이 비동기 함수는 방금전, fetchData의
const data = await callback();
이 부분의 callback에 해당하는 부분
즉, getUsers를 통해서 API의 데이터를 받아오는 걸 알 수 있음
const [state,refetch] = useAsync(getUsers, [], true);
이렇게 호출하면, useAsyncHook에서 반환해주는 [state, fetchData] 가 [state, refetch] 에 들어가게 됩니다.
이말은, state에는 API와 통신되서 데이터가 들어가 있다는 말이고, refetch는 API와 통신을 수행하는 fetchData를 Users.js에서
호출할 수 있다는 말이 된다.
const [userId, setUserId] = useState(null);
이 부분은 단지 userState니 설명 생략
const { loading, data: users, error } = state;
이부분은 좀 특이한데, data: users 이렇게 해주면, state에 들어있는 data가 data로 들어가는게 아닌 users로 들어간다.
const x = {y: 1, z:[1,2,3]}; const { y, z:a} = x; console.log(y); // 1 console.log(z); // Error console.log(a); // [1,2,3]
오히려 z를 호출하면 Error가 발생하고 심지어 에러에 대한 이유는 z is not defined 가 출력됨
if (loading) return <div>로딩중 ...</div>; if (error) return <div>에러!!!</div>; if (!users) return <buttononClick={refetch}>불러오기</button>;
이 부분을 loading 이 true 일때, 실제로 API에서 정보를 받아오고 있을 타이밍으로 로딩중... 이라고 출력되며, error가 null이 아닐 때는 뭔가 문제가 발생한것이고, !user가 null인 것은 아직 버튼이 만들어진 채로 API와 Connection을 진행하지 않았다는 의미이므로 버튼 클릭시 refetch함수가 실행되게끔 만든다.
요약하면, 문제가 발생하거나 API와 통신 진행중인 경우 단순히 로딩중 혹은 에러라는 문자열이 뜨며, API와 아직 통신하지 않았다면 불러오기 라는 문자열이 적힌 버튼이 생기고 통신을 진행한다.
return ( <> <ul> {users.map(user => ( <likey={user.id}onClick={() => setUserId(user.id)}> {user.username} ({user.name}) </li> ))} </ul> <buttononClick={refetch}>다시 불러오기</button> {userId && <Userid={userId} />} </> );
ul로 리스트를 만들어주고, 안에 내용을 li로 채워주는데, API로부터 받아온 users를 사용해 채워줌, 고유값인 key값도 넣어주고
li를 클릭할 시 유저에 관한 정보가 뜨도록 하기 위해 useState의 set도 사용해줌.
정보로 받아온 상태에서도 다시 불러오기 버튼의 refetch함수를 통해 데이터를 다시 받아올 수 있다. 그리고 userId가 setUserId에
의해 설정된다면, User Component를 렌더링 해줄 수 있게 됨
다음은 User.js를 작성해보자.
import React from "react"; import axios from "axios"; import useAsync from "./useAsync"; async function getUser(id) { const response = await axios.get( `https://jsonplaceholder.typicode.com/users/${id}` ); return response.data; } function User({ id }) { const [state] = useAsync(() => getUser(id), [id]); const { loading, data: user, error } = state; if (loading) return <div>로딩중..</div>; if (error) return <div>에러가 발생했습니다.</div>; if (!user) return null; return ( <div> <h2>{user.username}</h2> <p> <b>Email: </b> {user.email} </p> </div> ); } export default User;
설명하면
async function getUser(id) { const response = await axios.get( `https://jsonplaceholder.typicode.com/users/${id}` ); return response.data; }
이 부분 Users.js에서 getUsers처럼 API와 통신하는 부분인데, Users.js에서 {userId && <User id={userId} />} 이 부분을 통해
props에 id를 보내주었음, 그 떄문에 id를 Literal Template(`)를 통해 ${id}로 User들 마다 값을 다르게 설정해 API와 통신할 수 있음 (클릭되는 li`에 따라 값이 변하기 떄문)
즉, 방금 전 유저들의 모든 정보를 받아왔다면 이번에 단 한면에 유저에 대한 정보만 받아온 것.
notion imagenotion image
그 밑에 if 문 들은 전의 Users.js와 내용이 비슷한데, 딱 하나 다른게 있다.
if (!user) return null;
이 부분
무엇이 다르냐면, Users.js는 처음에 값을 받아오지 않도록 설정하고 버튼을 통해서 받아올 수 있게 했는데 이건 그냥 li를 클릭만해도 알아서 받아오기 때문에 이 User Component가 렌더링 되었는데 !user라는건 불가능
return ( <div> <h2>{user.username}</h2> <p> <b>Email: </b> {user.email} </p> </div> );
그래서 그렇게 받은 걸 위 처럼 출력해주면 결과물은 다음과 같다
notion imagenotion image
notion image
notion imagenotion image