[React 심화] axios 1 - 소개 및 설정

조아영·2025년 3월 13일

📌

axios는 node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트.
즉, http를 이용해 서버와 통신할 때 사용하는 패키지.

◼ 설치

npm install axios

◼ 설정

db.json 설정

프로젝트 root 경로에 db.json 파일 생성 후 아래 json 코드 삽입.

{
  "todos": [
    {
      "id": "1",
      "title": "react"
    }
  ]
}

.env 설정

민감 정보는 .env 파일로 관리.

Vite로 만든 프로젝트

// .env

VITE_원하는이름 = 123
VITE_EXAMPLE_SERVER_URL = http://localhost:4000
VITE_API_KEY = test1234
VITE_SECRET_KEY = abcdefg
// src/main.js 또는 src/App.jsx

const apiKey = import.meta.env.VITE_API_KEY;
console.log('API Key:', apiKey);

CRA로 만든 프로젝트

// .env

REACT_APP_원하는이름 = 123
REACT_APP_EXAMPLE_SERVER_URL = http://localhost:4000
REACT_APP_API_KEY = test123
REACT_APP_SECRET_KEY = abcdefg
// src/index.js 또는 src/App.jsx

const apiKey = process.env.REACT_APP_API_KEY;
console.log('API Key:', apiKey);

.gitignore 설정

원격 환경(github)에 보안 정보는 포함되지 않도록(git repository에 포함되지 않도록) 관리 필요.
root 경로에 .gitignore 파일 생성 후 .env(환경 설정 파일) 추가.

파일경로

├json_folder
│├node_modules
│├public
│└src
├.eslintrc.cjs
├.gitignore
├index.html
├package.json
├README.md
└vite.config.js

.gitignore 파일

# Node.js 관련 파일
/node_modules

# 환경 설정 파일
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# 빌드 출력 파일
/build
/dist

# 기타
.DS_Store

◼ GET

서버 데이터 조회 시 사용.

// GET(url은 매개변수, 대괄호([]) 안의 값은 선택사항.
axios.get(url[, config]) 

url에는 서버의 url. config에는 설정추가 가능. 세부옵션은 axios 공식문서에서 확인.

🔗 Axios config 공식문서

json-server API 명세서 확인

Axios는 HTTP 요청을 도와주는 도구일 뿐임. 어떤 방식으로 요청해야 하는지는 API 명세서 기준으로 결정.
예를 들어 GET 요청을 할 때 path variable 방식일지 query 방식일지는 API를 설계한 사람이 정의한 규칙을 따름.

json-server의 공식문서를 보면 전체 정보나 상세 정보는 path variable로 url 작성.

GET /posts
GET /posts/1

filter와 같은 기능을 위해서 GET 요청을 하고자할 때는 query로 보내라고 명시.

GET /posts?title=json-server&author=typicode 
GET /posts?id=1&id=2
GET /comments?author.name=typicode

예시

json-server에 데이터를 axios로 fetching 후 useState로 관리하는 로직 작성.

// src/App.js

import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import

const App = () => {
  const [todos, setTodos] = useState(null);

	// axios를 통해서 get 요청을 하는 함수를 생성.
	// 비동기처리를 해야하므로 async/await 구문을 통해서 처리.
  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data); // 서버로부터 fetching한 데이터를 useState의 state로 set.
  };
	
	// 생성한 함수를 컴포넌트가 mount된 후 실행하기 위해 useEffect를 사용.
  useEffect(() => {
		// effect 구문에 생성한 함수를 넣어 실행.
    fetchTodos();
  }, []);

	// data fetching이 정상적으로 되었는지 콘솔을 통해 확인.
  console.log(todos);
  return <div>App</div>;
};

export default App;

콘솔

null
--------------------------------------------[{...}] 10: {id: '1', title: 'react'}
		Length: 1[[Prototype]]: Array(0)

useEffect에서 최초 1회 실행. axios로 데이터 요청. 데이터를 state에 저장.
콘솔에서 null → 배열 데이터 변화 확인 가능.

◼ POST

서버 데이터 추가 시 사용.

axios.post(url[, data[, config]])

POST 요청 로직은 BE 개발자가 구현하므로 데이터 추가 외의 용도로도 사용할 수 있음.
보통 body에 데이터를 담아 전송.

예시

GET 예시에 POST 코드 추가.
input 값 입력 → 버튼 클릭 → onSubmitHandler 실행 → todo를 body에 담아 서버로 전송.

// src/App.jsx

import React, { useEffect, useState } from "react";
import axios from "axios"; // axios import

const App = () => {
	// ✅ S: 추가코드
  // 새롭게 생성하는 todo를 관리하는 state.
  const [todo, setTodo] = useState({
    title: "",
  });
	// ✅ E: 추가코드

  const [todos, setTodos] = useState(null);

  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data);
  };
	
	// ✅ S: 추가코드
	// axios는 내부적으로 JSON.stringify 자동 처리.
  const onSubmitHandler = async(todo) => {
    await axios.post("http://localhost:4000/todos", todo);
  };
  
	// fetch 사용 시 직접 JSON.stringify 필요.
  // await fetch("http://localhost:4000/todos", {
  //   method: "POST",
  //   headers: {
  //     "Content-Type": "application/json",
  //   },
  //   body: JSON.stringify(todo),
  // });
 	// ✅ E: 추가코드

  useEffect(() => {
    fetchTodos();
  }, []);

  return (
	 	{/* ✅ S: 추가코드 */}
    <>
      <form
        onSubmit={(e) => {
					// submit했을 때 브라우저의 새로고침을 방지. 
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      >
        <input
          type="text"
          onChange={(ev) => {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        />
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) => (
          <div key={todo.id}>{todo.title}</div>
        ))}
      </div>
    </>
	 	{/* ✅ E: 추가코드 */}
  );
};

export default App;

새로고침 없이 화면 반영 방법

POST 요청을 마치면 새로고침 해야만 새로운 정보가 표시됨.
POST 요청은 서버 데이터만 변경함. React 화면은 state가 변경되어야만 리렌더링 발생함.
현재 코드에서는 POST 요청 전송 → 서버 DB에는 데이터 저장 → 하지만 todos state는 그대로 유지.
즉, 클라이언트 state와 서버 데이터가 동기화되지 않은 상태.

// (...중략)
 const onSubmitHandler = async(todo) => {
   await axios.post("http://localhost:4000/todos", todo);

	setTodos([...todos, todo]);
 };

새로고침 없이 업데이트하려면 기존 배열 복사 → 새 todo 추가 → state 변경 발생 → 리렌더링 실행 → 화면 즉시 반영 가능.

네트워크 탭 확인

개발 시 Network 탭 확인 필요. 문제가 생겼을 때 이 정보를 통해 디버깅을 할 수 있음.

headers

  • Request URL : 의도한 URL로 POST 요청을 보냈는지 확인.
  • Request Method : POST 메서드 사용여부 확인.
  • Status Code : 201이면 정상 생성.
    status code는 자동으로 생성되지 않음. BE 개발자가 정의. 만약 BE 개발자가 구현해 두지 않았으면 문맥과 다른 status code가 브라우저에 표시될 수 있음.

payload

  • Payload : 전송한 body 확인 가능

response

  • Response : 서버 응답 데이터 확인 가능.
    response는 자동으로 생성되지 않음. FE 개발자가 BE 개발자에게 요청한 내용을 BE에서 직접 구현해야 응답 값이 생김. json-server는 POST 요청 시 클라이언트가 보낸 body를 그대로 응답하도록 만들어진 패키지.

◼ DELETE

저장된 데이터 삭제 시 사용.

axios.delete(url[, config])

예시

GET, POST에 이어서 코드 추가.
삭제 버튼 추가 → onClickDeleteButtonHandler 구현 → 해당 id로 DELETE 요청 전송.
성공 후 새로고침 시 데이터 삭제 확인 가능.

// src/App.jsx

import React, { useEffect, useState } from "react";
import axios from "axios"; 

const App = () => {
  const [todo, setTodo] = useState({
    title: "",
  });

  const [todos, setTodos] = useState(null);

  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data); 
  };

  const onSubmitHandler = (todo) => {
    axios.post("http://localhost:4000/todos", todo);
  };

	// ✅ S: 추가코드
	// 새롭게 추가한 삭제 버튼 이벤트 핸들러 
  const onClickDeleteButtonHandler = (todoId) => {
    axios.delete(`http://localhost:4000/todos/${todoId}`);
  };
  // ✅ E: 추가코드

  useEffect(() => {
    fetchTodos();
  }, []);

  return (
    <>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      >
        <input
          type="text"
          onChange={(ev) => {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        />
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) => (
          <div key={todo.id}>
            {todo.title}
            
					  {/* ✅ S: 추가코드 */}
            <button
              type="button"
              onClick={() => onClickDeleteButtonHandler(todo.id)}
            >
              삭제하기
            </button>
            {/* ✅ E: 추가코드 */}
          </div>
        ))}
      </div>
    </>
  );
};

export default App;

◼ PATCH

데이터 수정 시 사용.

axios.patch(url[, data[, config]])

HTTP 관례상 수정은 PATCH 또는 PUT 사용. 필수 규칙은 아니지만 관례적으로 사용.

예시

GET, POST, DELETE에 이어서 코드 추가. PUT은 PATCH와 원리가 같아 생략.
Todo 수정에 필요한 데이터는 수정대상 id, 수정할 값.

보통 수정 기능을 만들 때 직접 id를 입력받아 처리하는 방식은 거의 없음.
이번 예시에서는 아주 간단한 코드로 기능을 구현하기 때문에 이와 같이 처리.

// src/App.jsx

import React, { useEffect, useState } from "react";
import axios from "axios";

const App = () => {
  const [todo, setTodo] = useState({
    title: "",
  });
  const [todos, setTodos] = useState(null);

  // patch에서 사용할 id, 수정값의 state를 추가.
  const [targetId, setTargetId] = useState(null);
  const [editTodo, setEditTodo] = useState({
    title: "",
  });

  const fetchTodos = async () => {
    const { data } = await axios.get("http://localhost:4000/todos");
    setTodos(data);
  };

  const onSubmitHandler = (todo) => {
    axios.post("http://localhost:4000/todos", todo);
  };

  const onClickDeleteButtonHandler = (todoId) => {
    axios.delete(`http://localhost:4000/todos/${todoId}`);
  };
	
	// ✅ S: 추가코드
  // 수정버튼 이벤트 핸들러 추가
  const onClickEditButtonHandler = (todoId, edit) => {
    axios.patch(`http://localhost:4000/todos/${todoId}`, edit);
  };
  // ✅ E: 추가코드

  useEffect(() => {
    fetchTodos();
  }, []);

  return (
    <>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      >
	      {/* ✅ S: 추가코드 */}
        {/* 수정기능에 필요한 id, 수정값 input 2개와 수정하기 버튼을 추가 */}
        <div>
          <input
            type="text"
            placeholder="수정하고싶은 Todo ID"
            onChange={(ev) => {
              setTargetId(ev.target.value);
            }}
          />
          <input
            type="text"
            placeholder="수정값 입력"
            onChange={(ev) => {
              setEditTodo({
                ...editTodo,
                title: ev.target.value,
              });
            }}
          />
          <button
						// type='button' 을 추가해야 form의 영향에서 벗어남
            type="button"
            onClick={() => onClickEditButtonHandler(targetId, editTodo)}
          >
            수정하기
          </button>
        </div>
        {/* ✅ E: 추가코드 */}
        <input
          type="text"
          onChange={(ev) => {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        />
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) => (
          <div key={todo.id}>
	          {/* ✅ S: 추가코드 */}
						{/* todo의 아이디를 화면에 표시 */}
            {todo.id} :{todo.title}
            {/* ✅ E: 추가코드 */}
            <button
              type="button"
              onClick={() => onClickDeleteButtonHandler(todo.id)}
            >
              삭제하기
            </button>
          </div>
        ))}
      </div>
    </>
  );
};

export default App;

◼ 장점

fetch와 axios는 모두 HTTP 요청(GET, POST…)을 처리하기 위한 JavaScript 라이브러리.
React에서는 axios를 선호하는데 다음과 같은 장점 때문임.

1. 기본 설정 및 인터셉터 지원

기본 설정 : axios는 기본 설정을 정의해 모든 요청에 공통 설정을 적용할 수 있음.

const axiosInstance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 1000,
  headers: { 'X-Custom-Header': 'foobar' }
});

인터셉터 : 요청, 응답을 가로채 전처리나 후처리를 할 수 있음. 이를 통해 인증 토큰을 자동으로 추가하거나 오류를 일괄 처리할 수 있음.

axios.interceptors.request.use(config => {
  config.headers.Authorization = `Bearer ${token}`;
  return config;
}, error => {
  return Promise.reject(error);
});

2. 오류 처리 방식

에러 핸들링 : axios는 4xx, 5xx 상태 코드를 자동으로 catch에서 처리할 수 있어 일관된 오류 처리가 가능. fetch는 네트워크 오류만 catch에서 처리되며, 4xx, 5xx 오류는 then에서 처리해야 함.

axios.get('/user/12345')
  .then(response => console.log(response.data))
  .catch(error => {
    if (error.response) {
      // 서버가 4xx, 5xx 응답을 반환
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      // 요청이 전송되었지만 응답이 없음
      console.log(error.request);
    } else {
      // 요청 설정 중에 발생한 오류
      console.log('Error', error.message);
    }
  });

3. 브라우저 호환성

구형 브라우저 지원 : axios는 구형 브라우저 대응 용이. fetch는 일부 환경에서 폴리필 필요.

4. 문법 간결성

간결한 문법: axios는 간결하고 직관적인 문법 사용. fetch 대비 코드 간결성 확보 가능.

axios.get('/user')
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

0개의 댓글