React 앱에서 오류 처리 UX 제대로 설계하기 – 사용자 피드백, 에러 경계, 상태 복구까지 완벽 전략
프론트엔드 개발에서 '에러'는 피할 수 없는 존재입니다. 하지만 중요한 건, 에러가 났을 때 사용자가 얼마나 편하게 복구할 수 있느냐죠.
사용자가 앱을 이용하는 중에 갑자기 흰 화면이 뜨거나, "무슨 문제가 발생했습니다" 한 줄만 덩그러니 나온다면 그 서비스에 대한 신뢰는 급락합니다.
이번 글에서는 React 앱에서 발생하는 다양한 오류를 어떻게 감지하고, 어떻게 보여주며, 어떻게 회복할 수 있게 만들지에 대한 UX 중심의 에러 처리 설계 전략을 다뤄보겠습니다.
1. ❓ 어떤 오류를 처리해야 할까?
① 컴포넌트 렌더링 중 발생하는 JS 오류
- 예:
props.someProperty.toLowerCase()
– undefined 오류 - React 앱이 멈추거나 흰 화면이 됨
② 비동기 통신 오류 (API 실패)
- 예: 서버에서 500, 404, 401 오류 응답
- 데이터가 없거나 잘못 표시됨
③ 사용자 조작 중 에러 (폼 입력/유효성/버튼 중복 클릭)
- 예: 빠르게 여러 번 클릭해 중복 요청 → 에러 발생
- 서버 응답 실패 시 폼이 멈추거나 초기화
④ 예상치 못한 브라우저 환경 문제
- 예: 로컬스토리지 접근 불가, 쿠키 차단 등
이 모든 상황을 하나의 설계 흐름으로 UX에 녹여내는 것이 목표입니다.
2. ⚠️ React Error Boundary란?
React는 렌더링 도중 에러가 발생하면 전체 앱이 멈추는 문제를 방지하기 위해 Error Boundary(에러 경계)라는 개념을 제공합니다.
✅ 기본 구조
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
// 로깅 서비스 연동 가능
logErrorToService(error, info);
}
render() {
if (this.state.hasError) {
return <h1>문제가 발생했어요. 다시 시도해주세요.</h1>;
}
return this.props.children;
}
}
✅ 사용 예시
<ErrorBoundary>
<ComponentThatMayFail />
</ErrorBoundary>
🎯 적용 위치 팁
- 전체 앱을 감싸는 App.tsx 레벨에 1개
- 중요한 기능을 담당하는 각 feature 단위에도 ErrorBoundary 개별 적용
➡️ 이렇게 하면 한 부분에서 문제가 발생해도 전체 앱이 멈추지 않습니다.
3. 🔄 비동기 에러 처리 – API 요청 실패 대응법
대부분의 에러는 비동기 통신 과정에서 발생합니다. 서버가 응답하지 않거나, 잘못된 요청을 했을 때 에러가 발생하죠.
📌 기본 구조
try {
const res = await axios.get('/api/data');
setData(res.data);
} catch (err) {
setError(true);
}
✅ 개선된 패턴 – 상태 분리
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
const fetchData = async () => {
try {
const res = await axios.get('/api/data');
setData(res.data);
} catch (err) {
setError(true);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
🎯 UX에서 중요한 점
- 로딩 → 성공/에러 → 후속 동작을 명확하게 표현
- 에러 메시지는 사용자 눈높이에 맞게 설명
- 재시도 버튼 제공 or 자동 재시도
4. 📋 에러 상태 UI 패턴 정리
① 전체 화면 에러
<ErrorBoundary>
<ErrorFallback />
</ErrorBoundary>
② 컴포넌트 단위 에러
예: ProductList 에서만 실패했을 때
if (error) return <ErrorBox message="상품 목록을 불러올 수 없습니다." />
③ 서버 에러 메시지 표시
{error.response?.data?.message || "알 수 없는 오류가 발생했습니다."}
✅ 서버에서 보내는 메시지가 구체적이라면 그대로 활용, 아니면 사용자 친화적 메시지로 변환하세요.
5. 💬 사용자 피드백 UX 전략
✅ 메시지 예시
- ❌ "에러가 발생했습니다" → 너무 막연
- ✅ "상품 정보를 불러오지 못했습니다. 잠시 후 다시 시도해주세요."
✅ UX 요소 추가
- ⟳ [다시 시도] 버튼
- 📡 연결 상태 확인 메시지
- ⏳ 백오프 전략 기반 재시도
📌 접근성 고려
- aria-live 영역을 활용해 스크린 리더도 에러 감지 가능하도록
6. 🛰️ Sentry를 활용한 에러 모니터링 연동
클라이언트에서 발생한 에러를 실시간으로 수집하고, 콘솔이 아닌 대시보드에서 확인하고 싶다면 Sentry 연동이 필수입니다.
✅ Sentry 설치
npm install @sentry/react @sentry/tracing
✅ 초기화 설정 (main.jsx 또는 App.jsx)
import * as Sentry from "@sentry/react";
Sentry.init({
dsn: "https://your-project.sentry.io",
integrations: [new Sentry.BrowserTracing()],
tracesSampleRate: 1.0,
});
✅ ErrorBoundary와 Sentry 함께 사용
const ErrorFallback = ({ error }) => {
return (
<div>
<h2>문제가 발생했습니다.</h2>
<p>{error.message}</p>
</div>
);
};
<Sentry.ErrorBoundary fallback={ErrorFallback}>
<App />
</Sentry.ErrorBoundary>
🎯 장점
- 에러 발생 시 Sentry 대시보드에 실시간 업로드
- 브라우저 환경, 유저 경로, 콘솔 메시지 함께 수집
7. 📝 커스텀 에러 로깅 전략
Sentry 외에도 별도 로깅 서버에 에러 정보를 전송해야 하는 경우가 있어요.
✅ 로깅 유틸 예시
export function logClientError(error, info) {
fetch('/log/client-error', {
method: 'POST',
body: JSON.stringify({
message: error.message,
stack: error.stack,
componentStack: info?.componentStack,
}),
});
}
📌 에러 종류별 전송
- API 오류 → 요청 URL, 상태코드 포함
- 컴포넌트 렌더링 오류 → 컴포넌트 스택
8. 🧠 마무리 – 진짜 앱은 에러에 강하다
완성도 높은 앱이란, 에러가 없어서가 아니라, 에러가 나도 흐름이 끊기지 않는 앱입니다.
✨ 종합 정리
- ErrorBoundary로 예기치 못한 렌더링 오류 대비
- 비동기 오류는 상태 기반으로 처리하고, 유저에게 명확히 알리기
- UX 메시지는 기술 용어 대신 사용자 눈높이에서
- Sentry 또는 자체 로깅 서버로 실시간 모니터링 체계 구축
- 복구 경로를 항상 제공: 뒤로 가기, 다시 시도, 초기화 등
React에서의 에러 처리는 기술보다 설계와 사용자 배려의 문제입니다. 이번 글을 통해 여러분의 앱이 더욱 안정적이고 신뢰받는 서비스로 발전하길 바랍니다!
긴 글 읽어주셔서 감사합니다! 공감/댓글/공유로 많은 응원 부탁드려요 🙌
'일상이 개발' 카테고리의 다른 글
React 상태 관리 제대로 설계하기 – Context, Zustand, Redux 완전 비교와 전략적 선택 가이드 (0) | 2025.05.04 |
---|---|
컴포넌트 재사용성과 UX 확장성을 고려한 구조 설계 – Atomic Design부터 폴더 구조까지 실전 가이드 (1) | 2025.05.03 |
React 토스트/알림 시스템 설계 가이드 – 전역 상태부터 자동 닫힘, 접근성까지 완벽 정리 (0) | 2025.05.03 |
React 상태 동기화 이슈 해결법 – Form, 전역 상태, URL 상태를 하나로 묶는 실전 전략 (0) | 2025.05.03 |
React 모달/다이얼로그 시스템 설계 가이드 – Context 상태 관리, 포탈, 접근성까지 완벽 정리 (0) | 2025.05.03 |