Ch.18 API 연동 — fetch와 데이터 흐름
실전 — 날씨 앱 만들기
공개 API로 진짜 앱을 만들어보자
지금까지 배운 fetch, async/await, useEffect를 모두 결합해서 실제 날씨 API를 호출하는 앱을 만듭니다.
배운 것들을 하나의 앱으로 조립할 수 있을까?
실전 조립 — 검색 → fetch → 상태 관리 → 화면 표시까지 전체 흐름을 완성합니다.
핵심 내용
연습용 공개 API 무료로 바로 사용 가능합니다
// JSONPlaceholder — 바로 테스트 가능!
// 게시물 목록
const res = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
const posts = await res.json();
// [{id:1, title:"...", body:"..."}, ...]
// 특정 게시물
const res2 = await fetch(
"https://jsonplaceholder.typicode.com/posts/1"
);
const post = await res2.json();
// {id:1, title:"...", body:"..."}API 키란? 일부 API는 사용자 인증을 위해 API 키(비밀번호)를 요구합니다. 회원가입 → 무료 키 발급 → URL에 ?appid=YOUR_KEY 형태로 전달합니다.
사용자 입력 → fetch → 결과 표시 완전한 흐름을 구현합니다
import { useState } from "react";
function WeatherApp() {
const [city, setCity] = useState("");
const [weather, setWeather] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
async function handleSearch() {
if (!city.trim()) return;
setLoading(true);
setError(null);
try {
const API_KEY = "YOUR_API_KEY";
const url = `https://api.openweathermap.org
/data/2.5/weather?q=${city}
&appid=${API_KEY}&units=metric&lang=kr`;
const res = await fetch(url);
if (!res.ok) {
throw new Error("도시를 찾을 수 없습니다");
}
const data = await res.json();
setWeather(data);
} catch (err) {
setError(err.message);
setWeather(null);
} finally {
setLoading(false);
}
}입력: 사용자가 도시 이름 입력 (city state)
검증: 빈 입력 체크, 로딩 시작
API 호출: fetch()로 날씨 데이터 요청
결과 처리: 성공 → setWeather, 실패 → setError
화면 갱신: state 변경으로 자동 리렌더링
검색 전 검증을 잊지 마세요 city.trim()이 비어 있으면 API 호출을 하지 않습니다. 불필요한 네트워크 요청을 막는 기본적인 UX입니다.
로딩 스피너, 에러 메시지, 빈 상태 3가지 UX를 챙깁니다
// WeatherApp의 return 부분
return (
<div className="max-w-md mx-auto p-6">
<div className="flex gap-2">
<input
value={city}
onChange={e => setCity(e.target.value)}
onKeyDown={e =>
e.key === "Enter" && handleSearch()}
placeholder="도시 이름 입력"
className="flex-1 border rounded-lg
px-4 py-2 focus:ring-2
focus:ring-blue-500" />
<button
onClick={handleSearch}
disabled={loading}
className="bg-blue-500 text-white
px-4 py-2 rounded-lg
hover:bg-blue-600
disabled:opacity-50">
{loading ? "검색 중..." : "검색"}
</button>
</div>
{/* 에러 메시지 */}
{error && (
<p className="text-red-500 mt-4">
{error}
</p>
)}
{/* 날씨 결과 */}
{weather && (
<div className="mt-4 bg-white rounded-xl
shadow p-6 text-center">
<h2 className="text-2xl font-bold">
{weather.name}
</h2>
<p className="text-4xl mt-2">
{Math.round(weather.main.temp)}°C
</p>
<p className="text-gray-600 mt-1">
{weather.weather[0].description}
</p>
</div>
)}
</div>
);
}바이브 코더 필수 패턴 AI에게 앱을 만들어 달라고 하면 이 패턴이 반드시 포함됩니다. input + fetch + loading/error/data 상태 = 모든 API 앱의 뼈대!
API 호출 시 로딩 상태를 확실히 끝내기 위해 setLoading(false)는 어디에?
fetch()는 404 응답도 catch 블록에서 잡아준다.
다음 중 useEffect + fetch 패턴에서 빠지면 안 되는 것은?
바이브 코더가 API 앱에서 반드시 관리해야 할 3가지 상태는?
핵심 용어
JSONPlaceholder
가짜 REST API — 연습용 사용자/게시물/댓글 데이터
OpenWeatherMap
실제 날씨 데이터 — 무료 API 키 발급 필요
PokeAPI
포켓몬 데이터 — 키 불필요, 바로 사용 가능
The Cat API
고양이 사진 API — 랜덤 이미지 제공
로딩 상태
버튼 텍스트를 "검색 중..."으로 변경 + disabled
에러 표시
빨간색 텍스트로 사용자에게 원인 안내
빈 상태
weather가 null이면 결과 영역 미표시
키보드 지원
Enter 키로도 검색 가능 (onKeyDown)
정리 노트
실전 — 날씨 앱 만들기 총정리
핵심 포인트
- 공개 API
- OpenWeatherMap 등 무료 API로 실제 데이터 활용
- 검색 → 호출 흐름
- 사용자 입력 → API 호출 → 결과 표시의 전체 과정
- 에러 핸들링
- 네트워크 에러, 404, 빈 결과 등 예외 상황 처리
- UX 패턴
- 로딩 스피너, 에러 메시지, 빈 상태 안내 등 사용자 배려
실전 앱의 핵심은 '정상 작동'이 아니라 '에러 상황 처리'입니다.
퀴즈와 인터랙션으로 더 깊이 학습하세요
play_circle인터랙티브 레슨 시작