TIL

24. 07. 26 TIL

효ㄷi 2024. 7. 26. 21:02

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;

 

 

심화

  1. Query Cancellation: 불필요한 네트워크 요청을 취소하여 네트워크 비용 절감. (쿼리 취소)
  2. Optimistic Updates: 서버 요청이 성공할 것이라 가정하고 UI를 먼저 업데이트. (낙관적 업데이트)
  3. Prefetching: 페이지 이동 전에 데이터를 미리 가져와 로딩 없이 바로 보여줌. (프리패칭)
  4. Paginated Queries: 이전 데이터를 유지하면서 새로운 데이터를 받아옴. (페이지네이션)
  5. Infinite Queries: 데이터를 무한 스크롤 방식으로 계속 추가. (무한스크롤)