api 연동
api 연동이란?
-
axios 라이브러리 설치
yarn add axios
-
사용법
import axios from "axios"; axios.메소드방식("API주소", { 등록할정보 }); /* axios.get('/users/1'); */
예시
axios.post("/users", { username: "kaen", name: "sunghun", });
-
API를 요청할때 관리 순서
- 요청의 결과
- 로딩 상태
- 에러
rest api
클라이언트와 서버가 소통하는 방식으로 HTTP 메서드 방식으로 소통
-
HTML 메소드 방식 종류 |메소드|역할| |-|-| |GET|데이터 조회| |POST|데이터 등록| |PUT|데이터 수정| |DELETE|데이터 제거|
-
메소드 예시 |예시|설명| |-|-| |GET /users|모든 사용자의 계정을 반환| |GET /users/1|아이디가 1인 계정 데이터를 반환| |POST /articles|특정 정보를 등록| |GET /articles|모든 게시글 정보를 불러옴| |GET /articles/1|아이디가 1인 게시글 정보를 불러옴| |PUT /articles/1|아이디가 1인 게시물을 수정| |DELETE /articles/1|아이디가 1인 게시물을 삭제|
컴포넌트에서 API로 데이터를 요청하는 기본적인 방법은 useState와 useEffect로 데이터를 로딩하는 것
useState로 구현
import React, { useState, useEffect } from "react";
import axios from "axios";
function Users() {
// 결과물
const [users, setUsers] = useState(null);
// 로딩
const [loading, setLoading] = useState(false);
// 에러
const [error, setError] = useState(null);
// 새로고침하기 위해 useEffect 밖으로 함수를 꺼냈음
const fetchUsers = async () => {
try {
setUsers(null);
setError(null);
setLoading(true);
// await사용
const response = await axios.get(
"https://jsonplaceholder.typicode.com/users/"
);
// 결과값을 확인
setUsers(response.data);
} catch (e) {
// 발생한 에러값을 받아줌
setError(e);
}
// 로딩이 끝났다고 알림
setLoading(false);
};
// 처음 렌더링 될때 API를 요청
useEffect(() => {
// 함수 호출
fetchUsers();
}, []);
// 만약 로딩 중이라면
if (loading) return <div>로딩중..</div>;
// 만약 에러가 있다면
if (error) return <div>에러가 발생했습니다</div>;
// 만약 로딩이 끝났지만 users가 유효하지 않다면
if (!users) return null;
return (
<>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
{/* 누를때마다 API함수가 호출됨 */}
<button onClick={fetchUsers}>다시 불러오기</button>
</>
);
}
export default Users;
useReducer로 구현
요청 관리 로직을 분리해서 재사용할 수 있는 장점이 있음(커스텀 hook)
import React, { useEffect, useReducer } from "react";
import axios from "axios";
// LOADING, SUCCESS, ERROR reducer 함수를 생성
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}`);
}
}
function Users() {
const [state, dispatch] = useReducer(reducer, {
loading: false,
data: null,
error: null,
});
const fetchUsers = async () => {
// dispatch 값 사용
dispatch({ type: "LOADING" });
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/users/"
);
// dispatch 값 사용
dispatch({ type: "SUCCESS", data: response.data });
} catch (e) {
// dispatch 값 사용
dispatch({ type: "ERROR", error: e });
}
};
useEffect(() => {
fetchUsers();
}, []);
// 비구조화 할당
const { loading, data: users, error } = state; // data: users로 data가 users 값이 되게 해줌
if (loading) return <div>로딩중..</div>;
if (error) return <div>에러가 발생했습니다</div>;
if (!users) return null;
return (
<>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
<button onClick={fetchUsers}>다시 불러오기</button>
</>
);
}
export default Users;
커스텀 Hook 구현
// 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; // data: users로 data가 users 값이 되게 해줌
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;
// 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;
// useAsync.js
import React, { useReducer, useEffect, useCallback } from "react";
// LOADING, SUCCESS, ERROR reducer 함수를 생성
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}`);
}
}
// 콜백은 API를 호출하는 함수를 넣어줄것
// deps는 컴포넌트가 로딩 혹은 변경됬을때 요청
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();
}, deps);
return [state, fetchData];
}
export default useAsync;
react-async로 구현
useAsync와 비슷한 라이브러리
-
설치
yarn add react-async
-
사용법
react-async
의useAsync
를 사용할 때 파라미터로 넣는 옵션 객체에는 호출 할 함수promiseFn
을 넣고, 파라미터도 필드 이름과 함께 (customerId
)를 넣어줘야 함// 공식문서 import { useAsync } from "react-async"; const loadCustomer = async ({ customerId }, { signal }) => { const res = await fetch(`/api/customers/${customerId}`, { signal }); if (!res.ok) throw new Error(res); return res.json(); }; const MyComponent = () => { const { data, error, isLoading } = useAsync({ promiseFn: loadCustomer, customerId: 1, }); if (isLoading) return "Loading..."; if (error) return `Something went wrong: ${error.message}`; if (data) return ( <div> <strong>Loaded some data:</strong> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); return null; };
사용자의 특정 액션에 따라 API를 호출하고 싶을 땐
prmiseFn
대신deferFn
을 사용하고reload
대신run
을 사용하면 됨
Context로 구현
현재 로그인된 사용자의 정보나 설정 등 특정 데이터들을 Context API로 관리하는게 편함