폴백(Fallback)이란?
개발하다 보면 "폴백"이라는 단어를 자주 접하게 됩니다.
근데 막상 누군가 "폴백이 뭐예요?" 하고 물으면 설명하기 애매했던 경험, 한 번쯤 있지 않으신가요?
이번 포스팅에서 폴백의 개념부터 실무에서 어떻게 쓰는지까지 아주 쉽게 정리해 드릴게요.
폴백(Fallback)이란?
폴백(Fallback) = "안 되면 이걸로 대신 써" (PLAN B 의 뜻)
영어 fall back은 "뒤로 물러서다", "대안으로 의지하다"는 뜻입니다.
개발에서도 똑같아요.
원래 방법이 실패했을 때, 미리 준비해둔 대안으로 넘어가는 것
인터넷이 끊겼을 때 캐시 데이터를 보여주거나, 이미지 로딩이 실패했을 때 기본 이미지를 보여주는 것이 모두 폴백입니다.
폴백이 왜 필요할까?
현실 세계에서 완벽한 환경은 없습니다.
- 서버가 다운될 수 있고
- 네트워크가 느릴 수 있고
- 사용자의 브라우저가 특정 기능을 지원하지 않을 수 있고
- 외부 API가 응답을 안 할 수도 있어요
이럴 때 폴백이 없으면 사용자는 그냥 하얀 화면 또는 에러 메시지를 보게 됩니다.
폴백이 있으면 "완벽하진 않지만 그래도 쓸 수 있는 상태"를 유지할 수 있어요.
폴백의 종류와 실제 예제
1. API 폴백 — 외부 API가 죽었을 때
가장 흔한 케이스입니다. 외부 AI API나 날씨 API가 응답하지 않을 때 캐시 데이터나 기본값으로 대체합니다.
async function getWeather(city) {
try {
// 1순위: 실시간 날씨 API 호출
const response = await fetch(`https://api.weather.com/${city}`);
return await response.json();
} catch (error) {
console.warn("날씨 API 실패, 캐시 데이터 사용");
// 폴백: 캐시된 마지막 날씨 데이터 반환
return getCachedWeather(city) ?? { temp: "정보 없음", status: "unknown" };
}
}
핵심: try → catch → 폴백값 반환. 에러가 사용자에게 그대로 노출되지 않습니다.
2. UI 폴백 — 로딩 중일 때 (React Suspense)
React에서는 Suspense를 이용해 데이터 로딩 중 보여줄 UI를 지정할 수 있습니다.
import { Suspense } from "react";
function App() {
return (
<Suspense fallback={<div>로딩 중...</div>}>
{/* 데이터 로딩이 완료되면 이 컴포넌트가 보임 */}
<UserProfile />
</Suspense>
);
}
fallback prop에 넣은 <div>로딩 중...</div> 이 바로 폴백 UI입니다.
데이터가 준비되기 전까지는 이걸 대신 보여줍니다.
3. 이미지 폴백 — 이미지 로딩 실패 시
function UserAvatar({ src, name }) {
return (
<img
src={src}
alt={name}
onError={(e) => {
// 이미지 로딩 실패 시 기본 이미지로 교체
e.target.src = "/images/default-avatar.png";
}}
/>
);
}
onError 이벤트에서 폴백 이미지 경로로 바꿔치기 합니다. 간단하지만 실무에서 엄청 자주 씁니다.
4. 폰트 폴백 — 커스텀 폰트가 없을 때
CSS에서도 폴백은 있습니다.
body {
font-family: "Pretendard", "Noto Sans KR", sans-serif;
}
브라우저는 왼쪽부터 차례로 폰트를 시도합니다.
- Pretendard 있으면 사용
- 없으면 Noto Sans KR 사용
- 그것도 없으면 시스템 기본 sans-serif 사용
오른쪽으로 갈수록 폴백입니다.
5. 환경변수 폴백 — 설정값이 없을 때
서버 코드에서도 자주 씁니다.
// 환경변수가 없으면 기본값(폴백) 사용
const PORT = process.env.PORT ?? 3000;
const DB_URL = process.env.DATABASE_URL ?? "mongodb://localhost:27017/dev";
?? (널 병합 연산자)가 바로 폴백 패턴입니다.
왼쪽이 null 또는 undefined일 때만 오른쪽(폴백)을 씁니다.
6. 다중 폴백 체인 — 우선순위 순서대로
여러 단계의 폴백을 체인처럼 연결할 수도 있습니다.
async function getProductData(id) {
// 1순위: 실시간 DB
const fromDB = await db.products.find(id);
if (fromDB) return fromDB;
// 2순위 폴백: 캐시 서버
const fromCache = await redis.get(`product:${id}`);
if (fromCache) return JSON.parse(fromCache);
// 3순위 폴백: 정적 파일
const fromFile = await readFile(`./data/products/${id}.json`);
if (fromFile) return fromFile;
// 최종 폴백: null 반환
return null;
}
이 패턴은 마이크로서비스, 오프라인 지원 앱, CDN 캐싱 등에서 많이 씁니다.
좋은 폴백 vs 나쁜 폴백
구분 나쁜 폴백 ❌ 좋은 폴백 ✅
| 에러 메시지 | undefined is not a function | "현재 서비스를 이용할 수 없습니다" |
| 이미지 실패 | 깨진 이미지 아이콘 | 기본 프로필 이미지 |
| API 실패 | 흰 화면 | 마지막 캐시 데이터 표시 |
| 로딩 중 | 아무것도 없는 화면 | 스켈레톤 UI |
좋은 폴백의 기준은 딱 하나입니다.
사용자가 "뭔가 잘못됐는데 그래도 쓸 수는 있네"라고 느끼게 하는 것
폴백 설계 시 주의할 점
1. 폴백이 또 실패할 수 있다
폴백 데이터도 없을 경우를 대비해 항상 최종 기본값(빈 배열, 0, "알 수 없음" 등)을 준비하세요.
2. 폴백 사용 여부를 로그에 남겨라
폴백이 작동했다는 건 뭔가 문제가 생겼다는 신호입니다. 모니터링에 잡혀야 합니다.
} catch (error) {
console.warn("[FALLBACK] API 실패, 캐시 사용:", error.message);
return cachedData;
}
3. 폴백을 정상 케이스로 착각하지 마라
폴백이 너무 잘 돌아가면 원래 문제를 방치하게 됩니다. 폴백은 임시방편이지 해결책이 아닙니다.
정리
상황 폴백 전략
| API 응답 없음 | 캐시 데이터 또는 기본값 반환 |
| 컴포넌트 로딩 중 | Suspense fallback (스켈레톤 UI) |
| 이미지 로딩 실패 | onError로 기본 이미지 교체 |
| 폰트 없음 | CSS font-family 순서 지정 |
| 환경변수 없음 | ?? 연산자로 기본값 설정 |
| 여러 단계 실패 | 폴백 체인으로 우선순위 처리 |
폴백은 거창한 개념이 아닙니다.
"이게 안 되면 저걸 써" — 이 한 문장이 전부입니다.
하지만 이 단순한 개념이 서비스의 안정성을 결정짓습니다.
다음에 코드를 짤 때 "이게 실패하면 어떻게 되지?" 한 번만 더 생각해보세요. 그게 바로 폴백 설계의 시작입니다.