import { useEffect, useState } from "react";
import "./App.css";
function App() {
//가져온 데이터 저장할 state
const [data, setData] = useState();
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((res) => res.json())
.then((json) => {
return setData([...json]); //스프레드 연산자 사용해 새로운 객체 넣어줌
});
}, []);
return (
<>
<div>비동기 연습</div>
<div>
{data.map((user) => (
<ul key={user.id} style={{ border: "1px solid black" }}>
<li>{user.userId}</li>
<li>{user.title}</li>
<li>{user.body}</li>
</ul>
))}
</div>
</>
);
}
export default App;
이렇게 작성하면 오류가 발생한다.
Uncaught TypeError: Cannot read properties of undefined (reading 'map')
리액트 컴포넌트에서 상태를 초기화할 때 초기값을 설정하지 않으면 에러가 발생할 수 있다. 현재 배열 메서드인 map을 사용하고 있는데 이 때문에 문제가 발생한다.
const [data, setData] = useState();
import { useEffect, useState } from "react";
import "./App.css";
function App() {
const [data, setData] = useState();
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((res) => res.json())
.then((json) => {
return setData([...json]);
});
}, []);
return (
<>
<div>비동기 연습</div>
<div>
{data ? (
<div>
{data.map((user) => (
<ul key={user.id} style={{ border: "1px solid black" }}>
<li>{user.userId}</li>
<li>{user.title}</li>
<li>{user.body}</li>
</ul>
))}
</div>
) : (
<p>Loading...</p>
)}
</div>
</>
);
}
export default App;
초기값으로 빈 배열을 할당하는 것 만으로도 map 메서드 사용시 오류가 발생하지 않도록 할 수 있다.
const [data, setData] = useState([]);
data && data.map
와 같은 조건을 달 필요가 없다.useState([])
로 빈 배열을 초기값으로 설정하면, 데이터가 로드되기 전에 map 메서드를 안전하게 호출할 수 있다.import { useEffect, useState } from "react";
import "./App.css";
function App() {
//가져온 데이터 저장할 state
const [data, setData] = useState([]); // 초기값 설정
useEffect(() => {
// 마운트가 될 때, 서버로부터 데이터 받아서 컴포넌트에서 뿌려보자
const getData = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
const data = await response.json();
setData([...data]); //새로운 값 들어왔으니 전개 연산자 써서 새로운 객체 만들어 넣기
};
getData(); // 컴포넌트 마운트 시 데이터 fetch
}, []);
return (
<>
<div>비동기 연습</div>
<div>
{data.map((user) => (
<ul key={user.id} style={{ border: "1px solid black" }}>
<li>{user.userId}</li>
<li>{user.title}</li>
<li>{user.body}</li>
</ul>
))}
</div>
</>
);
}
export default App;
데이터가 로딩 중일 때 사용자에게 로딩 메시지를 표시하면 더 나은 사용자 경험 제공
에러가 발생했을 때 사용자에게 에러 메시지를 표시하면 더 나은 사용자 경험 제공
코드의 가독성을 높이기 위해 데이터 표시 부분을 별도의 컴포넌트로 분리할 수 있음
import { useEffect, useState } from "react";
import "./App.css";
// DataList 컴포넌트로 분리
const DataList = ({ data }) => (
<div>
{data.map((user) => (
<ul key={user.id} style={{ border: "1px solid black" }}>
<li>{user.userId}</li>
<li>{user.title}</li>
<li>{user.body}</li>
</ul>
))}
</div>
);
function App() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true); // 로딩 상태 추가
const [error, setError] = useState(null); // 에러 상태 추가
useEffect(() => {
const getData = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
if (!response.ok) {
throw new Error("네트워크 응답이 실패했습니다.");
}
const data = await response.json();
setData([...data]);
setLoading(false); // 로딩 완료
} catch (error) {
setError(error.message); // 에러 상태 설정
setLoading(false); // 로딩 완료
}
};
getData();
}, []);
if (loading) {
return <p>Loading...</p>; // 로딩 중인 경우
}
if (error) {
return <p>에러 발생: {error}</p>; // 에러가 발생한 경우
}
return (
<>
<div>비동기 연습</div>
<DataList data={data} /> {/* DataList 컴포넌트 사용 */}
</>
);
}
export default App;
fetch로 받은 데이터가 이미 새로운 객체이므로, 전개 연산자를 사용하지 않아도 문제는 없다.
하지만 항상 불변성을 유지하는 패턴을 따르는 것이 좋다.
원본 데이터를 변경하지 않는다는 것을 명시적으로 나타내기 위해 전개 연산자를 사용하는 것은 좋은 습관이다.
useEffect(() => {
const getData = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
if (!response.ok) {
throw new Error("네트워크 응답이 실패했습니다.");
}
const data = await response.json();
setData([...data]);
};
getData();
}, []);
항상 불변성을 유지하는 패턴을 따르면, 상태 관리 로직이 일관되어 유지보수성향상
불변성을 유지하면 상태 변경이 예상치 못하게 발생하지 않아 디버깅 쉬워짐
React는 상태가 변경될 때 컴포넌트를 다시 렌더링하는데, 불변성을 유지하면 변경된 부분만 다시 렌더링할 수 있기 때문에 성능 최적화
특정 데이터를 업데이트하거나 삭제할 때 전개 연산자를 사용하여 불변성을 유지하는 것은 반드시 필요하다.
//id에 해당하는 항목만 찾아 업데이트하고, 나머지 항목은 변경하지 않기
const updateData = (id, newData) => {
setData((prevData) =>
prevData.map((item) => (item.id === id ? { ...item, ...newData } : item))
);
};
//id에 해당하지 않는 항목만 새로운 배열로 반환
//즉, id에 해당하는 값을 제외한 나머지 항목만 유지한 배열 반환
const deleteData = (id) => {
setData((prevData) => prevData.filter((item) => item.id !== id));
};
서버에서 받아온 데이터를 초기화할 때는 데이터 불변성을 엄격히 유지할 필요는 없지만, 일관성을 위해 전개 연산자를 사용하여 명시적으로 데이터가 불변함을 나타낼 수 있다.
특정 데이터를 업데이트하거나 삭제할 때는 전개 연산자를 사용하여 불변성을 유지하는 것이 중요하다.
항상 불변성을 유지하는 패턴을 따르면 코드의 일관성, 디버깅 용이성, 성능 최적화에 도움이 된다.