
효율성
=> 책의 한 문장을 고치기 위해 책 전체를 다시 쓰는 대신, 바뀐 문장만 고치는 것
프로그래머 편의성
Cross-Browser Compatibility : 브라우저 간 차이를 추상화하여 일관된 동작을 제공
instance를 사용하여 반복적인 설정 없이 Axios 요청 재사용 가능import axios from 'axios';
// Axios instance 생성
const apiClient = axios.create({
baseURL: 'https://api.example.com', // 기본 URL
timeout: 5000, // 요청 제한 시간
headers: { // 기본 헤더
'Content-Type': 'application/json',
Authorization: 'Bearer YOUR_TOKEN'
}
});
// 요청 보내기
apiClient.get('/endpoint')
.then(response => console.log(response.data))
.catch(error => console.error(error));
요청(Request) Interceptor
// 요청 인터셉터 설정
apiClient.interceptors.request.use(
config => {
// 요청 전에 작업 수행 (ex: 토큰 추가)
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
console.log('Request sent:', config);
return config; // 반드시 config를 반환해야 함
},
error => {
// 요청 에러 처리
return Promise.reject(error);
}
);
응답(Response) Interceptor
// 응답 인터셉터 설정
apiClient.interceptors.response.use(
response => {
// 응답 데이터 가공
console.log('Response received:', response);
return response.data; // 필요한 데이터만 반환
},
error => {
// 에러 처리
if (error.response && error.response.status === 401) {
console.error('Unauthorized! Redirecting to login...');
// 로그인 페이지로 이동 등의 작업 수행
}
return Promise.reject(error);
}
);
// axios.js (Axios 설정 파일)
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000
});
// Request Interceptor
apiClient.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => Promise.reject(error)
);
// Response Interceptor
apiClient.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
alert('Session expired! Redirecting to login...');
}
return Promise.reject(error);
}
);
export default apiClient;
// 다른 파일에서 사용
import apiClient from './axios';
apiClient.get('/users')
.then(data => console.log(data))
.catch(err => console.error(err));
isLoading,isError, isFetching등 상태를 자동 관리useQueryuseMutationuseQueryClientuseInfiniteQuerynpm install @tanstack/react-query
npm install @tanstack/react-query-devtools
QueryClientProvider로 설정import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<MyComponent />
</QueryClientProvider>
);
}
export default App;
useQuery훅을 사용해 데이터를 가져올 수 있음import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
function MyComponent() {
// 서버에서 데이터 가져오기
const { data, isLoading, isError } = useQuery(['todos'], async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos');
return response.data;
});
// 로딩 상태 처리
if (isLoading) return <div>Loading...</div>;
// 에러 처리
if (isError) return <div>Error fetching data</div>;
// 데이터 렌더링
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
useMutation훅을 사용import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
function AddTodo() {
const queryClient = useQueryClient();
const mutation = useMutation(newTodo => axios.post('/api/todos', newTodo), {
onSuccess: () => {
// 변경된 데이터를 다시 가져오도록 요청
queryClient.invalidateQueries(['todos']);
},
});
const handleAddTodo = () => {
mutation.mutate({ title: 'New Todo', completed: false });
};
return (
<button onClick={handleAddTodo} disabled={mutation.isLoading}>
{mutation.isLoading ? 'Adding...' : 'Add Todo'}
</button>
);
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5000, // 데이터가 "오래되지 않음" 상태로 유지되는 시간
cacheTime: 10000, // 데이터가 메모리에 유지되는 시간
refetchOnWindowFocus: true, // 창에 포커스될 때 데이터 다시 가져오기
},
},
});
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
function App() {
return (
<QueryClientProvider client={queryClient}>
<MyComponent />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
useQuery, useMutation등)을 사용[특정 데이터를 가져오는 Hook]
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
const fetchUsers = async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
return response.data;
};
// Custom Hook
export const useUsers = () => {
return useQuery(['users'], fetchUsers, {
staleTime: 5000, // 데이터가 5초 동안 "오래되지 않음" 상태로 유지
cacheTime: 10000, // 데이터가 메모리에 10초 동안 유지
refetchOnWindowFocus: false, // 창 포커스 시 재요청 비활성화
});
};
import React from 'react';
import { useUsers } from './hooks/useUsers';
function UserList() {
const { data: users, isLoading, isError } = useUsers();
if (isLoading) return <p>Loading...</p>;
if (isError) return <p>Error loading users!</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
[특정 데이터 생성, 수정하는 Hook]
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
const addUser = async (newUser) => {
const response = await axios.post('https://jsonplaceholder.typicode.com/users', newUser);
return response.data;
};
// Custom Hook
export const useAddUser = () => {
const queryClient = useQueryClient();
return useMutation(addUser, {
onSuccess: () => {
// 사용자 추가 후 사용자 목록을 다시 가져옴
queryClient.invalidateQueries(['users']);
},
});
};
import React, { useState } from 'react';
import { useAddUser } from './hooks/useAddUser';
function AddUserForm() {
const [name, setName] = useState('');
const mutation = useAddUser();
const handleSubmit = (e) => {
e.preventDefault();
mutation.mutate({ name });
setName('');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter name"
/>
<button type="submit" disabled={mutation.isLoading}>
{mutation.isLoading ? 'Adding...' : 'Add User'}
</button>
</form>
);
}
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
const fetchUserById = async (userId) => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`);
return response.data;
};
// Custom Hook
export const useUser = (userId) => {
return useQuery(['user', userId], () => fetchUserById(userId), {
enabled: !!userId, // userId가 있을 때만 쿼리 실행
});
};
import React, { useState } from 'react';
import { useUser } from './hooks/useUser';
function UserDetail() {
const [userId, setUserId] = useState(null);
const { data: user, isLoading, isError } = useUser(userId);
return (
<div>
<input
type="number"
placeholder="Enter User ID"
onChange={(e) => setUserId(Number(e.target.value))}
/>
{isLoading && <p>Loading...</p>}
{isError && <p>Error loading user data!</p>}
{user && (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
</div>
)}
</div>
);
}