Fetch 사용
- 기존 사용하는 XMLHttpRequest를 대체함.
- 첫번째 인수로 요청 URL을, 두번째 인수로 옵션 객체(method, headers, body 등의 속성)를 입력받음
Todo 예
"use client";
import { useState } from "react";
type Todos = {
userId: string;
id: number;
title: string;
completed: boolean;
};
const TodosFetchPage = () => {
const [todoId, setTodoId] = useState("");
const [todos, setTodos] = useState<Todos[]>([]);
const getTodos = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos"
);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const data = await response.json();
setTodos(data);
} catch (error) {
console.error(error);
}
};
const postTodo = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userId: "1000",
title: "New Title",
completed: false,
}),
}
);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const data = await response.json();
setTodos([...todos, data]);
} catch (error) {
console.error(error);
}
};
const toggleTodo = async () => {
try {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId}`,
{
method: "PATCH",
body: JSON.stringify({ completed: true }),
}
);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const data = await response.json();
setTodos(
todos.map((todo) =>
todo.id === Number(todoId) ? { ...todo, done: data.done } : todo
)
);
} catch (error) {
console.error(error);
}
};
const updateTodo = async () => {
try {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title: "Update Todo" }),
}
);
if (!response.ok) {
console.log(response);
throw new Error("Network response was not ok");
}
const data = await response.json();
setTodos(todos.map((todo) => (todo.id === Number(todoId) ? data : todo)));
} catch (error) {
console.error(error);
}
};
const deleteTodo = async () => {
try {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId}`,
{
method: "DELETE",
}
);
if (!response.ok) {
throw new Error("Network response was not ok");
}
setTodos(todos.filter((todo) => todo.id !== Number(todoId)));
} catch (error) {
console.error(error);
}
};
return (
<div>
<h1>Todos</h1>
<pre>{JSON.stringify(todos, null, 2)}</pre>
<div>
<label htmlFor="todoId"> Todo ID: </label>
<input
id="todoId"
className="border p-3 rounded mb"
type="text"
value={todoId}
onChange={(e) => setTodoId(e.target.value)}
/>
</div>
<button
className="mr-3 bg-blue-700 text-white py-3 rounded hover:bg-blue-800"
onClick={getTodos}
>
Get Todos
</button>
<button
className="mr-3 bg-blue-700 text-white py-3 rounded hover:bg-blue-800"
onClick={postTodo}
>
Post Todos
</button>
<button
className="mr-3 bg-blue-700 text-white py-3 rounded hover:bg-blue-800"
onClick={toggleTodo}
>
Toggle Todos
</button>
<button
className="mr-3 bg-blue-700 text-white py-3 rounded hover:bg-blue-800"
onClick={updateTodo}
>
Update Todos
</button>
<button
className="mr-3 mb-3 bg-blue-700 text-white py-3 rounded hover:bg-blue-800"
onClick={deleteTodo}
>
Delete Todos
</button>
</div>
);
};
export default TodosFetchPage;
Axios 사용
- Axios는 웹브라우저에서 HTTP 통신을 쉽게 처리하기 위한 라이브러리임.
- Fetch API 보다 더 다양한 기능을 제공함.
- config는 method, url, data, headers를 설정할 수 있다.
- axios()함수는 response.data로 바로 응답 객체를 접근할 수 있다. fetch()는 reponse.json()을 따로 호출해야 한다.
- axios()는 응답이 실패한 경우 자동으로 catch 블록으로 오류를 반환한다. fetch()처럼 일일이 response.ok를 체크할 필요가 없다.
npm install axios
// Promise 기반 then() 방식
axios(config)
.then(response => console.log(response))
.catch(error => console.error('Error:', error));
// async/await 방식
const fetchData = async () => {
try{
const response = await axios(config);
console.log(response.data)
} catch (error) {
console.log(error)
}
}
Todo 예
import axios from "axios";
import { useState } from "react";
type Todos = {
userId: string;
id: number;
title: string;
completed: boolean;
};
const TodosPage = () => {
const [todos, setTodos] = useState<Todos[]>([]);
const getTodos = async () => {
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/todos"
);
setTodos(response.data);
} catch (error) {
console.error(error);
}
};
const postTodo = async () => {
try {
const response = await axios.post(
"https://jsonplaceholder.typicode.com/todos",
{
title: "New Title",
}
);
setTodos([...todos, response.data]);
} catch (error) {
console.error(error);
}
};
const toggleTodo = async () => {
const todoId = 1;
try {
const response = await axios.patch(
`https://jsonplaceholder.typicode.com/todos/${todoId}/done`
);
setTodos(
todos.map((todo) =>
todo.id === Number(todoId)
? { ...todo, done: response.data.done }
: todo
)
);
} catch (error) {
console.error(error);
}
};
const updateTodo = async () => {
const todoId = 1;
try {
const response = await axios.put(
`https://jsonplaceholder.typicode.com/todos/${todoId}`,
{
title: "Updated toto",
}
);
setTodos(
todos.map((todo) => (todo.id === Number(todoId) ? response.data : todo))
);
} catch (error) {
console.error(error);
}
};
const deleteTodo = async () => {
const todoId = 1;
try {
await axios.delete(
`https://jsonplaceholder.typicode.com/todos/${todoId}`
);
setTodos(todos.filter((todo) => todo.id !== Number(todoId)));
} catch (error) {
console.error(error);
}
};
};
export default TodosPage;
Tips
useState(), useEffect()를 이용하여 화면 출력 전 API 호출하기
import { useState, useEffect } from "react";
const App = () => {
const [data, setData] = useState<
{ id: number; title: string; completed: boolean }[]
>([]);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos"
);
if (!response.ok) {
throw new Error("데이터를 불러오지 못했습니다.");
}
const data = await response.json();
setData(data);
};
fetchData();
}, []);
return (
<div>
{}
{}
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</div>
);
};
오류 처리
import { useState, useEffect } from "react";
const App = () => {
const [data, setData] = useState<
{ id: number; title: string; completed: boolean }[]
>([]);
const [error, setError] = useState("");
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos"
);
if (!response.ok) {
throw new Error("데이터를 불러오지 못했습니다.");
}
const data = await response.json();
setData(data);
} catch (error) {
setError(
error instanceof Error
? error.message
: "알 수 없는 오류가 발생했습니다."
);
}
};
fetchData();
}, []);
if(error) return <div>{error}</div>
return (
<div>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</div>
);
};
Loading 중 표시
import { useState, useEffect } from "react";
const App = () => {
const [data, setData] = useState<
{ id: number; title: string; completed: boolean }[]
>([]);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos"
);
if (!response.ok) {
throw new Error("데이터를 불러오지 못했습니다.");
}
const data = await response.json();
setData(data);
} catch (error) {
setError(
error instanceof Error
? error.message
: "알 수 없는 오류가 발생했습니다."
);
} finally {
setIsLoading(false);
}
};
fetchData();
}, []);
if (isLoading) return <div>로딩 중 ...</div>;
if (error) return <div>{error}</div>;
return (
<div>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</div>
);
};
데이터 요청 취소하기
import { useState, useEffect } from "react";
const App = () => {
const [data, setData] = useState<
{ id: number; title: string; completed: boolean }[]
>([]);
const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
const fetchData = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos",
{ signal }
);
if (!response.ok) {
throw new Error("데이터를 불러오지 못했습니다.");
}
const data = await response.json();
setData(data);
setIsLoading(false);
} catch (error) {
setError(
error instanceof Error
? error.message
: "알 수 없는 오류가 발생했습니다."
);
setIsLoading(false);
} finally {
setIsLoading(false);
}
};
fetchData();
return () => {
controller.abort();
};
}, []);
if (isLoading) return <div>로딩 중 ...</div>;
if (error) return <div>{error}</div>;
return (
<div>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</div>
);
};