티스토리 뷰
설치 명령어
npm install json-server
포트 변경
npx json-server db.json --port 4000
서버 접속
http://localhost:4000/
axios와 json-server로 CRUD 만들기
.env
- 외부로 알려지면 안 되는 API_KEY나 db관련 정보 등등 보안이 필요한 값들을 보안이나 유지 보수를 용이하게 하기 위해 환경변수로 만들어 변수를 꺼내와 사용하는 것
- 서버 주소 값을 REACT_APP_SERVER_URL 변수에 담아 프로젝트 전반에서 변수명으로 주소에 모두 접근이 가능하다
.env 파일
REACT_APP_SERVER_URL=http://localhost:4000
Get
- useEffect으로 컴포넌트가 마운트 될 때 비동기 통신 함수실행시켜 서버에 데이터를 요청함
- 요청한 데이터를 콘솔로 확인해 원하는 데이터의 키를 찾음(data)
- 데이터를 컴포넌트에서 사용하기 위해 state를 만들어줌
- 서버에서 받아온 데이터를 state에 담아서 컴포넌트에 데이터를 뿌려준다
- useEffect은 return을 먼저 읽고 실행되기 때문에 서버에서 받아온 데이터가 없는 상태로 map을 돌려서 콘솔에 오류가 찍힘 옵셔널 함수를 통해 해결 (todos?.map : todos가 있으면 맵을 돌려라)
import axios from "axios";
import React, { useEffect, useState } from "react";
// Get - 데이터 가져오기
function App() {
// 받아온 데이터를 컴포넌트 안에서 state로 사용하기 위해 state에 담아줌
const [todos, setTodos] = useState(null);
// 비동기 통신 함수
const fetchTodos = async () => {
// 서버에 요청한 값이 담긴 data를 구조분해 할당으로 변수에 담음
const { data } = await axios.get("http://localhost:4000/todos");
// 서버에서 받아온 데이터를 state에 담음
setTodos(data);
console.log("data", data);
// const response = await axios.get("http://localhost:4000/todos");
// 콘솔로 response를 확인해보면 객체 형태로 되어있는 값에 원하는 데이터는 data:{}으로 들어가있음, 그러니 data 값만 빼오기 위해 구조분해 할당을 한 것
};
useEffect(() => {
// 최초 마운트 될 때(렌더링) 비동기 통신 함수를 작동시켜 db값을 가져온다
fetchTodos();
}, []);
return (
<div>
{todos?.map((item) => {
return (
<div key={item.id}>
{item.id} : {item.title}
</div>
);
})}
</div>
);
}
export default App;
Post
- db에 넣을 데이터를 만들기 위해 state생성
- state에 들어갈 값은 json 형식을 맞추기 위해 객체형태로 key를 넣어주고 value에 state 값이 들어간다
- 위와 마찬가지로 input에 입력될 값도 onChange 함수에 setState를 json 형식에 맞춰 값을 넣어준다 {title:e.target.value}
- * state 변경으로는 데이터를 추가하면 db에 입력된 id를 state가 바로 가져오지 못하기 때문에 state 변경 대신 데이터 조회 함수를 다시 실행시킨다
import axios from "axios";
import React, { useEffect, useState } from "react";
// POST - 데이터 등록
function App() {
const [todos, setTodos] = useState(null);
// 데이터 가져오기
const fetchTodos = async () => {
const { data } = await axios.get("http://localhost:4000/todos");
setTodos(data);
console.log("data", data);
};
useEffect(() => {
fetchTodos();
}, []);
// Json 서버에 데이터 등록하기
const [inputValue, setInputValue] = useState({
// noSQL의 경우 id값은 자동으로 넣어주기때문에 따로 넣어줄 필요가 없다(Ex. firebase)
// 이미 등록된 json 방식으로 값을 넣어주기 위해 객체로 만들고 title키에 inputValue 값을 넣어줄것
title: "",
});
// 추가함수
// 추가 버튼 클릭시 input에 입력된 (state에 저장된) 값을 db에 저장 (post 요청)
const onSubmitHandler = async () => {
// newTodo를 만들지 않은 이유는 inputValue 자체가 새 객체이기때문에 새 객체를 생성할 필요가 없음 title(key)에 value로 input값을 넣어주기때문
await axios.post("http://localhost:4000/todos", inputValue);
// 데이터 추가 후 새로고침해야 추가된 데이터가 나오는 이유는 state의 상태 변화가 없기때문에 리렌더링이 일어나지 않기 때문. 그래서 데이터 추가 후 state의 상태도 변경해주면 리렌더링돼서 새로고침을 하지 않아도 된다.
// setTodos([...todos, inputValue]);
// *** state 변경으로는 데이터를 추가하면 db에 입력된 id를 state가 바로 가져오지 못하기때문에 state 변경 대신 데이터 조회 함수를 다시 실행 시킨다
fetchTodos();
};
return (
<>
<div>
{/* 데이터 등록 부분(INPUT) */}
<form
onSubmit={(e) => {
e.preventDefault();
// 추가 버튼 클릭 시 submit 이벤트 실행
onSubmitHandler();
}}
>
<input
type="text"
value={inputValue.title}
onChange={(e) => {
setInputValue({ title: e.target.value });
}}
/>
<button type="submit">추가</button>
</form>
</div>
{/* 데이터 출력 부분(OUTPUT) */}
{todos?.map((item) => {
return (
<div key={item.id}>
{item.id} : {item.title}
</div>
);
})}
</>
);
}
export default App;
Delete
- form 안에 버튼은 submit으로만 작동하기 때문에 주의해야 한다
- 삭제 버튼 클릭 시 삭제 기준을 함수에 매개변수로 보내주고(id) filter를 통해 state의 상태를 변경시킨다
import axios from "axios";
import React, { useEffect, useState } from "react";
// delete - 데이터 삭제
function App() {
const [todos, setTodos] = useState(null);
// 데이터 가져오기
const fetchTodos = async () => {
const { data } = await axios.get("http://localhost:4000/todos");
setTodos(data);
console.log("data", data);
};
useEffect(() => {
fetchTodos();
}, []);
// 데이터 추가하기
const [inputValue, setInputValue] = useState({
title: "",
});
// 추가 함수
const onSubmitHandler = async () => {
await axios.post("http://localhost:4000/todos", inputValue);
fetchTodos();
};
// 데이터 삭제하기
const onDeleteHandler = async (id) => {
axios.delete(`http://localhost:4000/todos/${id}`);
setTodos(
todos.filter((item) => {
return item.id !== id;
})
);
};
return (
<>
<div>
{/* 데이터 등록 부분(INPUT) */}
<form
onSubmit={(e) => {
e.preventDefault();
onSubmitHandler();
}}
>
<input
type="text"
value={inputValue.title}
onChange={(e) => {
setInputValue({ title: e.target.value });
}}
/>
<button type="submit">추가</button>
</form>
</div>
{/* 데이터 출력 부분(OUTPUT) */}
{todos?.map((item) => {
return (
<div key={item.id}>
{item.id} : {item.title}
{/* 삭제 버튼 */}
<button
onClick={() => {
onDeleteHandler(item.id);
}}
>
삭제
</button>
{/* <button onClick={onDeleteHandler(item.id)}>삭제</button> */}
{/* 함수에 인자를 보낼때 위와 같이 작성할 경우 렌더링 시 함수를 먼저 실행시켜버리기떄문에 한번 더 함수로 감싸줘야한다 */}
</div>
);
})}
</>
);
}
export default App;
Patch
- 수정할 데이터의 id와 내용을 state를 생성하여 담는다
- 버튼 클릭 시 state에 담은 값으로 id를 가져오고 수정된 내용은 객체형태로 담아주면 된다 {title(키) : 바꿀내용}
- state 상태 변경을 위해 map을 이용하여 수정된 데이터는 전개구문으로 수정된 title을 새로운 객체에 담아주고 수정되지 않은 값은 map함수의 인자 그대로 반환하면 된다
import axios from "axios";
import React, { useEffect, useState } from "react";
// patch - 데이터 수정
function App() {
const [todos, setTodos] = useState(null);
// 데이터 가져오기
const fetchTodos = async () => {
const { data } = await axios.get("http://localhost:4000/todos");
setTodos(data);
console.log("data", data);
};
useEffect(() => {
fetchTodos();
}, []);
// 데이터 추가하기
const [inputValue, setInputValue] = useState({
title: "",
});
// 추가 함수
const onSubmitHandler = async () => {
await axios.post("http://localhost:4000/todos", inputValue);
fetchTodos();
};
// 데이터 삭제하기
const onDeleteHandler = async (id) => {
axios.delete(`http://localhost:4000/todos/${id}`);
setTodos(
todos.filter((item) => {
return item.id !== id;
})
);
};
// 데이터 수정하기
// 수정할 데이터의 id값을 담을 state
const [targetId, setTargetId] = useState("");
// 수정할 데이터의 내용을 담을 state
const [content, setContent] = useState("");
// 수정 함수
const onUpdateHandler = async () => {
// 수정할 id와 content는 state에 저장되어있기 때문에 todos에서 인자로 받을 필요가 없다
await axios.patch(`http://localhost:4000/todos/${targetId}`, {
title: content,
});
setTodos(
todos.map((item) => {
if (item.id === targetId) {
return { ...item, title: content };
} else {
return item;
}
})
);
};
return (
<>
<div>
{/* 데이터 수정 */}
<input
type="text"
value={targetId}
onChange={(e) => {
setTargetId(e.target.value);
}}
placeholder="수정할 id"
/>
<input
type="text"
value={content}
onChange={(e) => {
setContent(e.target.value);
}}
placeholder="수정할 내용"
/>
<button onClick={onUpdateHandler}>수정</button>
</div>
<br />
<div>
{/* 데이터 등록 부분(INPUT) */}
<form
onSubmit={(e) => {
e.preventDefault();
onSubmitHandler();
}}
>
<input
type="text"
value={inputValue.title}
onChange={(e) => {
setInputValue({ title: e.target.value });
}}
/>
<button type="submit">추가</button>
</form>
</div>
{/* 데이터 출력 부분(OUTPUT) */}
{todos?.map((item) => {
return (
<div key={item.id}>
{item.id} : {item.title}
{/* 삭제 버튼 */}
<button
onClick={() => {
onDeleteHandler(item.id);
}}
>
삭제
</button>
</div>
);
})}
</>
);
}
export default App;
Interceptor
요청-응답 사이에 값을 넣는 등의 기능을 추가하는 것
- 위 코드에 axios가 있던 자리에 가공된 axios(api)를 넣어준다 Ex. axios.get() = > api.get()
- api에 baseURL로 서버 주소를 넣어줬기 때문에 서버 주소 입력 부분도 생략이 가능하다
- Ex. api.get(`${process.env.REACT_APP_SERVER_URL}/todos`); => api.get("/todos")
import axios from "axios";
// baseURL에 담긴 값을 사용하기 위해 axios를 인스턴스로(객체) 가공하여 만든다
//baseURL에 담긴 값은 .env 환경 설정 파일에 담긴 서버 주소임
const instance = axios.create({
baseURL: process.env.REACT_APP_SERVER_URL,
// 요청 시 응답이 오기까지 기다리는 시간, 입력된 시간을 초과할 경우 오류 메세지가 출력됨
timeout: 1,
});
// interceptors를 이용해 요청-응답 사이에 값을 넣는 등. 관여해보기
// 요청(request)
instance.interceptors.request.use(
// 요청을 보내기 전 수행되는 함수
function (config) {
console.log("인터셉터 요청 성공!");
return config;
},
// 오류 요청을 보내기 전 수행되는 함수
function (error) {
console.log("인터셉터 요청 오류!");
return Promise.reject(error);
// 오류는 꼭 return Promise.reject 처리를 해줘야 한다
}
);
// 응답(response)
instance.interceptors.response.use(
// 응답을 내보내기 전 수행되는 함수
function (response) {
console.log("인터셉터 응답 성공!");
return response;
},
// 오류 응답을 내보내기 전 수행되는 함수
function (error) {
console.log("인터셉터 응답 오류!");
return Promise.reject(error);
}
);
export default instance;
'TIL > React' 카테고리의 다른 글
[리액트 React] 쓰로틀링, 디바운싱_2 (Throttling & Debouncing) (0) | 2024.02.21 |
---|---|
[리액트 React] 쓰로틀링, 디바운싱_1 (Throttling & Debouncing) (0) | 2024.02.20 |
[리액트 React] 깃허브 리드미 작성 (Github_ReadMe) (0) | 2024.02.16 |
[리액트 React] 뉴스피드 팀 프로젝트 종료_파이어베이스로 게시물, 이미지 등록, 수정, 삭제 (0) | 2024.02.15 |
[리액트 React] 뉴스피드 팀 프로젝트_파이어베이스 환경 설정 (0) | 2024.02.08 |