browser
브라우저 핵심 API — Fetch, Storage, Observer
프론트엔드 개발자는 매일 브라우저 API를 사용합니다. "Fetch와 XHR의 차이는?", "localStorage는 언제 쓰고 쿠키는 언제 쓰나요?", "IntersectionObserver를 어떻게 활용했나요?" 같은 질문이 실무형 면접에서 자주 나옵니다. 어떤 API를 언제, 왜 사용하는지 선택 기준을 설명할 수 있어야 합니다.
학습 개요
탄생 배경
해결하려 했던 문제
초기 웹은 서버 렌더링 중심으로 JavaScript의 역할이 제한적이었습니다. 동적 데이터 로딩(XHR), 클라이언트 상태 저장(쿠키), DOM 변화 감지 등이 모두 불편하거나 불가능했습니다.
역사적 맥락
XMLHttpRequest(2000년대 초)가 Ajax의 기반이 됐습니다. HTML5(2008-2014)에서 localStorage, sessionStorage, Canvas, Web Workers 등 강력한 API가 표준화됐습니다. Fetch API(2015)가 Promise 기반으로 XHR를 대체했고, IntersectionObserver(2016), ResizeObserver(2020) 등 Observer 패턴 API가 등장해 성능 친화적인 DOM 감지가 가능해졌습니다.
이전에는 어떻게 했나
Fetch API 대신 axios(더 간결한 API, 인터셉터), XHR(레거시 지원 필요 시), TanStack Query(서버 상태 관리). 스토리지 대신 IndexedDB(대용량 구조화 데이터), Cookie(서버 전송 필요 시). IntersectionObserver 대신 scroll 이벤트(레거시, 성능 나쁨).
멘탈 모델
동작 원리
Fetch API: - fetch(url, options)는 Promise<Response>를 반환 - Response.ok(200-299), Response.status, Response.json()으로 처리 - 4xx/5xx HTTP 에러는 자동으로 reject되지 않음 — res.ok 체크 필수 - AbortController로 요청 취소, credentials로 쿠키 포함 여부 제어 Web Storage: - localStorage: Origin 단위 영구 저장 (탭/창 공유, 명시적 삭제 전 유지) - sessionStorage: Origin + 탭 단위 임시 저장 (탭 닫으면 삭제) - 둘 다 5MB 제한, 동기 API, JS에서만 접근 - JSON.stringify/parse로 객체 저장 Observer APIs: - IntersectionObserver: 요소가 뷰포트에 보이는 비율 감지 (무한 스크롤, 지연 로딩) - ResizeObserver: 요소 크기 변화 감지 (레이아웃 반응형 처리) - MutationObserver: DOM 변화 감지 (서드파티 DOM 라이브러리 모니터링) - 모두 scroll/resize 이벤트보다 성능 우수 (메인 스레드 블로킹 없음)
핵심 구성 요소
Fetch API
Promise 기반 HTTP 요청. XHR 대체. AbortController로 취소 가능.
localStorage
영구적 클라이언트 저장소. 사용자 설정, 비로그인 장바구니 등.
sessionStorage
세션 단위 저장소. 다단계 폼 임시 데이터, 탭 간 격리 필요 시.
IntersectionObserver
요소의 뷰포트 진입/이탈 감지. 무한 스크롤, 이미지 지연 로딩.
ResizeObserver
요소 크기 변화 감지. window.resize보다 정확하고 효율적.
Canvas API
2D/WebGL 그래픽. 차트, 이미지 편집, 게임, 데이터 시각화.
흐름 설명
[Fetch API 올바른 에러 처리 패턴]
// ❌ 잘못된 코드 — 4xx/5xx도 resolve됨
try {
const res = await fetch('/api/data');
const data = await res.json(); // 404여도 실행됨
} catch (e) {
// 네트워크 에러만 잡힘
}
// ✅ 올바른 코드
const res = await fetch('/api/data');
if (!res.ok) throw new Error(`HTTP error: ${res.status}`);
const data = await res.json();
[Web Storage 선택 기준]
로그인 유지 X, JS 접근 O, 영구 보관 → localStorage (테마, 언어 설정)
탭 간 격리 필요, 임시 데이터 → sessionStorage (다단계 폼)
서버 전송 필요, 보안 쿠키 → Cookie (인증 토큰 — HttpOnly)
대용량(5MB 이상) 구조화 → IndexedDB
[IntersectionObserver — 무한 스크롤]
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadMoreItems();
}
}, { threshold: 0.1 });
observer.observe(sentinelRef.current);
코드 예제
// ✅ Fetch + AbortController (타임아웃 구현)
async function fetchWithTimeout(url: string, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const res = await fetch(url, {
signal: controller.signal,
credentials: 'include', // 쿠키 포함
headers: { 'Content-Type': 'application/json' },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} finally {
clearTimeout(timeoutId);
}
}
// ✅ localStorage 타입 안전 유틸
function getStorageItem<T>(key: string, fallback: T): T {
try {
const item = localStorage.getItem(key);
return item ? (JSON.parse(item) as T) : fallback;
} catch {
return fallback;
}
}
// ✅ IntersectionObserver — React 무한 스크롤
function useInfiniteScroll(callback: () => void) {
const sentinelRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) callback();
},
{ threshold: 0.1 }
);
if (sentinelRef.current) observer.observe(sentinelRef.current);
return () => observer.disconnect();
}, [callback]);
return sentinelRef;
}
// ✅ ResizeObserver — 요소 크기 감지
function useElementSize(ref: RefObject<HTMLElement>) {
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
const observer = new ResizeObserver(([entry]) => {
setSize({
width: entry.contentRect.width,
height: entry.contentRect.height,
});
});
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, [ref]);
return size;
}
비교 분석
브라우저
vsFetch API vs XHR (XMLHttpRequest)
브라우저
vslocalStorage vs Cookie
브라우저
vsIntersectionObserver vs scroll 이벤트
트레이드오프
이상적인 사용 사례