React에서 비동기 작업(API 호출, Promise, async/await)을 다룰 때 자주 발생하는 오류들을 정리했습니다. 각 오류의 원인, 오류 메시지, 해결방법을 구체적인 코드 예제와 함께 설명합니다.
📋 목차
1. ⚠️ 언마운트된 컴포넌트에서 상태 업데이트 오류
❌ 오류 메시지
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
🔍 원인 설명
컴포넌트가 언마운트된 후에도 비동기 작업(API 호출 등)이 완료되어 상태를 업데이트하려고 할 때 발생합니다. 이는 메모리 누수로 이어질 수 있습니다.
❌ 문제가 있는 코드
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// 비동기 작업
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
// 컴포넌트가 언마운트된 후에도 실행될 수 있음
setUser(data); // ⚠️ 오류 발생 가능
});
}, [userId]);
return
;
}
✅ 해결방법 1: cleanup 함수 사용
import { useState, useEffect, useRef } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const isMountedRef = useRef(true);
useEffect(() => {
isMountedRef.current = true;
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
// 컴포넌트가 마운트되어 있을 때만 상태 업데이트
if (isMountedRef.current) {
setUser(data);
}
});
// cleanup 함수
return () => {
isMountedRef.current = false;
};
}, [userId]);
return
;
}
✅ 해결방법 2: AbortController 사용
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const abortController = new AbortController();
fetch(`/api/users/${userId}`, {
signal: abortController.signal
})
.then(res => res.json())
.then(data => setUser(data))
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});
// cleanup 함수에서 요청 취소
return () => {
abortController.abort();
};
}, [userId]);
return
;
}
2. ⚠️ 비동기 데이터 접근 오류 (undefined 읽기)
❌ 오류 메시지
TypeError: Cannot read property 'name' of undefined
TypeError: Cannot read properties of undefined (reading 'map')
🔍 원인 설명
비동기 작업이 완료되기 전에 데이터에 접근하려고 할 때 발생합니다. 초기 렌더링 시 데이터가 null 또는 undefined인 상태에서 속성에 접근하면 오류가 발생합니다.
❌ 문제가 있는 코드
import { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState(null);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => setUsers(data));
}, []);
// users가 null일 때 map() 호출 시 오류 발생
return (
- {users.map(user => ( // ⚠️ TypeError 발생
- {user.name} ))}
);
}
✅ 해결방법 1: 옵셔널 체이닝과 기본값 사용
import { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState(null);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => setUsers(data));
}, []);
// 옵셔널 체이닝과 기본값 사용
return (
- {(users || []).map(user => (
- {user.name} ))}
);
}
✅ 해결방법 2: 조건부 렌더링
import { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
});
}, []);
if (loading) {
return
;
}
if (!users || users.length === 0) {
return
;
}
return (
- {users.map(user => (
- {user.name} ))}
);
}
3. ⚠️ 무한 업데이트 루프 오류
❌ 오류 메시지
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
🔍 원인 설명
useEffect의 의존성 배열이 잘못 설정되어 무한 루프가 발생합니다. 또는 상태 업데이트가 다시 렌더링을 트리거하여 계속 반복되는 경우입니다.
❌ 문제가 있는 코드
import { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
useEffect(() => {
// data가 변경될 때마다 실행
fetch('/api/data')
.then(res => res.json())
.then(result => {
setData(result); // data 변경
setCount(count + 1); // count 변경
});
}, [data, count]); // ⚠️ data와 count가 의존성에 포함되어 무한 루프 발생
return
;
}
✅ 해결방법 1: 의존성 배열 수정
import { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(result => {
setData(result);
setCount(prev => prev + 1); // 함수형 업데이트 사용
});
}, []); // ✅ 빈 배열로 마운트 시 한 번만 실행
return
;
}
✅ 해결방법 2: useCallback 사용
import { useState, useEffect, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
const fetchData = useCallback(() => {
fetch('/api/data')
.then(res => res.json())
.then(result => {
setData(result);
setCount(prev => prev + 1);
});
}, []); // 의존성 없음
useEffect(() => {
fetchData();
}, [fetchData]);
return
;
}
4. ⚠️ Promise 객체를 직접 렌더링하는 오류
❌ 오류 메시지
Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
🔍 원인 설명
Promise 객체를 직접 JSX에 렌더링하려고 할 때 발생합니다. React는 Promise 객체를 렌더링할 수 없습니다.
❌ 문제가 있는 코드
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const fetchUser = async () => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
};
// Promise 객체를 직접 반환
const userPromise = fetchUser(); // ⚠️ Promise 객체
return (
);
}
✅ 해결방법: useEffect에서 Promise 처리
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUser = async () => {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data); // ✅ Promise 결과를 상태에 저장
} catch (error) {
console.error('Error fetching user:', error);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return
;
if (!user) return
;
return (
);
}
5. ⚠️ useEffect 의존성 배열 경고
❌ 오류 메시지
React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.
🔍 원인 설명
useEffect 내부에서 사용하는 함수나 변수가 의존성 배열에 포함되지 않아 발생하는 경고입니다. 이는 오래된 클로저 문제를 일으킬 수 있습니다.
❌ 문제가 있는 코드
function UserList({ userId }) {
const [users, setUsers] = useState([]);
const fetchData = async () => {
const response = await fetch(`/api/users?userId=${userId}`);
const data = await response.json();
setUsers(data);
};
useEffect(() => {
fetchData();
}, []); // ⚠️ fetchData와 userId가 의존성에 없음
return
;
}
✅ 해결방법 1: useCallback 사용
import { useState, useEffect, useCallback } from 'react';
function UserList({ userId }) {
const [users, setUsers] = useState([]);
const fetchData = useCallback(async () => {
const response = await fetch(`/api/users?userId=${userId}`);
const data = await response.json();
setUsers(data);
}, [userId]); // ✅ userId를 의존성에 포함
useEffect(() => {
fetchData();
}, [fetchData]); // ✅ fetchData를 의존성에 포함
return
;
}
✅ 해결방법 2: useEffect 내부에서 함수 정의
import { useState, useEffect } from 'react';
function UserList({ userId }) {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(`/api/users?userId=${userId}`);
const data = await response.json();
setUsers(data);
};
fetchData();
}, [userId]); // ✅ userId를 의존성에 포함
return
;
}
6. ⚠️ Promise 체이닝 오류
❌ 오류 메시지
TypeError: Cannot read property 'then' of undefined
🔍 원인 설명
Promise를 반환하지 않는 함수에 .then()을 호출하거나, 비동기 함수에서 값을 반환하지 않아 발생합니다.
❌ 문제가 있는 코드
function fetchUserData(userId) {
fetch(`/api/users/${userId}`)
.then(res => res.json())
// ⚠️ return이 없어서 undefined 반환
}
function UserProfile({ userId }) {
useEffect(() => {
fetchUserData(userId)
.then(data => { // ⚠️ undefined에 .then() 호출
console.log(data);
});
}, [userId]);
return
;
}
✅ 해결방법 1: return 추가
function fetchUserData(userId) {
return fetch(`/api/users/${userId}`) // ✅ return 추가
.then(res => res.json());
}
function UserProfile({ userId }) {
useEffect(() => {
fetchUserData(userId)
.then(data => {
console.log(data);
});
}, [userId]);
return
;
}
✅ 해결방법 2: async/await 사용
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
return response.json(); // ✅ Promise 반환
}
function UserProfile({ userId }) {
useEffect(() => {
const loadData = async () => {
const data = await fetchUserData(userId);
console.log(data);
};
loadData();
}, [userId]);
return
;
}
7. ⚠️ Promise 에러 처리 누락
❌ 오류 메시지
Uncaught (in promise) Error: Failed to fetch
🔍 원인 설명
Promise의 에러를 처리하지 않아 발생합니다. 네트워크 오류나 서버 오류 시 사용자에게 적절한 피드백을 제공하지 못합니다.
❌ 문제가 있는 코드
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
// ⚠️ .catch()가 없어서 에러 처리 안 됨
}, [userId]);
return
;
}
✅ 해결방법 1: .catch() 추가
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => {
if (!res.ok) {
throw new Error('Failed to fetch user');
}
return res.json();
})
.then(data => setUser(data))
.catch(err => {
setError(err.message); // ✅ 에러 처리
console.error('Error:', err);
});
}, [userId]);
if (error) return
;
if (!user) return
;
return
;
}
✅ 해결방법 2: try-catch 사용 (async/await)
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUser = async () => {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const data = await response.json();
setUser(data);
setError(null);
} catch (err) {
setError(err.message); // ✅ 에러 처리
console.error('Error:', err);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return
;
if (error) return
;
if (!user) return
;
return
;
}
✅ 마무리
React에서 비동기 작업을 다룰 때 발생하는 주요 오류 7가지를 정리했습니다. 각 오류의 원인과 해결방법을 이해하면 더 안정적인 React 애플리케이션을 개발할 수 있습니다.
주요 포인트:
- cleanup 함수 사용: 언마운트된 컴포넌트에서 상태 업데이트 방지
- 조건부 렌더링: 비동기 데이터 접근 전 null 체크
- 의존성 배열 관리: useEffect 의존성 올바르게 설정
- 에러 처리: 모든 Promise에 .catch() 또는 try-catch 추가
- 로딩 상태 관리: 비동기 작업 중 로딩 상태 표시
💡 참고: React 18부터는 자동 배치(Automatic Batching)가 개선되어 여러 상태 업데이트가 자동으로 배치됩니다. 또한 Suspense와 함께 사용하면 비동기 데이터 로딩을 더 효율적으로 처리할 수 있습니다.

카카오톡 오픈채팅 링크
https://open.kakao.com/o/seCteX7h
'개발 > FRONT' 카테고리의 다른 글
| ⚠️ Flutter 비동기 오류 setState() called after dispose(),A build function returned null,Looking up a deactivated widget's ancestor is unsafe. 해결방법 (0) | 2025.12.27 |
|---|---|
| Info.plist 개인정보 사용 목적 누락(ITMS-90683) 오류 해결방법 (1) | 2025.12.23 |
| 웹/하이브리드 앱에서 PDF 생성 후 다운로드하기 (0) | 2025.12.23 |
| Capacitor 환경에서 Google 광고 AdMob 붙이기 (0) | 2025.12.23 |
| npm cache 때문에 수정이 적용되지 않을 때 해결방법 npmcache 삭제 (0) | 2025.12.23 |