서버와 네트워크가 아플 때: 서버 오류와 네트워크 장애
인터넷이 끊기거나 백엔드 서버가 다운되었을 때 사용자가 당황하지 않도록 우아하게 에러 화면(Error Boundary)을 보여줍니다.
아무리 코드를 완벽하게 작성하더라도 우리가 통제할 수 없는 변수가 있습니다. 사용자가 탄 지하철에서 인터넷이 끊기거나, 백엔드 데이터베이스 서버가 갑자기 멈춰버리는 상황입니다. 이때 앱이 하얗게 멈추거나 알 수 없는 영어 에러 코드를 뱉어낸다면 사용자는 패닉에 빠집니다. 이번 에피소드에서는 서버 오류(Server Error)와 네트워크 에러(Network Error) 상황에서 앱이 무너지지 않도록 방어하는 법을 알아봅니다.
세상의 모든 에러와 맞서기
프론트엔드 개발자는 항상 "서버는 언제든 죽을 수 있고, 네트워크는 언제든 끊길 수 있다"는 비관적인(?) 태도를 가져야 합니다. 우리가 다뤄야 할 대표적인 외부 에러는 두 가지입니다.
- 서버 내부 오류 (500 Internal Server Error): 백엔드 서버에서 코드 문제나 DB 장애로 올바른 데이터를 내려주지 못하는 상황.
- 네트워크 장애 (Network Error): 사용자의 와이파이가 끊기거나 터널을 지나갈 때처럼 아예 인터넷 연결이 안 되는 상황.
이런 상황에서 자바스크립트는 조용히 작동을 멈춥니다(Crash). 화면은 멈추고 버튼을 눌러도 반응이 없어집니다. 우리는 이를 막기 위해 코드 전체에 안전망을 깔아야 합니다.
try-catch: 에러를 잡아채는 그물망
데이터를 주고받는 비동기 함수(fetch 등)를 작성할 때는 반드시 `try-catch` 구문으로 감싸야 합니다. 이것이 가장 기초적인 방어선입니다.
안전한 데이터 페칭 함수 작성법
typescript```typescript
async function fetchUserData() {
try {
const response = await fetch('/api/user');
// 서버가 정상(200번대) 응답을 하지 않으면 강제로 에러를 던집니다.
if (!response.ok) {
throw new Error('서버에 문제가 발생했습니다.');
}
const data = await response.json();
return data;
} catch (error) {
// 인터넷이 끊겼거나 위에서 던진 에러가 이쪽으로 넘어옵니다.
console.error(error);
alert('데이터를 불러오지 못했습니다. 잠시 후 다시 시도해주세요.');
}
}
```- 1try 블록 안에서 정상적으로 서버에 데이터를 요청합니다.
- 2만약 서버가 500 에러를 뱉거나 네트워크가 끊기면 에러를 발생(throw)시킵니다.
- 3catch 블록이 에러를 낚아채어, 사용자에게 보여줄 에러 메시지 상태를 업데이트합니다.
Next.js의 방어막: error.tsx
컴포넌트마다 일일이 `try-catch`를 작성하고 에러 상태를 관리하는 것은 매우 피곤한 일입니다. 다행히 Next.js는 `loading.tsx`처럼 파일 시스템 기반의 에러 처리 기능인 `error.tsx`를 제공합니다.
특정 컴포넌트에서 에러가 터지면, 그 에러가 전체 화면을 망가뜨리지 않도록 Next.js가 중간에서 에러를 격리합니다. 그리고 미리 만들어둔 `error.tsx` 화면을 사용자에게 보여주어, 우아한 실패(Graceful Degradation)를 만들어냅니다.
에러 바운더리(Error Boundary)의 역할
- 에러 포착
- 하위 컴포넌트 렌더링 중 발생하는 런타임 에러를 감지합니다.
- 격리(Isolation)
- 에러가 난 부분만 에러 화면으로 대체하고, 헤더나 사이드바 등 나머지 화면은 정상 유지합니다.
- 복구 시도(Recovery)
- 사용자가 '다시 시도' 버튼을 눌러 앱을 새로고침하지 않고 복구할 기회를 줍니다.
Next.js의 error.tsx 컴포넌트 작성
// app/error.tsx
'use client'; // 에러 컴포넌트는 무조건 클라이언트 컴포넌트여야 합니다.
export default function Error({ error, reset }) {
return (
<div className="error-container">
<h2>앗, 무언가 잘못되었습니다!</h2>
<p className="text-gray-500">
서버와 통신하는 중 문제가 발생했습니다. 네트워크 상태를 확인해주세요.
</p>
{/* reset 함수를 호출하면 에러가 발생한 컴포넌트를 다시 렌더링 시도합니다. */}
<button onClick={() => reset()} className="retry-btn">
다시 시도하기
</button>
</div>
);
}친절한 에러 안내 문구 작성하기
에러 상황에서 가장 피해야 할 것은 기술적인 용어를 그대로 노출하는 것입니다. "500 Internal Server Error"나 "Failed to fetch" 같은 문구는 개발자에게나 유용하지 일반 사용자에게는 공포감을 줍니다.
에러 메시지 작성의 좋은 예와 나쁜 예
오해 또는 실수
(나쁜 예) 500 에러 발생: DB 연결 실패
바로잡기
(좋은 예) 일시적인 서버 문제가 발생했습니다. 잠시 후 다시 시도해주세요.
오해 또는 실수
(나쁜 예) Network Error
바로잡기
(좋은 예) 인터넷 연결이 끊긴 것 같습니다. 와이파이 상태를 확인해주세요.
오해 또는 실수
(나쁜 예) 알 수 없는 오류
바로잡기
(좋은 예) 예상치 못한 문제가 발생했습니다. 계속되면 고객센터로 문의해주세요.
네트워크 장애나 서버 오류는 피할 수 없는 현실입니다. 이 오류들을 감추려 하지 말고, 사용자가 당황하지 않게 다독이며 "다시 시도하기" 같은 명확한 다음 행동을 제시하는 것이 진정한 프론트엔드 장인의 자세입니다.
개념 퀴즈
퀴즈 답을 맞춰야 학습 완료가 됩니다.Next.js의 에러 처리 컴포넌트인 error.tsx에 대한 설명으로 올바른 것은 무엇인가요?
3줄 요약
- 1비동기 통신을 할 때는 항상 try-catch를 사용하여 네트워크 장애와 서버 에러를 대비해야 합니다.
- 2Next.js의 error.tsx를 활용하면 에러가 발생해도 전체 화면이 망가지지 않게 격리할 수 있습니다.
- 3에러 메시지는 기술 용어를 배제하고, 사용자에게 명확한 원인과 다음 행동을 제시해야 합니다.