천진난만 코딩 스토리
2023.03.11) 항해 34일차 (3- 비동기 통신 - axios) 본문
드디어 걱정되던 비동기 시작......!!!
내용 자체는 크게 어렵지 않았다.
다만 포트를 잘못 연결해서 오류가 나거나
깃 하면서 오류가 났어서 그렇지........
강의에는 없는 내용인 인풋에서 값 입력 후 버튼을 누르면 인풋을 빈 값이 되도록 구현도 하였다.
오늘은 좀 늦게까지 공부하느라 힘들긴 했지만 뿌듯하니 됐당!!!!!!!!
*이번 내용에서 사용하게 될 기본 명령어 모음!
yarn add react-redux @reduxjs/toolkit //리덕스 툴킷 설치
yarn add json-server //json-server 설치
yarn json-server --watch db.json --port 4000 //json-server의 4000 port 실행
1) Axios
공식문서에 따르면 axios 란 node.js와 브라우저를 위한 Promise 기반 http 클라이언트 라고 한다.
즉, http를 이용해서 서버와 통신하기 위해 사용하는 패키지이다.
① Axios 설치하기
yarn add axios
설치 후 테스트를 해야 하니 json-server도 설치 후 실행해주자!
2) GET
① Axios get 사용방법
get은 서버의 데이터를 조회할 때 사용한다.
// url에는 서버의 url이 들어가고, config에는 기타 여러가지 설정을 추가할 수 있다.
// config는 axios 공식문서에서 확인하기!
axios.get(url[, config]) // GET
참고 공식문서 링크: https://axios-http.com/kr/docs/req_config
② 우리가 사용하는 json-server API 명세서 확인하기
우리가 Axios를 사용해서 GET 요청 코드를 작성하기에 앞서,
어떤 방식으로 요청 해야할지는 우리가 사용하는 json-server의 방식을 알아야 한다.
즉, Axios는 GET 요청을 할 수 있도록 도와주는 패키지일뿐이지,
“어떻게 요청을 해야하지?” 와 같은 방식에 대한 확인은 우리가 사용할 API 명세서를 보아야 한다.
ex) GET 요청을 할 때 path variable로 해야할지, query로 보내야할지는 API를 만든 사람이 하라는대로 해야 한다....!
json-server의 공식문서를 보면,
전체 정보나 상세 정보는 아래와 같이 path variable 로 url을 작성하면 된다고 한다.
그리고 filter와 같은 기능을 위해서 GET 요청을 하고자할 때는 query로 보내라고 한다.
③ 코드로 알아보기
코드를 통해 사용방법을 알아보자.
우리가 만든 json-server에 있는 todos를, axios를 이용해서 fetching하고 useState를 통해서 관리하는 로직이다.
import React, { useEffect, useState } from "react";
import axios from "axios";
const App = () => {
const [todos, setTodos] = useState(null);
// axios를 통해서 get 요청을 하는 함수를 생성힌다.
// 비동기처리를 해야하므로 async/await 구문을 통해서 처리한다.
const fetchTodos = async () => {
const { data } = await axios.get("http://localhost:3001/todos");
setTodos(data); // 서버로부터 fetching한 데이터를 useState의 state로 set 한다.
};
// 생성한 함수를 컴포넌트가 mount 됐을 떄 실행하기 위해 useEffect를 사용한다.
useEffect(() => {
// effect 구문에 생성한 함수를 넣어 실행한다.
fetchTodos();
}, []);
// data fetching이 정상적으로 되었는지 콘솔을 통해 확인한다.
console.log(todos);
return (
<div>
{todos?.map((item) => {
return (
<div key={item.id}>
{item.id} : {item.title}
</div>
);
})}
</div>
);
};
export default App;
콘솔로 결과를 확인하니, 우리가 생성한 Todos를 정상적으로 서버에서 가져와서 state가 set 한 것을 확인할 수 있다.
3) POST
① Axios post 사용방법
Axios POSTpost는 보통 서버에 데이터를 추가할 때 사용한다.
다만 post 요청에 대한 로직은 BE 개발자가 구현하는 것이기때문에 추가외에 다른 용도로 사용될 수 있지만,
보통은 클라이언트의 데이터를 body형태로 서버에 보내고자 할 때 사용한다.
axios.post(url[, data[, config]]) // POST
② 코드로 알아보기
화면에 인풋과 버튼이 있고, 인풋에 어떤 값을 넣고 버튼을 클릭했을 때 onSubmitHandler 이 실행된다.
onSubmitHandler 함수의 목적은 todo를 body에 담아 서버로 POST 요청을 보내는 것이다.
아까의 get을 사용했던 코드에 아래의 코드를 추가만 해 주었다!
const App = () => {
const [todo, setTodo] = useState({
title: "",
});
// 추가 함수
const onSubmitHandler = async () => {
axios.post(`${process.env.REACT_APP_SEVER_KEY}/todos`, todo);
setTodos([...todos, todo]);
};
return (
<>
{/* Input영역 */}
<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.id} : {todo.title}
</div>
))}
</div>
</>
);
};
export default App;
4) DELETE
① Axios delete 사용방법
DELETE는 저장되어 있는 데이터를 삭제하고자 요청을 보낼 때 사용한다.
axios.delete(url[, config]) // DELETE
② 코드로 알아보기
onClickDeleteButtonHandler 와 map 을 돌린 항목별로 삭제하기 버튼을 추가해준다.
삭제 버튼을 누르면 삭제 된다.
기존 위의 코드들에 아래 코드만 추가해주었다.
//삭제 함수
const onClickDeleteButtonHandler = (id) => {
axios.delete(`http://localhost:4000/todos/${id}`);
setTodos(
todos.filter((item) => {
return item.id !== id;
})
);
};
<div>
{todos?.map((todo) => (
<div key={todo.id}>
{todo.id} : {todo.title}
//삭제 버튼 추가
<button
type="button"
onClick={() => onClickDeleteButtonHandler(todo.id)}
>
삭제하기
</button>
</div>
))}
</div>
5) PATCH
① Axios patch 사용방법
patch는 보통 어떤 데이터를 수정하고자 서버에 요청을 보낼 때 사용하는 메서드 이다.
다만, 이것은 http 환경에서 서로가 한 약속이자 문맥이기때문에,
수정을 하고자 반드시 patch, put 을 써야만 하는 것은 아니다.
BE에 의해서 POST를 통해서 “수정" 이라는 기능은 충분히 만들 수 있기 때문이다.
다만 이러한 약속들을 대부분의 개발자들이 지키고 있다는 점을 알고 있자!
axios.patch(url[, data[, config]]) // PATCH
② 코드로 알아보기
Todo를 수정하기 위해 필요한 데이터는 2개가 있다.
수정하고자하는 Todo의 id, 그리고 수정하고자 하는 값이다.
수정하고자 하는 값은 기존에 있던 todo라는 state를 사용하면 될 것이고,
id는 직접 입력을 해서 url로 넘겨주는 방식으로 구현했다.
마찬가지로 위의 코드들에 밑의 코드만 추가했다.
const [targetId, setTargetId] = useState("");
const [editTodo, setEditTodo] = useState("");
//수정 함수
const onClickEditButtonHandler = async () => {
axios.patch(`http://localhost:4000/todos/${targetId}`, {
title: editTodo,
});
setTodos(
todos.map((item) => {
if (item.id == targetId) {
return { ...item, title: editTodo };
} else {
return item;
}
})
);
setTargetId("");
setEditTodo("");
};
return (
<div>
<input
type="text"
placeholder="수정하고싶은 Todo ID"
value={targetId}
onChange={(e) => {
setTargetId(e.target.value);
}}
/>
<input
type="text"
placeholder="수정값 입력"
value={editTodo}
onChange={(e) => {
setEditTodo(e.target.value);
}}
/>
<button
// type='button' 을 추가해야 form의 영향에서 벗어남
type="button"
onClick={() => onClickEditButtonHandler()}
>
수정하기
</button>
</div>
6) ENV
변수명은 반드시 'REACT_APP_'으로 시작해야 한다.
.gitignore에 env를 등록해야 한다.
기본으로 세팅이 되어있지만 확인해주도록 하자!!
소스코드에서 import 없이 process.env.REACT_APP_변수명으로 불러오면 적용된다.
만약 env의 값을 변경하여 적용하고 싶다면, 변경 후 server를 다시 실행시켜주어야 정상적으로 적용된다.
import React, { useEffect, useState } from "react";
import axios from "axios";
const REACT_APP_SEVER_KEY = "http://localhost:4000";
const App = () => {
// 조회 함수
const fetchTodos = async () => {
const { data } = await axios.get(`${REACT_APP_SEVER_KEY}/todos`);
};
// 추가 함수
const onSubmitHandler = async () => {
axios.post(`${REACT_APP_SEVER_KEY}/todos`, todo);
};
//삭제 함수
const onClickDeleteButtonHandler = async (id) => {
axios.delete(`${REACT_APP_SEVER_KEY}/todos/${id}`);
};
//수정 함수
const onClickEditButtonHandler = async () => {
axios.patch(`http://localhost:4000/todos/${targetId}`, {
};
7) instance와 interceptor
① axios interceptor
이름에서 알 수 있듯, 다음 두 상황에서 흐름을 가로채서 여러분이 어떠한 코드 상의 관여를 할 수 있게 한다.
- 요청(request)이 처리되기 전( = http request가 서버에 전달되기 전)
- 응답(response)의 then(=성공) 또는 catch(=실패)가 처리되기 전
따라서, 우리가 가정했던 상황들을 포함하여 요청 및 응답시에 필요한 작업들을 한꺼번에 처리를 할 수 있다.
- 요청 헤더 추가
- 인증 관리
- 로그 관련 로직 삽입
- 에러 핸들링
이러한 부분에서 빛을 발한다.
② 코드로 알아보기
❶ instance 만들기, baseURL 설정하기
const data = axios.get("<http://localhost:4000/>");
axios를 사용하는 방법으로 데이터 통신을 해왔다.
완전히 plain axios, 순수 axios이다.
즉, custom 설정이 전혀 되어있지 않았는데, 이 axios를 인스턴스(instance)라고 한다.
이걸 변경하여 사용할 수 있다.
먼저, api 파일을 만든다.
//src > axios > api.js
import axios from "axios";
// axios.create의 입력값으로 들어가는 객체는 configuration 객체이다.
const instance = axios.create({
baseURL: "http://localhost:4000",
});
export default instance;
다음 app 파일에 연결을 해준다.
import api from "./axios/api";
// 조회 함수
const fetchTodos = async () => {
const { data } = await api.get("/todos");
};
// 추가 함수
const onSubmitHandler = async () => {
api.post("/todos", todo);
};
//삭제 함수
const onClickDeleteButtonHandler = async (id) => {
api.delete(`/todos/${id}`);
};
//수정 함수
const onClickEditButtonHandler = async () => {
api.patch(`/todos/${targetId}`);
};
api를 import 하고 기존에 axios.을 사용하였던 것을 api.으로 바꾸고,
주소도 지워줄 수 있어 /todos로 연결할 수 있다.
❷ request, response에 적용해보기
요청을 보낼 때, 그리고 서버로부터 응답을 받을 때(혹은 실패할 때)
특정한 일을 수행해야 한다면 interceptors를 사용할 수 있다.
//api.js
import axios from "axios";
const instance = axios.create({
baseURL: "http://localhost:4000",
// timeout: 1,
//0.001초의 시간을 주어 이 시간내에 응답을 못 받으면 오류가 나게 한다.
});
instance.interceptors.request.use(
// 요청을 보내기 전 수행되는 함수
function (config) {
console.log("인터셉트 요청 성공!");
return config;
},
// 오류 요청을 보내기 전 수행되는 함수
function (error) {
console.log("인터셉트 요청 오류!");
return Promise.reject(error);
}
);
instance.interceptors.response.use(
//응답을 내보내기 전 수행되는 함수
function (response) {
console.log("인터넵트 응답 받았어요!");
return response;
},
// 오류응답을 내보내기 전 수행되는 함수
function (error) {
console.log("인터셉트 응답 못받았어요...ㅠㅠ");
return Promise.reject(error);
}
);
export default instance;
이렇게 instance.interceptors를 사용하여 성공 시와 오류가 났을 때의 함수를 만들어준다.
또한, request와 response로 요청 시와 응답 시에 실행되도록 한다.
만약 settimeout:1을 해주게 되면 0.001초라는 짧은 시간내에 응답을 받아야 하기에 오류가 날 수 밖에 없다.
이런 식으로 오류가 났을 시의 함수를 확인하면 될 것 같다!
'TIL(Today I Learned)' 카테고리의 다른 글
2023.03.13) 항해 36일차 (2-React Query) (0) | 2023.03.13 |
---|---|
2023.03.13) 항해 36일차 (1-미들웨어, thunk) (0) | 2023.03.13 |
2023.03.11) 항해 34일차 (2-json-server, HTTP) (0) | 2023.03.12 |
2023.03.11) 항해 34일차 (1-리덕스 툴킷) (0) | 2023.03.12 |
2023.03.10) 항해 33일차 (0) | 2023.03.10 |