저번 시간에는 express를 이용해서 서버에 데이터를 추가하고 변경하는 것까지 해봤습니다.
이번에는 리액트에서 코드를 짜서 서버에 데이터 요청을 해보겠습니다.
(라매개발자님의 강의 영상을 참고하였습니다.)
먼저 create-react-app database를 터미널에 입력해서 작업 폴더를 만들어 줍니다.
그리고 필요없는 css파일들과 logo.svg파일을 지워줍시다.
파일을 지웠으니까 index.js와 app.js의 import문과 내용을 지워줍니다.
function App() {
return (
<div className="App">
<h1>Todolist</h1>
</div>
);
}
export default App;
임의로 코드를 적고 npm start를 통해서 테스트 해봅시다.
잘 나오네요!
서버에 데이터를 요청하려면
fetch와 axios를 사용해야 합니다.
fetch는 설치할 필요가 없이 간단히 사용할 수 있습니다.
서버에 데이터를 요청하려면
1. 서버 주소를 알아야 하고
2. 어떤 HTTP 메소드를 사용할 지 알아야 합니다.
먼저 서버의 호스트를 4000으로 바꿔줍시다.
app.listen(4000, ()=>{
console.log("잘 구동됩니다");
})
이 부분을 위와 같이 4000으로 바꿔주면 됩니다.
function App() {
fetch('http://localhost:4000/api/todo')
.then((response) => response.json())
.then((data) => console.log(data));
return (
<div className="App">
<h1>Todolist</h1>
</div>
);
}
export default App;
fetch에 host 주소와 json파일 형식으로 바꿔주는 코드를 짜줍니다.
근데 이때 cors오류가 생깁니다.
cors문제는 host origin이 달라서 생기기 때문에
데이터를 제공해주는 서버측에서 문제를 해결해야 합니다.
이때 npm의 cors라이브러리를 사용해서 문제를 해결할 수 있습니다.
cors를 설치하고
app.use(cors())를 사용하면
어떤 곳에서든 데이터를 꺼내갈 수 있습니다.
cors정책 문제가 해결된 모습
페이지 검색 툴에서 콘솔로 들어가 보면 데이터가 잘 넘어온 것을 확인할 수 있습니다.
다음은 넘어온 데이터를 직접 사용해보겠습니다.
useState를 이용합니다.
먼저 import 해줍시다.
import {useState} from 'react';
const [todoList, setTodoList] = useState(null);
fetch('http://localhost:4000/api/todo')
.then((response) => response.json())
.then((data) => setTodoList(data));
넘어온 데이터를 useState를 이용해서 가져옵니다.
하지만 이렇게 작성하면 무한로프를 돌면서 에러가 발생됩니다.
계속 랜더링 되지 않게 useEffect를 써주면 해결될 것입니다.
import {useEffect, useState} from 'react';
function App() {
const [todoList, setTodoList] = useState(null);
useEffect(()=>{
fetch('http://localhost:4000/api/todo')
.then((response) => response.json())
.then((data) => setTodoList(data));
},[]
);
return (
<div className="App">
<h1>Todolist</h1>
{todoList.map((todo) => (
<div key = {todo.id}>
<div>{todo.id}</div>
<div>{todo.text}</div>
<div> {todo.done ? 'done' : 'not done'}</div>
</div>
))}
</div>
);
}
export default App;
이렇게 맵함수를 써서 화면에 출력한는 코드를 짜줍니다.
하지만 맵함수에서 에러가 뜨네요.
찾아보니까 리액트는 랜더링이 화면에 커밋된 후에야 모든 효과를 실행해서 첫턴에 데이터가 안들어와도
렌더링이 실행되며 데이터가 없기 떄문에 에러가 나는 것이라네요.
해결법은
{todoList && todoList.map((todo) => (
<div key = {todo.id}>
<div>{todo.id}</div>
<div>{todo.text}</div>
<div> {todo.done ? 'done' : 'not done'}</div>
</div>
))}
map함수에서 데이터를 앞에 붙이고 뒤에 &&를 붙여도 되고
하드코딩으로 초기값을 설정해줘도 해결됩니다.
Postman으로 post한 데이터도 잘 출력되는 모습입니다.
그 다음은 데이터를 출력하는 창을 만들어 보겠습니다.
import {useEffect, useState} from 'react';
function App() {
const [todoList, setTodoList] = useState(null);
useEffect(()=>{
fetch('http://localhost:4000/api/todo')
.then((response) => response.json())
.then((data) => setTodoList(data));
},[]
);
const onSubmitHandler = (e) =>{
e.preventDefault();
const text = e.target.text.value;
const done = e.target.done.value;
fetch('http://localhost:4000/api/todo',{
method : 'POST',
body : JSON.stringify({
text,
done,
}),
})
}
return (
<div className="App">
<h1>Todolist</h1>
<form onSubmit={onSubmitHandler}>
<input name = 'text' />
<input name = 'done' type = 'checkbox'/>
<input type = 'submit' value = '추가'/>
</form>
{todoList && todoList.map((todo) => (
<div key = {todo.id}>
<div>{todo.id}</div>
<div>{todo.text}</div>
<div> {todo.done ? 'done' : 'not done'}</div>
</div>
))}
</div>
);
}
export default App;
input태그와 onSubmitHandler를 만들어서 값을 입력하면 그 값을 추가할 수 있도록 해줍니다.
fetch('http://localhost:4000/api/todo',{
method : 'POST',
body : JSON.stringify({
text,
done,
}),
이때 입력한 값을 서버로 json형태로 직렬화 해서 포스트 요청을 합니다.
데이터 추가 전
데이터 추가 후
데이터가 추가되었지만 text와 done이 잘 넘어오지 않았습니다.
fetch('http://localhost:4000/api/todo',{
headers : {
'Content-Type': 'application/json',
},
method : 'POST',
body : JSON.stringify({
text,
done,
}),
})
}
fetch에서 헤더에 Content Type을 추가하면 해결됩니다.
display : flex 까지 추가한 모습입니다.
import {useEffect, useState} from 'react';
function App() {
const fetchData = ()=>{
fetch('http://localhost:4000/api/todo')
.then((response) => response.json())
.then((data) => setTodoList(data))
};
const [todoList, setTodoList] = useState(null);
useEffect(()=>{
fetchData()
},[]
);
const onSubmitHandler = (e) =>{
e.preventDefault();
const text = e.target.text.value;
const done = e.target.done.checked;
fetch('http://localhost:4000/api/todo',{
headers : {
'Content-Type': 'application/json',
},
method : 'POST',
body : JSON.stringify({
text,
done,
}),
}).then(()=>
fetchData()
);
}
return (
<div className="App">
<h1>Todolist</h1>
<form onSubmit={onSubmitHandler}>
<input name = 'text' />
<input name = 'done' type = 'checkbox'/>
<input type = 'submit' value = '추가'/>
</form>
{todoList && todoList.map((todo) => (
<div key = {todo.id} style = {{display : 'flex'
}}>
<div>{todo.id}</div>
<div>{todo.text}</div>
<div> {todo.done ? 'done' : 'not done'}</div>
</div>
))}
</div>
);
}
export default App;
fetch를 이용한 최종 코드
import {useEffect, useState} from 'react';
import axios from 'axios';
function App() {
const fetchData = async()=>{
const response = await axios.get('http://localhost:4000/api/todo');
setTodoList(response.data);
};
const [todoList, setTodoList] = useState(null);
useEffect(()=>{
fetchData()
},[]
);
const onSubmitHandler = async(e) =>{
e.preventDefault();
const text = e.target.text.value;
const done = e.target.done.checked;
await axios.post('http://localhost:4000/api/todo', {text,done});
fetchData();
};
return (
<div className="App">
<h1>Todolist</h1>
<form onSubmit={onSubmitHandler}>
<input name = 'text' />
<input name = 'done' type = 'checkbox'/>
<input type = 'submit' value = '추가'/>
</form>
{todoList && todoList.map((todo) => (
<div key = {todo.id} style = {{display : 'flex'
}}>
<div>{todo.id}</div>
<div>{todo.text}</div>
<div> {todo.done ? 'done' : 'not done'}</div>
</div>
))}
</div>
);
}
export default App;
axios를 이용한 코드
확실히 axios를 이용하는 것이 현명할 것 같네요!
좋은 공부가 된 것 같습니다.
감사합니다!
