React에서 데이터 패칭에 대한 고생을 멈추세요 (React Day 7)
I’m happy to translate the article for you, but I’ll need the full text that you’d like translated. Could you please paste the content (or the portion you want translated) here? I’ll keep the source link and all formatting exactly as you requested.
Fetch 기본
네이티브 fetch API는 브라우저에 내장되어 있으며 프로미스와 함께 작동합니다.
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
Axios 개요
Axios는 자동 JSON 파싱, 향상된 오류 처리, 요청 취소, 인터셉터와 같은 편의 기능을 제공하는 인기 있는 HTTP 클라이언트입니다.
import axios from 'axios';
axios.get('https://api.example.com/data')
.then(res => console.log(res.data))
.catch(err => console.error(err));
Fetch vs. Axios
| Feature | Fetch | Axios |
|---|---|---|
| Size | 내장 (추가 0 KB) | ~13 KB 압축 |
| JSON Handling | 수동 (res.json()) | 자동 |
| Error Handling | 네트워크 오류만 | 네트워크 오류 + HTTP 상태(예: 404) |
| Cancel Requests | AbortController 필요 | 내장 취소 토큰 |
| Best For | 빠른 프로토타입 | 인증 및 인터셉터가 포함된 프로덕션 앱 |
전역 인증 헤더, 요청/응답 인터셉터가 필요하거나 보다 견고한 오류 처리가 필요할 때 Axios를 사용하세요.
일반적인 로딩/오류 패턴
import { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => {
if (!res.ok) throw new Error('Fetch failed!');
return res.json();
})
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, []);
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<>
{data?.map(post => (
<div key={post.id}>- {post.title}</div>
))}
</>
);
}
로딩 UI 관리
loading이true인 동안 스피너나 스켈레톤을 표시합니다.error가 설정되면 친절한 오류 메시지를 표시합니다.
useEffect 패턴: 데이터 가져오기
| 시나리오 | 의존성 배열 | 설명 |
|---|---|---|
| 마운트 시 가져오기 | [] | 첫 렌더링 후 한 번 실행됩니다. |
| 변경 시 재조회 | [userId] | userId가 변경될 때마다 다시 실행됩니다. |
| 언마운트 시 정리 | return function | 진행 중인 요청을 중단하거나 구독을 해제합니다. |
AbortController를 사용한 중단 예시
useEffect(() => {
const controller = new AbortController();
fetch('https://api.example.com/data', { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') setError(err);
});
return () => controller.abort();
}, []);
주의점: 의존성 배열을 생략하면 효과가 매 렌더링마다 실행되어 무한 fetch 루프가 발생합니다.
실제 시나리오
날씨 앱 (Axios)
useEffect(() => {
axios.get('https://api.openweathermap.org/data/2.5/weather', {
params: { q: 'Bengaluru', appid: process.env.REACT_APP_OWM_KEY }
})
.then(res => setWeather(res.data))
.catch(setError);
}, []);
무한 스크롤 피드
- 사용자가 화면 하단 근처로 스크롤할 때 추가 페이지를 가져옵니다.
- 기존 상태를 대체하지 않고 새로운 항목을 추가합니다.
- 현재 페이지 번호를 추적하여 중복 항목이 생기지 않도록 방지합니다.
공통 문제 및 해결책
| 문제 | 원인 | 해결책 |
|---|---|---|
| 중복 데이터 | 페이지 추적 누락 | 페이지 카운터를 저장하고 증가시킵니다. |
| 무한 루프 | useEffect에 의존성 배열이 없음 | 적절한 의존성을 추가합니다 ([] 또는 [var]). |
| 메모리 누수 | 언마운트 시 정리 작업이 없음 | 정리 함수에서 fetch를 중단하거나 구독을 해제합니다. |
| 구식 클로저 | 의존성 누락 | 모든 외부 변수를 의존성 배열에 포함하거나 함수형 상태 업데이트를 사용합니다. |
| 경쟁 상태 | 요청 겹침 | AbortController 또는 Axios 취소 토큰으로 이전 요청을 취소합니다. |
| 개발 환경에서 이중 fetch | React StrictMode가 효과를 두 번 실행함 | 개발 환경에서만 발생하는 것을 받아들이고, 정리 작업이 제대로 작동하도록 합니다. |
성능 팁
- Debounce 빠른 입력 변화를 가져오기 전에 처리합니다.
- 무한 스크롤에서 지연 로딩을 위해 IntersectionObserver를 사용합니다.
- React Query 또는 SWR와 같은 라이브러리를 사용해 결과를 Cache하여 불필요한 네트워크 호출을 방지합니다.
- 가능한 경우 여러 요청을 Batch합니다.
- 자산을 CDN을 통해 제공하고 compression(gzip/Brotli)을 활성화합니다.
보안 알림
API 키를 소스 제어에 커밋하지 마세요. 환경 변수(process.env.REACT_APP_...)에 저장하고 런타임에 참조하세요.
Advanced Topics (Brief Overview)
- Error Boundaries – 실패한 fetch로 인한 렌더링 오류를 잡기 위해 컴포넌트를 래핑합니다.
- React Suspense – “와 함께 미래 지향적인 로딩 상태를 구현합니다.
- Testing – MSW 또는 jest-fetch-mock을 사용해 네트워크 호출을 모킹합니다.
- Caching Strategies – 빠른 UI 업데이트를 위한 Stale‑while‑revalidate 패턴.
추가 학습
- 비디오 튜토리얼: Data Fetching in React (YouTube에서 종합적인 walkthrough를 검색하세요).
이제 React에서 데이터를 가져오는 기본 fetch 호출부터 Axios를 활용한 견고한 패턴, 로딩 상태, 정리, 성능 고려사항까지 탄탄한 기반을 갖추었습니다. GitHub 사용자 검색기나 API 기반 기능을 만들어 보면서 이 개념들을 확고히 하세요. 즐거운 코딩 되세요!