본문은 라매개발자 강의 프론트에서 서버에 데이터 요청하는 방법 (React로 fetch, axios 사용하기)를 기반으로 작성되었습니다.
지난 글에서 클라이언트가 받아온 서버 데이터를 console.log로 확인했다.
이번에는 서버 데이터를 화면에 띄워 보자.
useState는 state 값 변화가 생기면 리렌더링해서 화면에 새로운 값을 보여준다.
useState를 써서 서버 데이터에 따라 화면이 달라지도록 만들어보자.
console.log로 보여줬던 데이터를 setState의 파라미터로 넘겨준다.
function App() {
const [todoList, setTodoList] = useState(null);
fetch('http://localhost:4000/api/todo')
.then((res)=>res.json())
.then((data)=>setTodoList(data));
//이하 생략
그런데 이렇게 하면 무한리렌더링 지옥에 갇힌다.
(영상에서는 갇힌다... 나도 전에 갇혔었다... 근데 왜 지금은 안 갇히지??)
무한 리렌더링이 되는 이유를 알기 위해서는 useState의 작동원리에 대해 알아야 한다.
문제의 코드는 여기다.
function App() {
const [todoList, setTodoList] = useState(null);
fetch('http://localhost:4000/api/todo')
.then((res)=>res.json())
.then((data)=>setTodoList(data));
return(
//생략
리액트는 컴포넌트를 최초로 렌더링했을 때 코드를 한 줄씩 실행한다.
첫 실행때 useState를 만나 state를 만든다.
그리고 fetch 함수로 들어가는데, 서버로부터 응답을 받기까지 시간이 조금 걸린다.
함수 실행이 끝나기 전에 return문으로 와서 화면을 보여준다.
fetch가 끝났다. setTodoList를 해 준다.
어? 근데 state가 바뀌었다. 리렌더링을 해줘야 한다.
그러면 코드가 처음부터 다시 실행된다.
✨ 정리
첫 렌더링 ➡ useState(null)
➡ fetch(), 서버 응답 대기 중 return()
➡ 서버 응답 완료 후 setTodoList(data)
➡ state 변화로 인한 리렌더링, 처음부터 다시 실행
➡ fetch(), 서버 응답 대기 중 return()
...
무한반복
고치기 위해서는 딱 한 번만 실행되도록 하면 되는데 이게 useEffect이다.
useEffect는 컴포넌트가 처음 나타날 때 (mount), 컴포넌트가 사라질 때 (unmount), 컴포넌트가 업데이트 될 때 (re-render)를 관리한다.
(useEffect에 관해 전에 정리했던 게시물도 있다... 참고!)
지금 필요한 것은 컴포넌트가 처음 나타날 때(mount시)에만 fetch시키는 것.
코드를 이렇게 수정해 준다.
function App() {
const [todoList, setTodoList] = useState(null);
useEffect(() => {
fetch('http://localhost:4000/api/todo')
.then((res)=>res.json())
.then((data)=>setTodoList(data));
}, [])
return (
//이하생략
또다른 에러가 있다...
state 초기값이 null이라서 이러는거 같다.
구글신께 여쭤보니 null 대신 초기값의 자료형을 알려주면 된다고 한다.
useState를 null로 초기화했던 부분을 바꿔주자.
데이터는 배열로 넘어오니까 빈 배열로 초기화한다.
const [todoList, setTodoList] = useState([]);
그럼 다시 잘 돌아간다.
그런데 영상 조금 더 보면... 해결법을 알려주신다.
todoList가 null일때는 실행하지 않도록 하는 것이다.
map 메소드 앞에 물음표를 넣어 준다.
{todoList?.map((todo)=>(
이러면 또 잘 돌아간다.
data는 배열 형식이니 map 메소드로 하나씩 화면에 띄워준다.
map은 배열 안 각각의 element에 대해 실행된다.
return 문 안을 아래와 같이 수정해준다.
return (
<div className="App">
<h1>TODO LIST</h1>
{todoList.map((todo)=>(
<div>
<div>{todo.id}</div>
<div>{todo.text}</div>
<div>{todo.done ? 'Y' : 'N'}</div>
</div>
))}
</div>
);
화면에 서버에서 넘긴 데이터가 잘 뜬다면 성공!
client/App.js
import React from "react";
import {useState,useEffect} from "react";
function App() {
const [todoList, setTodoList] = useState(null);
useEffect(() => {
fetch('http://localhost:4000/api/todo')
.then((res)=>res.json())
.then((data)=>setTodoList(data));
}, [])
return (
<div className="App">
<h1>TODO LIST</h1>
{todoList?.map((todo)=>(
<div>
<div>{todo.id}</div>
<div>{todo.text}</div>
<div>{todo.done ? 'Y' : 'N'}</div>
</div>
))}
</div>
);
}
export default App;