일상이 개발

React 앱 오류 처리 UX 가이드 – ErrorBoundary, 비동기 예외, 사용자 피드백까지 완벽 설계

아빠고미 2025. 5. 3. 15:58
반응형

React 앱에서 오류 처리 UX 제대로 설계하기 – 사용자 피드백, 에러 경계, 상태 복구까지 완벽 전략

프론트엔드 개발에서 '에러'는 피할 수 없는 존재입니다. 하지만 중요한 건, 에러가 났을 때 사용자가 얼마나 편하게 복구할 수 있느냐죠.

사용자가 앱을 이용하는 중에 갑자기 흰 화면이 뜨거나, "무슨 문제가 발생했습니다" 한 줄만 덩그러니 나온다면 그 서비스에 대한 신뢰는 급락합니다.

React 앱 오류 처리 UX 가이드 – ErrorBoundary, 비동기 예외, 사용자 피드백까지 완벽 설계

이번 글에서는 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에서의 에러 처리는 기술보다 설계와 사용자 배려의 문제입니다. 이번 글을 통해 여러분의 앱이 더욱 안정적이고 신뢰받는 서비스로 발전하길 바랍니다!


긴 글 읽어주셔서 감사합니다! 공감/댓글/공유로 많은 응원 부탁드려요 🙌

반응형