목차
[리액트] REST API, AXIOS로 받아온 Array 객체의 map 호출 에러 해결하기
이번 포스팅에서는 React와 Axios를 사용하여 REST API로부터 데이터를 받아올 때 발생하는 “map 함수 호출 불가” 에러에 대해 심도 있게 다루어보겠습니다. 개발 과정에서 API 호출 결과가 정상적으로 formdata에 저장되는 것은 확인되었지만, 화면의 특정 div 영역에서 데이터를 꺼내 렌더링하려고 할 때 에러가 발생한 경험이 있으신 분들이라면 이번 글이 큰 도움이 될 것입니다. 특히, 비동기 처리 과정에서 데이터가 완전히 준비되기 전에 렌더링이 시도되어 발생하는 문제를 어떻게 해결할 수 있는지 구체적인 코드 예시와 함께 설명드리겠습니다.
도입부: 문제 상황과 발생 원인
React 애플리케이션에서 REST API를 호출할 때, Axios와 같은 라이브러리를 사용하면 비동기적으로 데이터를 받아올 수 있습니다. API 호출 후 받은 데이터를 state에 저장하고 이를 map 함수를 사용해 반복 렌더링하는 경우가 많은데, 이때 “map 함수 호출 불가”라는 에러가 발생하는 경우가 있습니다.
실제로 formdata에 정상적으로 데이터가 저장되는 것을 확인하였음에도, 화면의 div 내부에서 해당 객체를 꺼내 사용하려고 하면 아래와 같은 에러 메시지가 뜨는 경우가 있습니다.
TypeError: Cannot read property 'map' of null
또는
TypeError: data.map is not a function
이러한 에러는 대부분 비동기 데이터 호출 과정에서 데이터가 아직 준비되지 않은 상태(예: null 또는 Promise 객체가 임시로 존재하는 상태)에서 map 함수를 호출하려고 시도할 때 발생합니다.
예를 들어, API 호출 결과가 Promise 객체의 pending 상태에 있을 때, 해당 데이터를 바로 렌더링하려고 하면 React는 아직 완전히 해결되지 않은 데이터를 대상으로 map 함수를 실행하려 하므로 위와 같은 에러가 발생하게 됩니다.
한 시간 이상 삽질한 후, 결국 해결책은 매우 간단하다는 사실에 놀라게 되기도 합니다.
문제 원인 분석: 비동기 데이터와 렌더링 타이밍의 문제
React 컴포넌트는 렌더링 시점에 현재 state 값을 사용하여 화면을 구성합니다. 만약 컴포넌트가 마운트되면서 비동기 요청을 보내고, 이 요청이 완료되기 전에 컴포넌트가 렌더링된다면, state에 저장된 데이터는 초기값(대부분 null 혹은 빈 배열)일 수 있습니다.
특히, 다음과 같은 상황에서 문제가 발생할 수 있습니다.
- 초기 state 값이 null인 경우:
데이터가 아직 도착하지 않은 상태에서 map 함수를 호출하면, null에는 map 함수가 정의되어 있지 않으므로 에러가 발생합니다. - Promise 객체가 일시적으로 존재하는 경우:
비동기 함수를 호출할 때, 데이터가 실제 값으로 바뀌기 전에 Promise 객체가 잠시 존재하는 경우에도 map 함수를 호출하려고 시도하면 문제가 발생할 수 있습니다.
따라서, 데이터를 렌더링하기 전에 “데이터 준비 완료” 여부를 명확히 확인하는 로직이 필요합니다. 이를 위해 React에서는 흔히 로딩 상태를 관리하는 별도의 state 변수를 사용합니다.
해결 방법: 로딩 상태 관리와 조건부 렌더링
위와 같은 문제를 해결하기 위한 가장 간단하고 효과적인 방법은 로딩 상태(loading state)를 명시적으로 관리하는 것입니다.
즉, 데이터가 완전히 로드되어 사용할 준비가 되었는지를 확인할 수 있도록 별도의 state 변수를 도입합니다. 예를 들어, 다음과 같이 useState를 활용할 수 있습니다.
const [data, setData] = useState(null);
const [showList, setShowList] = useState(false);
위 코드에서 data
는 API 호출 결과를 저장하는 state이며, showList
는 데이터 로딩이 완료되었는지를 나타내는 불리언 값입니다.
비동기 함수 내부에서 데이터를 받아오고 나면 아래와 같이 처리할 수 있습니다.
useEffect(() => {
async function fetchData() {
try {
const response = await axios.get('https://api.example.com/data');
setData(response.data);
setShowList(true); // 데이터가 완전히 로드되었음을 표시
} catch (error) {
console.error('데이터를 가져오는 중 에러 발생:', error);
}
}
fetchData();
}, []);
이처럼 데이터가 성공적으로 받아지면 setShowList(true)
를 호출하여 로딩이 끝났음을 React 컴포넌트에게 알려줍니다.
그 후, JSX에서는 삼항 연산자를 사용하여 로딩 상태에 따라 다른 컴포넌트를 렌더링할 수 있습니다.
예를 들어, showList
가 true일 때만 data를 map 함수로 순회하여 렌더링하도록 조건부 렌더링을 구현하면 다음과 같이 작성할 수 있습니다.
return (
<div>
{showList ? (
<ul>
{data.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
) : (
<p>로딩 중입니다...</p>
)}
</div>
);
이 코드는 showList
가 false인 경우 “로딩 중입니다…”라는 메시지를 화면에 보여주며, 데이터가 완전히 준비되었을 때만 리스트를 렌더링합니다.
이와 같이 조건부 렌더링을 적용하면, 데이터가 아직 null이거나 Promise 객체 상태에 있을 때 map 함수를 호출하는 오류를 방지할 수 있습니다.
실제 코드 구현 예시
아래는 위에서 설명한 로딩 상태 관리를 포함한 전체적인 React 컴포넌트 코드 예시입니다. 이 코드는 Axios를 이용하여 API 데이터를 받아오고, 로딩 상태에 따라 올바르게 렌더링하는 방법을 보여줍니다.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function DataList() {
// 초기 state 설정: data는 null, 로딩 상태는 false
const [data, setData] = useState(null);
const [showList, setShowList] = useState(false);
useEffect(() => {
// 비동기 함수 정의
async function fetchData() {
try {
// API 호출 (REST API에서 데이터 받아오기)
const response = await axios.get('https://api.example.com/data');
// 받아온 데이터를 state에 저장
setData(response.data);
// 데이터 로딩 완료 표시
setShowList(true);
} catch (error) {
console.error('데이터를 가져오는 중 에러 발생:', error);
}
}
// 컴포넌트 마운트 시 API 호출
fetchData();
}, []);
return (
<div>
{showList ? (
// 데이터가 준비되었을 때만 map 함수를 사용하여 렌더링
<ul>
{data && data.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
) : (
// 데이터가 준비되지 않은 경우 로딩 메시지 출력
<p>로딩 중입니다...</p>
)}
</div>
);
}
export default DataList;
이 예시에서는 API 호출 결과가 비동기적으로 처리되는 동안 발생할 수 있는 문제를 해결하기 위해 두 가지 주요 전략을 사용했습니다.
- 로딩 상태 관리:
showList
state를 통해 데이터가 완전히 로드되었는지 확인합니다. 데이터가 도착하면setShowList(true)
를 호출하여 화면에 데이터를 렌더링하도록 합니다. - 조건부 렌더링:
JSX 내에서 삼항 연산자를 사용하여showList
가 true일 때만 데이터를 map 함수로 순회하며 렌더링합니다. 이를 통해 데이터가 null인 상태에서 map 함수를 호출하는 오류를 미연에 방지합니다.
이러한 방법은 비동기 처리와 관련된 문제뿐만 아니라, 사용자에게 로딩 상태를 명확하게 전달하여 UX를 향상시키는 효과도 있습니다.
구현 시 고려 사항 및 추가 팁
1. 초기 state 값의 중요성
초기 state 값을 null 대신 빈 배열([]
)로 설정하는 방법도 있습니다. 만약 API에서 항상 배열 형태의 데이터를 반환하는 것이 보장된다면, 초기 state를 빈 배열로 설정하면 map 함수를 호출할 때 에러가 발생하지 않습니다. 그러나 데이터가 아직 준비되지 않았음을 사용자에게 명확히 전달하기 위해 로딩 상태를 별도로 관리하는 것이 더 좋은 방법입니다.
// 초기 state를 빈 배열로 설정하는 예시
const [data, setData] = useState([]);
하지만 이 경우, 데이터가 없는 상태와 로딩 중인 상태를 구분하기 어려워질 수 있으므로, 별도의 로딩 상태를 관리하는 것이 바람직합니다.
2. 에러 처리
비동기 함수 내부에서 try-catch 구문을 사용하여 에러를 처리하는 것은 매우 중요합니다. API 호출 중 에러가 발생할 경우, 사용자에게 적절한 에러 메시지를 전달하거나 fallback UI를 제공하는 방법도 고려해야 합니다.
try {
const response = await axios.get('https://api.example.com/data');
setData(response.data);
setShowList(true);
} catch (error) {
console.error('데이터를 가져오는 중 에러 발생:', error);
// 에러 발생 시 별도의 에러 state를 관리하여 사용자에게 안내할 수 있습니다.
}
3. 삼항 연산자 외 조건부 렌더링 방식
삼항 연산자 외에도 논리 연산자(&&
)를 사용하여 조건부 렌더링을 구현할 수 있습니다. 다만, 삼항 연산자는 로딩 상태와 완료 상태를 명확하게 구분하여 처리할 수 있기 때문에 이 경우에 더 적합합니다.
return (
<div>
{showList && (
<ul>
{data.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
)}
{!showList && <p>로딩 중입니다...</p>}
</div>
);
4. 사용자 경험(UX) 개선
로딩 상태를 관리하는 것 외에도, 데이터를 불러오는 동안 스피너나 애니메이션 효과를 추가하여 사용자 경험을 향상시킬 수 있습니다. 예를 들어, CSS 애니메이션이나 외부 라이브러리를 사용하여 로딩 중임을 시각적으로 전달하면 좋습니다.
마무리 및 결론
React와 Axios를 사용하여 REST API로부터 데이터를 받아올 때, 비동기 처리의 특성상 데이터가 완전히 로드되기 전에 컴포넌트가 렌더링되면서 “map 함수 호출 불가”와 같은 에러가 발생할 수 있습니다. 이러한 문제는 주로 초기 state 값이 null이거나 아직 Promise 객체의 상태에 있을 때 발생합니다.
해결 방법으로는 로딩 상태를 명시적으로 관리하고, 조건부 렌더링을 통해 데이터가 완전히 준비되었을 때만 map 함수를 호출하도록 하는 것입니다.
포스팅에서 소개한 예시 코드를 참고하여, 여러분의 React 프로젝트에서도 데이터 로딩 상태를 효과적으로 관리하고 안정적인 UI 렌더링을 구현하시기 바랍니다.
이 글을 통해 비동기 데이터 처리와 조건부 렌더링에 관한 기본 원리와 실무 적용 방법을 익힐 수 있기를 바랍니다. 복잡해 보이는 문제일 수 있으나, 로딩 상태를 적절히 관리하는 것만으로도 많은 오류를 미연에 방지할 수 있으니 꼭 적용해보세요. 또한, 에러 처리와 사용자 경험 개선을 위한 추가 팁들도 함께 고려하여 보다 완성도 높은 애플리케이션을 개발하시길 추천드립니다.
'Dev > reactJS' 카테고리의 다른 글
React 실행 오류: npm ERR! errno -4058 해결 방법 (0) | 2023.11.22 |
---|---|
리액트 쿼리 v5 에서 바뀐 점 react query v5 (0) | 2023.11.16 |
리액트 프로젝트의 폴더 구조 (0) | 2023.10.30 |
리액트 초기 세팅 환경 구축하기: Windows에서의 React 설치 가이드 (0) | 2023.08.22 |
javascript classList.toggle로 jquery addClass, removeClass, toggleClass 구현하기 / 더보기 버튼 클릭시 영역 확장 (0) | 2022.11.03 |
댓글