# Intro
React는 효율적인 UI 구현을 위한 라이브러리입니다. HTTP Client(HTTP 상에서 커뮤니케이션 하는 자바기반 컴포넌트)
를 내장하고 있는 Angular와 다르게, 리액트에는 따로 내장하는 내장클래스가 존재하지 않습니다.
따라서 리액트에서 AJAX(비동기 웹 애플리케이션) 를 구현하기 위해서 Javascript 내장 객체인 XMLRequest를 사용하거나, 다른 HTTP Client를 사용해야 합니다.
그렇다면 어떤 HTTP Client 라이브러리를 사용하는게 좋을까요? React와 함께 쓰면 좋은 HTTP Client라이브러리는 많지만, 여기서는 현재 리액트에서 가장 많이 사용하고 있는 Fetch API를 비교하면서 Axios라이브러리가 무엇인지 알아봅시다.
# 짚고 넘어가기
1-1. AJAX (Asynchronous Javascript And XML)
AJAX란, Javascript의 라이브러리중 하나로 Asynchronous(비동기) Javascript(자바스크립트) And
Xml(비동기식 자바스크립트와 xml)의 약자입니다. 브라우저가 가진 XMLHttpRequest객체를 이용한
전체 페이지를 새로 고치지 않고도 페이지의 일부만을 위한 데이터를 로드하는 기법이며,
Javascript를 사용한 통신, 클라이언트와 서버간에 XML 데이터를 주고받는 기술이다.
정리하면, 자바스크립트를 통해서 서버에 데이터를 요청하는 것이다.
1-2. 비동기 방식이란?
비동기 방식은 웹페이지를 리로드 하지 않아도 데이터를 불러와주는 방식이며, Ajax를 통해 서버에 요청을 한 후 멈추는 것이 아닌
그 프로그램을 계속 돌아간다는 의미를 가진다.
이렇게 동기방식과 비동기방식의 특징을 가지고 있습니다. 이에대해서는 추후 자세히 설명드리기 위해 포스트를 작성해놓겠습니다.
1-3. 비동기 방식의 장단점
- 동기방식
- 설계가 매우 간단하고 간편적이다.
- 결과가 주어질때까지 아무것도 못하고 대기야하는 단점이 있다. - 비동기방식
- 동기보다 조금 더 복잡하다.
- 결과가 주어지는데 시간이 걸리지만 그 시간 동안에는 다른작업을 병행할 수 있다는 장점과 자원을 효율적으로
사용할 수 있다.
# axios VS Fetch API
일반적으로 자바스크립트에서 API를 연동하기 위해서는 보통 Fetch API를 사용하곤 했습니다. 리액트도 자바스크립트
bulit-in 라이브러리중 하나인 Fetch API라는 훌륭한 API 연동 모듈을 사용합니다.
하지만 Fetch API 에서는 자바스크립트 built-in 라이브러리라는 특성 때문에 많은사람들이 axios를 사용하는 것을 선호합니다.
npm Trends 라는 사이트에서 비교한 최근 1년기준으로 NPM에서 가장 많이 다운로드한 라이브러리를 React와 axios로 비교해보았습니다.보시면 리액트보다 axios가 그래프로보면 더 상승폭이죠
그렇다면 많은사람들이 왜 FetchAPI 보다 Axios를 더 선호 할까요??
서로 각 코드를 비교해 보겠습니다.
# Fetch API
// fetch API
const url = 'http://localhost:3000/test'
const options = {
method: 'POST',
header: {
'Accept' : 'application/json',
'Content-Type':'application/json';charset=UTP-8'
},
body:JSON.stringify({
name: 'jungho',
age: 23
})
fetch(url.options)
.then(response => console.log(response))
}
# axios
// axios
const option = {
url = 'http://localhost:3000/test'
method:'POST',
header: {
'Accept':'application/json',
'Content-Type': 'application/json';charset=UTP-8'
},
data: {
name: 'jungHo',
age: 23
}
axios(options)
.then(response => console.log(response))
}
동일한 기능을 수행하는 코드이며, 간단하게 작성된 코드입니다.
위코드의 차이점을 비교해보면
- Fetch()는 body 프로퍼티를 사용하고, axios는 data 프로퍼티를 사용합니다.
- Fetch의 url이 Fetch()함수의 인자로 들어가고, axios에서는 url이 option객체로 들어갑니다.
- Fetch에서 body부분은 stringify()로 되어집니다.
이처럼 axios는 HTTP 통신간에 요구사항을 Compact한 패키지로써 사용하기 쉽게 구성되어 있다.
위와 같은 내용들을 요약하면, 우리가 axios를 왜 배워야하고 써야하는 이유를 알게 될것이고, 이번에는 axios에 대해 알아보도록 합시다.
# Axios란 ?
Axios는 브라우저, Node.js를 위해서 만들어진 Promise API를 활요하는 HTTP 비동기 통신 라이브러리 입니다.
(백엔드와 프론드엔드간에 통신을 위해서 만들어진 AJAX도 더불어 사용하기도 합니다.)
# Axios의 특징
- Axios는 운영환경에 따라서 브라우저간 XMLHttpRequest 객체 또는 Node.js의 HTTP API를 사용한다.
- Promise(ES6) API를 사용
- 요청(Request) 응답 (reply)을 JSON 형태로 자동 변경
# Axios 사용법
- Axios 다운로드
- HTTP Methods (GET,POST,DELETE,PUT)
- Axios 사용해보기
# Axios 다운로드
yarn add axios & npm i axios
// 생성한 프로젝트 상단에 import로 추가
import axios from "axios"
# HTTP Methods
클아이언트가 웹서버에게 사용자 요청의 목적/종류를 알려주는 수단
이 Method중에서 Axios로 통신하면서 가장 많이 사용되는 메소드들을 정리해보았습니다.
1. GET
GET: 입력한 url이 존재하는 자원에 요청을 보낸다.
문법 코드
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 형태로 넘어온다.
2. POST
POST : 새로운 리소스를 생성(create)할 때 사용한다.
문법
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!') });
3. Delete
REST 기반 API 프로그램에서 데이터베이스에 저장된 내용을 삭제하는 목적으로 사용된다.
문법
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)
}
4. PUT
REST 기반 API 프로그램에서 데이터베이스에 저장된 내용을 갱신하는 목적으로 사용된다.
문법
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 <button onClick={refetch}>불러오기</button>
return (
<>
<ul>
{users.map(user => (
<li key={user.id} onclick={() => setUserId(user.id)}>
{user.username} ({user.name})
</li>
))}
</ul>
<button onClick={refetch}>다시 불러오기</button>
{userId && <User id={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 <button onClick={refetch}>불러오기</button>;
이 부분을 loading 이 true 일때, 실제로 API에서 정보를 받아오고 있을 타이밍으로 로딩중... 이라고 출력되며, error가 null이 아닐 때는 뭔가 문제가 발생한것이고, !user가 null인 것은 아직 버튼이 만들어진 채로 API와 Connection을 진행하지 않았다는 의미이므로 버튼 클릭시 refetch함수가 실행되게끔 만든다.
요약하면, 문제가 발생하거나 API와 통신 진행중인 경우 단순히 로딩중 혹은 에러라는 문자열이 뜨며, API와 아직 통신하지 않았다면 불러오기 라는 문자열이 적힌 버튼이 생기고 통신을 진행한다.
return (
<>
<ul>
{users.map(user => (
<li key={user.id} onClick={() => setUserId(user.id)}>
{user.username} ({user.name})
</li>
))}
</ul>
<button onClick={refetch}>다시 불러오기</button>
{userId && <User id={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`에 따라 값이 변하기 떄문)
즉, 방금 전 유저들의 모든 정보를 받아왔다면 이번에 단 한면에 유저에 대한 정보만 받아온 것.
그 밑에 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>
);
그래서 그렇게 받은 걸 위 처럼 출력해주면 결과물은 다음과 같다
참고 사이트
'프론트 엔드 > React' 카테고리의 다른 글
[React + Express] Node.js, Express란 무엇인가 (0) | 2021.08.31 |
---|---|
오직 React 만을 위한 라이브러리 - recoil-TodoList 만들어보기 (0) | 2021.08.30 |
오직 React만을 위한 상태관리라이브러리 - Recoil (0) | 2021.08.30 |