24. 07. 26 TIL
JSON -server
= 간단한 데이터 베이스 api 서버를 쉽게 생성해주는 도구
임시로 사용할 목적으로 많이 사용
설치 방법
yarn add json-server
# 개발 환경에만 필요할 경우 -D 옵션을 추가합니다.
yarn add json-server -D
사용
1. db.json 파일 생성
{
"todos": []
}
2. 서버 실행
실행 중인 터미널이 아닌 다른 터미널을 열어서 다음 명령어 입력
yarn json-server db.json --port 4000
axios
= 브라우저와 노드 js 환경에서 사용할 수 있는 프로미스 기반 http 클라이언트
간단히 말해, 서버와 http 통신을 쉽게 할 수 있도록 도와주는 도구
설치 방법
yarn add axios
GET 요청
axios.get(url[, config]) // URL에 서버 주소를 입력하고, config는 선택적으로 추가할 수 있습니다.
ex.
const fetchTodos = async () => {
const { data } = await axios.get("http://localhost:4000/todos");
setTodos(data);
};
useEffect(() => {
fetchTodos();
}, []);
axios는 fetch보다 간단한 문법, 더 나은 오류 처리, 기본 설정 및 인터셉터 지원, 구형 브라우저 호환성 등의 장점이 있음
axios custom instance
= axios 설정을 한 곳에서 관리할 수 있도록 해주는 특별한 axios
(매번 같은 설정을 반복하지 않기 위해 사용함. 한번 설정해두면 모든 요청에 적용됨)
1. 만들기
= 설정을 한 곳에서 관리할 수 있음
예를 들어 baseURL을 설정해 서버 주소가 변경되더라도 한 곳에서 수정하면 모든 요청에 반영되도록 할 수 있음
// axios를 import 합니다.
import axios from "axios";
// axios.create()를 사용하여 새로운 인스턴스를 만듭니다.
const api = axios.create({
baseURL: "http://localhost:4000", // 모든 요청의 기본 URL 설정
});
// 만들어진 인스턴스를 export 합니다.
export default api;
2. 사용
import React, { useEffect } from "react";
import api from "./axios/api"; // 우리가 만든 Custom Instance를 import 합니다.
function App() {
useEffect(() => {
// Custom Instance를 사용하여 GET 요청을 보냅니다.
api
.get("/cafe")
.then((res) => {
console.log("결과 => ", res.data); // 성공적으로 데이터를 받으면 콘솔에 출력합니다.
})
.catch((err) => {
console.log("오류가 발생하였습니다!"); // 에러가 발생하면 콘솔에 에러 메시지를 출력합니다.
});
}, []);
return <div>axios 예제입니다.</div>;
}
export default App;
interceptor
= http 요청과 응답을 가로채서 특정 작업을 할 수 있게 해주는 기능
* 요청 전 -> 요청이 보내지기 전에 실행됨 (ex. 모든 요청에 공통적인 헤더 추가 가능)
* 응답 전 -> 응답이 도착하기 전에 실행됨 (ex. 에러 메세지를 통일해 처리 가능)
1. interceptor 추가
import axios from "axios";
// axios.create()를 사용하여 새로운 인스턴스를 만듭니다.
const instance = axios.create({
baseURL: "http://localhost:4000", // 모든 요청의 기본 URL 설정
});
// 요청 Interceptor
instance.interceptors.request.use(
function (config) {
console.log("인터셉트 요청 성공!"); // 요청이 성공적으로 보내지기 전에 실행
return config;
},
function (error) {
console.log("인터셉트 요청 오류!"); // 요청 오류가 발생하기 전에 실행
return Promise.reject(error);
}
);
// 응답 Interceptor
instance.interceptors.response.use(
function (response) {
console.log("인터셉트 응답 받았어요!"); // 응답이 성공적으로 도착하기 전에 실행
return response;
},
function (error) {
console.log("인터셉트 응답 못받았어요...ㅠㅠ"); // 응답 오류가 발생하기 전에 실행
return Promise.reject(error);
}
);
export default instance;
2. 테스트
import React, { useEffect } from "react";
import api from "./axios/api"; // Interceptor가 추가된 Custom Instance를 import 합니다.
function App() {
useEffect(() => {
// Interceptor가 작동하는지 테스트하기 위해 GET 요청을 보냅니다.
api
.get("/cafe")
.then((res) => {
console.log("결과 => ", res.data); // 성공적으로 데이터를 받으면 콘솔에 출력합니다.
})
.catch((err) => {
console.log("오류가 발생하였습니다!"); // 에러가 발생하면 콘솔에 에러 메시지를 출력합니다.
});
}, []);
return <div>axios 예제입니다.</div>;
}
export default App;
헤더를 추가하면 뭐가 달라지나요?
1. 인증
- 목적: 클라이언트가 서버에 접근할 권한이 있는지 확인하기 위함입니다.
- 동작 방식: 클라이언트가 요청을 보낼 때 인증 정보를 헤더에 포함시켜 서버에 전달합니다.
- 예시: Authorization 헤더
Authorization: Bearer your_token_here- 설명: 서버는 이 헤더를 확인하여 클라이언트의 인증 정보를 확인하고, 유효한 경우 요청을 처리합니다.
2. 콘텐츠 타입 지정
- 목적: 서버가 클라이언트로부터 받은 데이터가 어떤 형식인지 알 수 있도록 하기 위함입니다.
- 예시: Content-Type 헤더동작 방식: 클라이언트가 데이터를 보낼 때 콘텐츠 타입 정보를 헤더에 포함시켜 서버에 전달합니다.
-
Content-Type: application/json
- 설명: 서버는 이 헤더를 확인하여 데이터를 JSON 형식으로 해석합니다. 이 정보가 없으면 서버는 데이터를 제대로 처리하지 못할 수 있습니다.
3. 커스텀 헤더
- 목적: 서버와 클라이언트 간의 특별한 정보를 전달하기 위함입니다.
-
- 예시: X-Custom-Header 헤더동작 방식: 개발자가 필요에 따라 정의한 헤더를 사용하여 특정 정보를 주고받습니다.
X-Custom-Header: some_custom_value- 설명: 클라이언트와 서버는 이 헤더를 통해 특별한 정보나 지시사항을 주고받을 수 있습니다. 예를 들어, 디버깅 정보를 전달하거나 특정 작업을 트리거할 수 있습니다.
Tanstack Query
= 비동기 데이터를 더 쉽게 관리할 수 있는 라이브러리
주요 기능
1. Fetching -> 서버에서 데이터를 가져옴
2. Caching -> 동일한 데이터를 반복해서 요청하지 않도록 캐싱
3. Synchromizing -> 서버의 데이터와 캐시된 데이터 동기화
4. Updating -> 서버의 데이터를 쉽게 업데이트 하고 캐시에 반영
SWR 전략
= 최신 데이터가 도착하기 전까지 기존 캐시 데이터를 사용하는 전략
이를 통해 사용자는 최신 데이터를 기다리는 동안에도 빠른 응답을 받을 수 있음
캐시 데이터
= 탠스택 쿼리는 캐시 데이터를 전역적으로 관리
쿼리 클라이언트 프로바이더를 사용해 리액트 애플리케이션 전체에서 캐시 데이터에 접근 가능
캐시 데이터는 내부적으로 리액트 컨텍스트 api를 통해 관리됨
쿼리 옵션
1. enabled -> 쿼리 실행 여부 제어, 기본값은 true
쿼리가 자동으로 실행될지 여부
특정 조건이 충족될 때만 쿼리를 실행하거나, 수동으로 쿼리 실행 가능
import React, { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
// 비동기 데이터 패칭 함수
const fetchTodos = async () => {
const { data } = await axios.get("https://jsonplaceholder.typicode.com/todos/1");
return data;
};
const App = () => {
const [shouldFetch, setShouldFetch] = useState(false); // 데이터 패칭 여부를 결정하는 state
const { data, refetch, isLoading, isError } = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
enabled: shouldFetch, // 쿼리가 자동으로 실행되지 않도록 설정
});
return (
<div>
<h1>TanStack Query</h1>
<button onClick={() => setShouldFetch(true)}>데이터 불러오기</button> {/* 버튼 클릭 시 shouldFetch를 true로 설정 */}
{shouldFetch && isLoading && <div>Loading...</div>} {/* 로딩 상태일 때 표시 */}
{isError && <div>Error: Unable to fetch data</div>} {/* 에러 발생 시 표시 */}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>} {/* 데이터가 존재할 때 표시 */}
</div>
);
};
export default App;
2. select -> 쿼리 함수에서 반환된 데이터를 변형하여 사용할 수 있도록 하는 옵션
쿼리 함수가 반환한 데이터를 가공해 원하는 형태로 사용 가능
import React from "react";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
// 사용자 데이터를 가져오는 비동기 함수
const fetchUser = async () => {
const { data } = await axios.get("https://jsonplaceholder.typicode.com/users/1");
return data;
};
const App = () => {
const { data, isLoading, isError } = useQuery({
queryKey: ["user"],
queryFn: fetchUser,
select: (user) => user.username, // user 객체에서 username만 선택
});
return (
<div>
<h1>User Information</h1>
{isLoading && <div>Loading...</div>} {/* 로딩 상태일 때 표시 */}
{isError && <div>Error: Unable to fetch user</div>} {/* 에러 발생 시 표시 */}
{data && <div>Username: {data}</div>} {/* username 데이터 표시 */}
</div>
);
};
export default App;
심화
- Query Cancellation: 불필요한 네트워크 요청을 취소하여 네트워크 비용 절감. (쿼리 취소)
- Optimistic Updates: 서버 요청이 성공할 것이라 가정하고 UI를 먼저 업데이트. (낙관적 업데이트)
- Prefetching: 페이지 이동 전에 데이터를 미리 가져와 로딩 없이 바로 보여줌. (프리패칭)
- Paginated Queries: 이전 데이터를 유지하면서 새로운 데이터를 받아옴. (페이지네이션)
- Infinite Queries: 데이터를 무한 스크롤 방식으로 계속 추가. (무한스크롤)