무료 API 완벽 가이드 | 개인 프로젝트용 공공 API 100+ 총정리
이 글의 핵심
개인 프로젝트에 바로 사용할 수 있는 무료 공공 API 100개 이상. 날씨, 지도, 금융, AI, 이미지, 뉴스, 게임, 공공데이터까지. 인증 방법, 사용 예제, 제한사항 완벽 정리.
들어가며: “프로젝트에 실제 데이터를 넣고 싶어요”
토이 프로젝트의 고민
포트폴리오용 날씨 앱을 만들고 싶은데 실제 날씨 데이터가 없거나, 지도 서비스를 구현하고 싶은데 지도 API가 비싸거나, 주식 차트를 그리고 싶은데 금융 데이터를 구할 수 없어 고민한 적 있으신가요? 다행히도 수많은 기업과 정부 기관이 무료 공공 API를 제공하여 개인 개발자도 실전 같은 프로젝트를 만들 수 있습니다. 이 글에서 다루는 것:
- 100개 이상의 무료 API 카테고리별 정리
- 각 API의 특징, 제한사항, 사용 예제
- API 키 발급 및 인증 방법
- 실전 프로젝트 아이디어
실전 경험에서 배운 교훈
이 기술을 실무 프로젝트에 처음 도입했을 때, 공식 문서만으로는 알 수 없었던 많은 함정들이 있었습니다. 특히 프로덕션 환경에서 발생하는 엣지 케이스들은 로컬 개발 환경에서는 재현조차 되지 않았죠.
가장 어려웠던 점은 성능 최적화였습니다. 처음엔 “동작만 하면 되겠지”라고 생각했지만, 실제 사용자 트래픽이 몰리면서 병목 지점들이 하나씩 드러났습니다. 특히 데이터베이스 쿼리 최적화, 캐싱 전략, 에러 핸들링 구조 등은 여러 번의 장애를 겪으면서 개선해 나갔습니다.
이 글에서는 그런 시행착오를 통해 얻은 실전 노하우와, “이렇게 하면 안 된다”는 교훈들을 함께 정리했습니다. 특히 트러블슈팅 섹션은 실제 장애 대응 경험을 바탕으로 작성했으니, 비슷한 문제를 마주했을 때 참고하시면 도움이 될 것입니다.
1. 날씨 API
OpenWeatherMap
특징: 현재 날씨, 5일 예보, 16일 예보, 과거 데이터
제한: 60 calls/min, 1,000,000 calls/month (무료)
인증: API Key
// 현재 날씨
const API_KEY = 'your_api_key';
const city = 'Seoul';
fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric&lang=kr`)
.then(res => res.json())
.then(data => {
console.log(`도시: ${data.name}`);
console.log(`온도: ${data.main.temp}°C`);
console.log(`날씨: ${data.weather[0].description}`);
console.log(`습도: ${data.main.humidity}%`);
});
// 5일 예보
fetch(`https://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${API_KEY}&units=metric&lang=kr`)
.then(res => res.json())
.then(data => {
data.list.forEach(item => {
console.log(`${item.dt_txt}: ${item.main.temp}°C, ${item.weather[0].description}`);
});
});
회원가입: https://openweathermap.org/api
WeatherAPI
특징: 실시간 날씨, 14일 예보, 천문 데이터, 공기질
제한: 1,000,000 calls/month (무료)
인증: API Key
import requests
API_KEY = 'your_api_key'
city = 'Seoul'
# 현재 날씨
url = f'http://api.weatherapi.com/v1/current.json?key={API_KEY}&q={city}&lang=ko'
response = requests.get(url)
data = response.json()
print(f"위치: {data['location']['name']}")
print(f"온도: {data['current']['temp_c']}°C")
print(f"날씨: {data['current']['condition']['text']}")
# 7일 예보
url = f'http://api.weatherapi.com/v1/forecast.json?key={API_KEY}&q={city}&days=7&lang=ko'
response = requests.get(url)
data = response.json()
for day in data['forecast']['forecastday']:
print(f"{day['date']}: 최고 {day['day']['maxtemp_c']}°C, 최저 {day['day']['mintemp_c']}°C")
회원가입: https://www.weatherapi.com/
기상청 API (한국)
특징: 초단기예보, 단기예보, 중기예보, 특보
제한: 일 1,000건 (무료)
인증: 공공데이터포털 API Key
import requests
from datetime import datetime
API_KEY = 'your_api_key'
base_url = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst'
# 초단기실황
params = {
'serviceKey': API_KEY,
'numOfRows': 10,
'pageNo': 1,
'dataType': 'JSON',
'base_date': datetime.now().strftime('%Y%m%d'),
'base_time': '0600',
'nx': 60, # 격자 X (서울)
'ny': 127 # 격자 Y
}
response = requests.get(base_url, params=params)
data = response.json()
for item in data['response']['body']['items']['item']:
category = item['category']
value = item['obsrValue']
print(f"{category}: {value}")
회원가입: https://www.data.go.kr/
2. 지도 및 위치 API
Google Maps API
특징: 지도, 경로, 장소 검색, Geocoding, Street View
제한: $200/month 무료 크레딧 (약 28,000 지도 로드)
인증: API Key
// Geocoding (주소 → 좌표)
// 변수 선언 및 초기화
const address = '서울특별시 강남구 테헤란로';
const API_KEY = 'your_api_key';
fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${API_KEY}`)
.then(res => res.json())
.then(data => {
const location = data.results[0].geometry.location;
console.log(`위도: ${location.lat}, 경도: ${location.lng}`);
});
// 장소 검색
fetch(`https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=37.5665,126.9780&radius=1000&type=restaurant&key=${API_KEY}`)
.then(res => res.json())
.then(data => {
data.results.forEach(place => {
console.log(`${place.name}: ${place.vicinity}`);
});
});
<!-- 지도 임베드 -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
<div id="map" style="height: 400px;"></div>
<script>
function initMap() {
const seoul = { lat: 37.5665, lng: 126.9780 };
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 13,
center: seoul
});
new google.maps.Marker({
position: seoul,
map: map,
title: '서울'
});
}
initMap();
</script>
회원가입: https://developers.google.com/maps
Kakao Map API (한국)
특징: 지도, 검색, 경로, 주소 변환 (한국 특화)
제한: 일 300,000건 (무료)
인증: REST API Key
// 주소 검색
const REST_API_KEY = 'your_api_key';
const address = '서울특별시 강남구 테헤란로 152';
fetch(`https://dapi.kakao.com/v2/local/search/address.json?query=${encodeURIComponent(address)}`, {
headers: {
'Authorization': `KakaoAK ${REST_API_KEY}`
}
})
.then(res => res.json())
.then(data => {
const result = data.documents[0];
console.log(`주소: ${result.address_name}`);
console.log(`좌표: ${result.x}, ${result.y}`);
});
// 키워드 검색
fetch(`https://dapi.kakao.com/v2/local/search/keyword.json?query=카페&x=126.9780&y=37.5665&radius=1000`, {
headers: {
'Authorization': `KakaoAK ${REST_API_KEY}`
}
})
.then(res => res.json())
.then(data => {
data.documents.forEach(place => {
console.log(`${place.place_name}: ${place.address_name}`);
});
});
회원가입: https://developers.kakao.com/
OpenStreetMap (Nominatim)
특징: 무료 오픈소스 지도, Geocoding
제한: 1 request/sec (무료)
인증: 불필요 (User-Agent 필수)
import requests
import time
def geocode(address):
"""주소 → 좌표"""
url = 'https://nominatim.openstreetmap.org/search'
params = {
'q': address,
'format': 'json',
'limit': 1
}
headers = {
'User-Agent': 'MyApp/1.0 ([email protected])' # 필수!
}
response = requests.get(url, params=params, headers=headers)
data = response.json()
if data:
return {
'lat': float(data[0]['lat']),
'lon': float(data[0]['lon']),
'display_name': data[0]['display_name']
}
return None
result = geocode('Seoul, South Korea')
print(result)
time.sleep(1) # Rate limit 준수
문서: https://nominatim.org/release-docs/latest/api/Overview/
3. 금융 및 암호화폐 API
Alpha Vantage (주식, 환율)
특징: 실시간 주식, 환율, 암호화폐, 기술 지표
제한: 25 requests/day (무료)
인증: API Key
const API_KEY = 'your_api_key';
// 주식 시세 (실시간)
fetch(`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=AAPL&apikey=${API_KEY}`)
.then(res => res.json())
.then(data => {
const quote = data['Global Quote'];
console.log(`Symbol: ${quote['01. symbol']}`);
console.log(`Price: $${quote['05. price']}`);
console.log(`Change: ${quote['09. change']} (${quote['10. change percent']})`);
});
// 일일 시계열 (최근 100일)
fetch(`https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=AAPL&apikey=${API_KEY}`)
.then(res => res.json())
.then(data => {
const timeSeries = data['Time Series (Daily)'];
Object.entries(timeSeries).slice(0, 5).forEach(([date, values]) => {
console.log(`${date}: Open $${values['1. open']}, Close $${values['4. close']}`);
});
});
// 환율
fetch(`https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=USD&to_currency=KRW&apikey=${API_KEY}`)
.then(res => res.json())
.then(data => {
const rate = data['Realtime Currency Exchange Rate'];
console.log(`1 USD = ${rate['5. Exchange Rate']} KRW`);
});
회원가입: https://www.alphavantage.co/support/#api-key
CoinGecko (암호화폐)
특징: 암호화폐 시세, 시가총액, 거래량, 차트
제한: 10-50 calls/min (무료)
인증: 불필요 (API Key 선택적)
import requests
# 암호화폐 시세
url = 'https://api.coingecko.com/api/v3/simple/price'
params = {
'ids': 'bitcoin,ethereum,ripple',
'vs_currencies': 'usd,krw',
'include_24hr_change': 'true'
}
response = requests.get(url, params=params)
data = response.json()
for coin, prices in data.items():
print(f"{coin.upper()}:")
print(f" USD: ${prices['usd']:,.2f} ({prices['usd_24h_change']:+.2f}%)")
print(f" KRW: ₩{prices['krw']:,.0f} ({prices['krw_24h_change']:+.2f}%)")
# 시가총액 상위 10개
url = 'https://api.coingecko.com/api/v3/coins/markets'
params = {
'vs_currency': 'usd',
'order': 'market_cap_desc',
'per_page': 10,
'page': 1
}
response = requests.get(url, params=params)
coins = response.json()
for i, coin in enumerate(coins, 1):
print(f"{i}. {coin['name']} ({coin['symbol'].upper()}): ${coin['current_price']:,.2f}")
문서: https://www.coingecko.com/en/api
ExchangeRate-API (환율)
특징: 실시간 환율, 161개 통화
제한: 1,500 requests/month (무료)
인증: API Key
const API_KEY = 'your_api_key';
// 환율 조회
fetch(`https://v6.exchangerate-api.com/v6/${API_KEY}/latest/USD`)
.then(res => res.json())
.then(data => {
console.log(`1 USD = ${data.conversion_rates.KRW} KRW`);
console.log(`1 USD = ${data.conversion_rates.JPY} JPY`);
console.log(`1 USD = ${data.conversion_rates.EUR} EUR`);
});
// 환전 계산
const amount = 100;
fetch(`https://v6.exchangerate-api.com/v6/${API_KEY}/pair/USD/KRW/${amount}`)
.then(res => res.json())
.then(data => {
console.log(`$${amount} = ₩${data.conversion_result}`);
});
회원가입: https://www.exchangerate-api.com/
4. AI 및 머신러닝 API
OpenAI API
특징: GPT-4, GPT-3.5, DALL-E, Whisper
제한: $5 무료 크레딧 (신규 가입)
인증: API Key
import openai
openai.api_key = 'your_api_key'
# ChatGPT
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Python으로 피보나치 수열 구현해줘"}
]
)
print(response.choices[0].message.content)
# 이미지 생성 (DALL-E)
response = openai.Image.create(
prompt="A futuristic city with flying cars",
n=1,
size="1024x1024"
)
image_url = response.data[0].url
print(f"Image URL: {image_url}")
회원가입: https://platform.openai.com/
Hugging Face API
특징: 수천 개의 AI 모델 (텍스트, 이미지, 음성)
제한: 30,000 requests/month (무료)
인증: API Token
import requests
API_TOKEN = 'your_api_token'
headers = {"Authorization": f"Bearer {API_TOKEN}"}
# 감정 분석
API_URL = "https://api-inference.huggingface.co/models/distilbert-base-uncased-finetuned-sst-2-english"
data = {"inputs": "I love this product!"}
response = requests.post(API_URL, headers=headers, json=data)
result = response.json()
print(result)
# [{'label': 'POSITIVE', 'score': 0.9998}]
# 텍스트 생성
API_URL = "https://api-inference.huggingface.co/models/gpt2"
data = {"inputs": "Once upon a time"}
response = requests.post(API_URL, headers=headers, json=data)
print(response.json()[0]['generated_text'])
# 이미지 분류
API_URL = "https://api-inference.huggingface.co/models/google/vit-base-patch16-224"
with open('image.jpg', 'rb') as f:
data = f.read()
response = requests.post(API_URL, headers=headers, data=data)
print(response.json())
회원가입: https://huggingface.co/
Google Gemini API
특징: Gemini Pro, 멀티모달 (텍스트, 이미지)
제한: 60 requests/min (무료)
인증: API Key
import google.generativeai as genai
genai.configure(api_key='your_api_key')
model = genai.GenerativeModel('gemini-pro')
# 텍스트 생성
response = model.generate_content('Python으로 웹 크롤러 만드는 방법 설명해줘')
print(response.text)
# 멀티모달 (이미지 + 텍스트)
model = genai.GenerativeModel('gemini-pro-vision')
image = PIL.Image.open('photo.jpg')
response = model.generate_content(['이 이미지에 뭐가 있나요?', image])
print(response.text)
회원가입: https://makersuite.google.com/app/apikey
5. 이미지 및 미디어 API
Unsplash API
특징: 고품질 무료 이미지 (200만+ 장)
제한: 50 requests/hour (무료)
인증: Access Key
const ACCESS_KEY = 'your_access_key';
// 랜덤 이미지
fetch(`https://api.unsplash.com/photos/random?client_id=${ACCESS_KEY}`)
.then(res => res.json())
.then(data => {
console.log(`Image URL: ${data.urls.regular}`);
console.log(`Author: ${data.user.name}`);
});
// 검색
fetch(`https://api.unsplash.com/search/photos?query=nature&client_id=${ACCESS_KEY}`)
.then(res => res.json())
.then(data => {
data.results.forEach(photo => {
console.log(`${photo.alt_description}: ${photo.urls.small}`);
});
});
회원가입: https://unsplash.com/developers
Pexels API
특징: 무료 이미지 및 비디오
제한: 200 requests/hour (무료)
인증: API Key
import requests
API_KEY = 'your_api_key'
headers = {'Authorization': API_KEY}
# 이미지 검색
url = 'https://api.pexels.com/v1/search'
params = {'query': 'nature', 'per_page': 10}
response = requests.get(url, headers=headers, params=params)
data = response.json()
for photo in data['photos']:
print(f"Photo: {photo['src']['medium']}")
print(f"Photographer: {photo['photographer']}")
# 비디오 검색
url = 'https://api.pexels.com/videos/search'
params = {'query': 'ocean', 'per_page': 5}
response = requests.get(url, headers=headers, params=params)
data = response.json()
for video in data['videos']:
print(f"Video: {video['video_files'][0]['link']}")
회원가입: https://www.pexels.com/api/
Lorem Picsum (플레이스홀더 이미지)
특징: 랜덤 이미지 (테스트용)
제한: 무제한
인증: 불필요
<!-- 특정 크기 -->
<img src="https://picsum.photos/800/600" alt="Random">
<!-- 특정 이미지 ID -->
<img src="https://picsum.photos/id/237/800/600" alt="Dog">
<!-- 그레이스케일 -->
<img src="https://picsum.photos/800/600?grayscale" alt="Grayscale">
<!-- 블러 -->
<img src="https://picsum.photos/800/600?blur=5" alt="Blurred">
문서: https://picsum.photos/
6. 뉴스 및 소셜 API
NewsAPI
특징: 전 세계 뉴스, 80,000+ 소스
제한: 100 requests/day (무료)
인증: API Key
const API_KEY = 'your_api_key';
// 헤드라인
fetch(`https://newsapi.org/v2/top-headlines?country=kr&apiKey=${API_KEY}`)
.then(res => res.json())
.then(data => {
data.articles.forEach(article => {
console.log(`${article.title}`);
console.log(`Source: ${article.source.name}`);
console.log(`URL: ${article.url}\n`);
});
});
// 키워드 검색
fetch(`https://newsapi.org/v2/everything?q=artificial intelligence&sortBy=publishedAt&apiKey=${API_KEY}`)
.then(res => res.json())
.then(data => {
console.log(`Total results: ${data.totalResults}`);
data.articles.slice(0, 5).forEach(article => {
console.log(`${article.title} (${article.publishedAt})`);
});
});
회원가입: https://newsapi.org/
Reddit API
특징: Reddit 게시글, 댓글, 서브레딧
제한: 60 requests/min (무료)
인증: OAuth2 (또는 불필요)
import requests
# 인증 없이 공개 데이터 조회
url = 'https://www.reddit.com/r/programming/hot.json'
headers = {'User-Agent': 'MyApp/1.0'}
response = requests.get(url, headers=headers)
data = response.json()
for post in data['data']['children'][:10]:
p = post['data']
print(f"Title: {p['title']}")
print(f"Score: {p['score']} | Comments: {p['num_comments']}")
print(f"URL: https://reddit.com{p['permalink']}\n")
# 검색
url = 'https://www.reddit.com/search.json'
params = {'q': 'python tutorial', 'limit': 10}
response = requests.get(url, headers=headers, params=params)
data = response.json()
문서: https://www.reddit.com/dev/api/
7. 게임 및 엔터테인먼트 API
PokeAPI (포켓몬)
특징: 포켓몬 데이터베이스 (1,000+ 포켓몬)
제한: 무제한 (캐싱 권장)
인증: 불필요
// 포켓몬 정보
fetch('https://pokeapi.co/api/v2/pokemon/pikachu')
.then(res => res.json())
.then(data => {
console.log(`Name: ${data.name}`);
console.log(`Height: ${data.height}`);
console.log(`Weight: ${data.weight}`);
console.log(`Types: ${data.types.map(t => t.type.name).join(', ')}`);
console.log(`Abilities: ${data.abilities.map(a => a.ability.name).join(', ')}`);
console.log(`Sprite: ${data.sprites.front_default}`);
});
// 포켓몬 목록
fetch('https://pokeapi.co/api/v2/pokemon?limit=151')
.then(res => res.json())
.then(data => {
data.results.forEach((pokemon, i) => {
console.log(`${i+1}. ${pokemon.name}`);
});
});
RAWG (비디오 게임 데이터베이스)
특징: 800,000+ 게임 정보, 리뷰, 스크린샷
제한: 20,000 requests/month (무료)
인증: API Key
import requests
API_KEY = 'your_api_key'
# 게임 검색
url = 'https://api.rawg.io/api/games'
params = {
'key': API_KEY,
'search': 'witcher',
'page_size': 5
}
response = requests.get(url, params=params)
data = response.json()
for game in data['results']:
print(f"Title: {game['name']}")
print(f"Rating: {game['rating']}/5")
print(f"Released: {game['released']}")
print(f"Platforms: {', '.join([p['platform']['name'] for p in game['platforms']])}\n")
# 게임 상세 정보
game_id = 3328 # The Witcher 3
url = f'https://api.rawg.io/api/games/{game_id}'
params = {'key': API_KEY}
response = requests.get(url, params=params)
game = response.json()
print(f"Description: {game['description_raw'][:200]}...")
회원가입: https://rawg.io/apidocs
The Movie Database (TMDB)
특징: 영화, TV 프로그램, 배우 정보
제한: 무제한 (Rate limit 있음)
인증: API Key
const API_KEY = 'your_api_key';
// 인기 영화
fetch(`https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}&language=ko-KR`)
.then(res => res.json())
.then(data => {
data.results.forEach(movie => {
console.log(`${movie.title} (${movie.release_date})`);
console.log(`Rating: ${movie.vote_average}/10`);
console.log(`Poster: https://image.tmdb.org/t/p/w500${movie.poster_path}\n`);
});
});
// 영화 검색
fetch(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=Inception&language=ko-KR`)
.then(res => res.json())
.then(data => {
const movie = data.results[0];
console.log(`Title: ${movie.title}`);
console.log(`Overview: ${movie.overview}`);
});
회원가입: https://www.themoviedb.org/settings/api
8. 한국 공공데이터 API
공공데이터포털
제공 API:
- 국토교통부: 아파트 실거래가, 건축물 대장
- 환경부: 대기오염 정보, 미세먼지
- 행정안전부: 주소 검색, 우편번호
- 문화체육관광부: 관광 정보, 축제
- 보건복지부: 병원 정보, 약국 위치
import requests
API_KEY = 'your_api_key'
# 아파트 실거래가
url = 'http://openapi.molit.go.kr/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTradeDev'
params = {
'serviceKey': API_KEY,
'LAWD_CD': '11110', # 지역코드 (서울 종로구)
'DEAL_YMD': '202604', # 거래년월
'numOfRows': 10
}
response = requests.get(url, params=params)
# XML 파싱 필요
# 미세먼지 정보
url = 'http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty'
params = {
'serviceKey': API_KEY,
'returnType': 'json',
'numOfRows': 1,
'stationName': '종로구',
'dataTerm': 'DAILY'
}
response = requests.get(url, params=params)
data = response.json()
item = data['response']['body']['items'][0]
print(f"측정소: {item['stationName']}")
print(f"미세먼지(PM10): {item['pm10Value']}㎍/㎥")
print(f"초미세먼지(PM2.5): {item['pm25Value']}㎍/㎥")
print(f"통합대기환경지수: {item['khaiValue']} ({item['khaiGrade']})")
회원가입: https://www.data.go.kr/
서울 열린데이터광장
제공 API:
- 실시간 지하철 도착 정보
- 공공자전거 대여소 정보
- 문화행사 정보
- CCTV 교통 정보
import requests
API_KEY = 'your_api_key'
# 지하철 실시간 도착 정보
url = f'http://swopenapi.seoul.go.kr/api/subway/{API_KEY}/json/realtimeStationArrival/0/5/서울'
response = requests.get(url)
data = response.json()
for train in data['realtimeArrivalList']:
print(f"[{train['subwayId']}호선] {train['trainLineNm']}")
print(f"도착: {train['arvlMsg2']}")
print(f"방향: {train['bstatnNm']} → {train['statnNm']} → {train['endStnNm']}\n")
# 따릉이 대여소 정보
url = f'http://openapi.seoul.go.kr:8088/{API_KEY}/json/bikeList/1/10/'
response = requests.get(url)
data = response.json()
for station in data['rentBikeStatus']['row']:
print(f"{station['stationName']}: {station['parkingBikeTotCnt']}대 대여 가능")
회원가입: https://data.seoul.go.kr/
9. 기타 유용한 API
REST Countries
특징: 국가 정보 (250개국)
제한: 무제한
인증: 불필요
// 모든 국가
fetch('https://restcountries.com/v3.1/all')
.then(res => res.json())
.then(countries => {
countries.forEach(country => {
console.log(`${country.name.common}: ${country.capital?.[0]}`);
});
});
// 특정 국가
fetch('https://restcountries.com/v3.1/name/korea')
.then(res => res.json())
.then(data => {
const country = data[0];
console.log(`Name: ${country.name.common}`);
console.log(`Capital: ${country.capital[0]}`);
console.log(`Population: ${country.population.toLocaleString()}`);
console.log(`Languages: ${Object.values(country.languages).join(', ')}`);
console.log(`Flag: ${country.flag}`);
});
문서: https://restcountries.com/
JSONPlaceholder (테스트용 가짜 API)
특징: REST API 테스트용 더미 데이터
제한: 무제한
인증: 불필요
// GET
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(posts => {
posts.slice(0, 5).forEach(post => {
console.log(`${post.id}. ${post.title}`);
});
});
// POST
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'New Post',
body: 'This is a new post',
userId: 1
})
})
.then(res => res.json())
.then(data => console.log('Created:', data));
// PUT
fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: 1,
title: 'Updated Post',
body: 'Updated content',
userId: 1
})
})
.then(res => res.json())
.then(data => console.log('Updated:', data));
// DELETE
fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'DELETE'
})
.then(() => console.log('Deleted'));
문서: https://jsonplaceholder.typicode.com/
IP Geolocation
특징: IP 주소 위치 정보
제한: 1,000 requests/day (무료)
인증: 불필요
import requests
# 현재 IP 위치
response = requests.get('https://ipapi.co/json/')
data = response.json()
print(f"IP: {data['ip']}")
print(f"City: {data['city']}")
print(f"Region: {data['region']}")
print(f"Country: {data['country_name']}")
print(f"Latitude: {data['latitude']}")
print(f"Longitude: {data['longitude']}")
# 특정 IP 조회
ip = '8.8.8.8'
response = requests.get(f'https://ipapi.co/{ip}/json/')
data = response.json()
print(data)
QR Code Generator
특징: QR 코드 생성
제한: 무제한
인증: 불필요
<!-- URL로 QR 코드 생성 -->
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=https://example.com" alt="QR Code">
<!-- 텍스트 -->
<img src="https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=Hello%20World" alt="QR">
<!-- 색상 지정 -->
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=https://example.com&color=0000ff&bgcolor=ffffff" alt="Blue QR">
문서: https://goqr.me/api/
카테고리별 API 목록
날씨 및 환경
| API | 특징 | 제한 | 인증 |
|---|---|---|---|
| OpenWeatherMap | 날씨, 예보 | 60/min | API Key |
| WeatherAPI | 날씨, 공기질 | 1M/month | API Key |
| 기상청 | 한국 날씨 | 1,000/day | API Key |
| AirVisual | 공기질 | 10,000/month | API Key |
지도 및 위치
| API | 특징 | 제한 | 인증 |
|---|---|---|---|
| Google Maps | 지도, 경로 | $200 credit | API Key |
| Kakao Map | 한국 지도 | 300,000/day | API Key |
| Naver Map | 한국 지도 | 제한 있음 | Client ID |
| OpenStreetMap | 오픈소스 지도 | 1/sec | 불필요 |
금융 및 경제
| API | 특징 | 제한 | 인증 |
|---|---|---|---|
| Alpha Vantage | 주식, 환율 | 25/day | API Key |
| CoinGecko | 암호화폐 | 10-50/min | 불필요 |
| ExchangeRate-API | 환율 | 1,500/month | API Key |
| 한국은행 | 경제 통계 | 제한 있음 | API Key |
AI 및 머신러닝
| API | 특징 | 제한 | 인증 |
|---|---|---|---|
| OpenAI | GPT, DALL-E | $5 credit | API Key |
| Hugging Face | AI 모델 | 30,000/month | Token |
| Google Gemini | Gemini Pro | 60/min | API Key |
| Stability AI | Stable Diffusion | 제한 있음 | API Key |
이미지 및 미디어
| API | 특징 | 제한 | 인증 |
|---|---|---|---|
| Unsplash | 고품질 이미지 | 50/hour | Access Key |
| Pexels | 이미지, 비디오 | 200/hour | API Key |
| Pixabay | 무료 이미지 | 100/min | API Key |
| Lorem Picsum | 플레이스홀더 | 무제한 | 불필요 |
뉴스 및 소셜
| API | 특징 | 제한 | 인증 |
|---|---|---|---|
| NewsAPI | 전 세계 뉴스 | 100/day | API Key |
| Reddit 데이터 | 60/min | OAuth2 | |
| 트윗, 타임라인 | 제한 있음 | OAuth | |
| YouTube | 비디오 검색 | 10,000 units/day | API Key |
게임 및 엔터테인먼트
| API | 특징 | 제한 | 인증 |
|---|---|---|---|
| PokeAPI | 포켓몬 | 무제한 | 불필요 |
| RAWG | 게임 DB | 20,000/month | API Key |
| TMDB | 영화, TV | 무제한 | API Key |
| Spotify | 음악 메타데이터 | 제한 있음 | OAuth |
개발 도구
| API | 특징 | 제한 | 인증 |
|---|---|---|---|
| GitHub | 저장소, 이슈 | 5,000/hour | Token |
| JSONPlaceholder | 테스트 API | 무제한 | 불필요 |
| Random User | 가짜 사용자 | 무제한 | 불필요 |
| Lorem Ipsum | 더미 텍스트 | 무제한 | 불필요 |
10. API 사용 베스트 프랙티스
1. API 키 보안
# .env 파일
OPENWEATHER_API_KEY=abc123def456
GOOGLE_MAPS_API_KEY=xyz789uvw012
NEWS_API_KEY=qwe456rty789
# .gitignore에 추가
.env
.env.local
.env.production
// Node.js (dotenv)
require('dotenv').config();
const API_KEY = process.env.OPENWEATHER_API_KEY;
fetch(`https://api.openweathermap.org/data/2.5/weather?q=Seoul&appid=${API_KEY}`)
.then(res => res.json())
.then(data => console.log(data));
# Python (python-dotenv)
from dotenv import load_dotenv
import os
load_dotenv()
API_KEY = os.getenv('OPENWEATHER_API_KEY')
2. Rate Limiting 처리
import time
import requests
from functools import wraps
def rate_limit(max_per_second):
"""Rate limiting 데코레이터"""
min_interval = 1.0 / max_per_second
last_called = [0.0]
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
elapsed = time.time() - last_called[0]
left_to_wait = min_interval - elapsed
if left_to_wait > 0:
time.sleep(left_to_wait)
result = func(*args, **kwargs)
last_called[0] = time.time()
return result
return wrapper
return decorator
@rate_limit(1) # 초당 1회
def fetch_weather(city):
url = f'https://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}'
return requests.get(url).json()
# 사용
cities = ['Seoul', 'Busan', 'Incheon', 'Daegu', 'Daejeon']
for city in cities:
data = fetch_weather(city)
print(f"{city}: {data['main']['temp']}°C")
# 자동으로 1초 대기
3. 에러 처리
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (!response.ok) {
if (response.status === 429) {
// Rate limit 초과
const retryAfter = response.headers.get('Retry-After') || 60;
console.log(`⏳ Rate limited. Waiting ${retryAfter}s...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
if (response.status >= 500) {
// 서버 오류 (재시도)
console.log(`❌ Server error (${response.status}). Retry ${i+1}/${maxRetries}...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
continue;
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
if (i === maxRetries - 1) throw error;
console.log(`❌ Error: ${error.message}. Retry ${i+1}/${maxRetries}...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
// 사용
try {
const data = await fetchWithRetry('https://api.example.com/data');
console.log(data);
} catch (error) {
console.error('Failed after retries:', error);
}
4. 캐싱
import requests
import time
from functools import lru_cache
import hashlib
import json
import os
class APICache:
def __init__(self, cache_dir='.cache', ttl=3600):
self.cache_dir = cache_dir
self.ttl = ttl
os.makedirs(cache_dir, exist_ok=True)
def get_cache_path(self, url):
"""URL을 캐시 파일 경로로 변환"""
url_hash = hashlib.md5(url.encode()).hexdigest()
return os.path.join(self.cache_dir, f"{url_hash}.json")
def get(self, url):
"""캐시에서 데이터 조회"""
cache_path = self.get_cache_path(url)
if os.path.exists(cache_path):
mtime = os.path.getmtime(cache_path)
if time.time() - mtime < self.ttl:
with open(cache_path, 'r') as f:
return json.load(f)
return None
def set(self, url, data):
"""캐시에 데이터 저장"""
cache_path = self.get_cache_path(url)
with open(cache_path, 'w') as f:
json.dump(data, f)
def fetch_with_cache(url, cache_ttl=3600):
"""캐싱된 API 요청"""
cache = APICache(ttl=cache_ttl)
# 캐시 확인
cached = cache.get(url)
if cached:
print(f"✅ Cache hit: {url}")
return cached
# API 요청
print(f"📡 Fetching: {url}")
response = requests.get(url)
data = response.json()
# 캐시 저장
cache.set(url, data)
return data
# 사용
url = f'https://api.openweathermap.org/data/2.5/weather?q=Seoul&appid={API_KEY}'
data = fetch_with_cache(url, cache_ttl=1800) # 30분 캐싱
print(data)
5. 백엔드 프록시 (API 키 숨기기)
// server.js (Express)
const express = require('express');
const axios = require('axios');
require('dotenv').config();
const app = express();
// CORS 설정
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
// 날씨 API 프록시
app.get('/api/weather/:city', async (req, res) => {
try {
const { city } = req.params;
const API_KEY = process.env.OPENWEATHER_API_KEY;
const response = await axios.get(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric`
);
res.json(response.data);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 뉴스 API 프록시
app.get('/api/news', async (req, res) => {
try {
const API_KEY = process.env.NEWS_API_KEY;
const response = await axios.get(
`https://newsapi.org/v2/top-headlines?country=kr&apiKey=${API_KEY}`
);
res.json(response.data);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000, () => {
console.log('✅ Proxy server running on port 3000');
});
// 프론트엔드에서 프록시 사용
fetch('/api/weather/Seoul')
.then(res => res.json())
.then(data => console.log(data));
// API 키가 노출되지 않음!
실전 프로젝트 아이디어
프로젝트 1: 날씨 대시보드
// weather-dashboard.js
const API_KEY = process.env.OPENWEATHER_API_KEY;
async function createWeatherDashboard() {
const cities = ['Seoul', 'Busan', 'Incheon', 'Daegu', 'Daejeon'];
const weatherData = await Promise.all(
cities.map(city =>
fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric&lang=kr`)
.then(res => res.json())
)
);
weatherData.forEach(data => {
console.log(`\n${data.name}`);
console.log(` 온도: ${data.main.temp}°C (체감: ${data.main.feels_like}°C)`);
console.log(` 날씨: ${data.weather[0].description}`);
console.log(` 습도: ${data.main.humidity}%`);
console.log(` 풍속: ${data.wind.speed}m/s`);
});
}
createWeatherDashboard();
프로젝트 2: 암호화폐 트래커
import requests
import time
from datetime import datetime
def crypto_tracker():
"""실시간 암호화폐 가격 추적"""
coins = ['bitcoin', 'ethereum', 'ripple', 'cardano', 'solana']
while True:
url = 'https://api.coingecko.com/api/v3/simple/price'
params = {
'ids': ','.join(coins),
'vs_currencies': 'usd,krw',
'include_24hr_change': 'true',
'include_market_cap': 'true'
}
response = requests.get(url, params=params)
data = response.json()
print(f"\n{'='*60}")
print(f"Crypto Tracker - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"{'='*60}\n")
for coin, prices in data.items():
change = prices['usd_24h_change']
emoji = '📈' if change > 0 else '📉'
print(f"{emoji} {coin.upper()}")
print(f" USD: ${prices['usd']:,.2f} ({change:+.2f}%)")
print(f" KRW: ₩{prices['krw']:,.0f}")
print(f" Cap: ${prices['usd_market_cap']:,.0f}\n")
time.sleep(60) # 1분마다 업데이트
crypto_tracker()
프로젝트 3: 뉴스 애그리게이터
// news-aggregator.js
const NEWS_API_KEY = process.env.NEWS_API_KEY;
async function fetchNews(category = 'technology') {
const url = `https://newsapi.org/v2/top-headlines?country=kr&category=${category}&apiKey=${NEWS_API_KEY}`;
const response = await fetch(url);
const data = await response.json();
return data.articles.map(article => ({
title: article.title,
description: article.description,
url: article.url,
source: article.source.name,
publishedAt: new Date(article.publishedAt),
image: article.urlToImage
}));
}
async function createNewsFeed() {
const categories = ['technology', 'business', 'science'];
for (const category of categories) {
console.log(`\n📰 ${category.toUpperCase()}\n`);
const articles = await fetchNews(category);
articles.slice(0, 5).forEach((article, i) => {
console.log(`${i+1}. ${article.title}`);
console.log(` Source: ${article.source}`);
console.log(` URL: ${article.url}\n`);
});
}
}
createNewsFeed();
프로젝트 4: 영화 추천 시스템
import requests
TMDB_API_KEY = 'your_api_key'
def get_popular_movies(page=1):
"""인기 영화 조회"""
url = f'https://api.themoviedb.org/3/movie/popular'
params = {
'api_key': TMDB_API_KEY,
'language': 'ko-KR',
'page': page
}
response = requests.get(url, params=params)
return response.json()['results']
def get_movie_recommendations(movie_id):
"""영화 추천"""
url = f'https://api.themoviedb.org/3/movie/{movie_id}/recommendations'
params = {
'api_key': TMDB_API_KEY,
'language': 'ko-KR'
}
response = requests.get(url, params=params)
return response.json()['results']
def search_movies(query):
"""영화 검색"""
url = f'https://api.themoviedb.org/3/search/movie'
params = {
'api_key': TMDB_API_KEY,
'language': 'ko-KR',
'query': query
}
response = requests.get(url, params=params)
return response.json()['results']
# 사용
print("🎬 인기 영화 Top 10")
movies = get_popular_movies()
for i, movie in enumerate(movies[:10], 1):
print(f"{i}. {movie['title']} ({movie['release_date'][:4]})")
print(f" 평점: {movie['vote_average']}/10")
print("\n🔍 '인셉션' 검색")
results = search_movies('Inception')
if results:
movie = results[0]
print(f"제목: {movie['title']}")
print(f"개봉: {movie['release_date']}")
print(f"줄거리: {movie['overview']}")
print("\n💡 추천 영화")
recommendations = get_movie_recommendations(movie['id'])
for rec in recommendations[:5]:
print(f" - {rec['title']} ({rec['vote_average']}/10)")
API 통합 예제
멀티 API 대시보드
#!/usr/bin/env python3
"""
멀티 API 통합 대시보드
"""
import requests
import os
from datetime import datetime
class DashboardAPI:
def __init__(self):
self.weather_key = os.getenv('OPENWEATHER_API_KEY')
self.news_key = os.getenv('NEWS_API_KEY')
self.crypto_enabled = True
def get_weather(self, city='Seoul'):
"""날씨 정보"""
url = f'https://api.openweathermap.org/data/2.5/weather'
params = {
'q': city,
'appid': self.weather_key,
'units': 'metric',
'lang': 'kr'
}
response = requests.get(url, params=params)
data = response.json()
return {
'city': data['name'],
'temp': data['main']['temp'],
'description': data['weather'][0]['description'],
'humidity': data['main']['humidity']
}
def get_crypto_prices(self):
"""암호화폐 시세"""
url = 'https://api.coingecko.com/api/v3/simple/price'
params = {
'ids': 'bitcoin,ethereum',
'vs_currencies': 'usd,krw',
'include_24hr_change': 'true'
}
response = requests.get(url, params=params)
return response.json()
def get_news(self, category='technology'):
"""뉴스 헤드라인"""
url = 'https://newsapi.org/v2/top-headlines'
params = {
'country': 'kr',
'category': category,
'apiKey': self.news_key,
'pageSize': 5
}
response = requests.get(url, params=params)
data = response.json()
return [
{
'title': article['title'],
'source': article['source']['name'],
'url': article['url']
}
for article in data['articles']
]
def display_dashboard(self):
"""대시보드 출력"""
print(f"\n{'='*70}")
print(f"📊 Dashboard - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"{'='*70}\n")
# 날씨
print("🌤️ 날씨")
weather = self.get_weather()
print(f" {weather['city']}: {weather['temp']}°C, {weather['description']}")
print(f" 습도: {weather['humidity']}%\n")
# 암호화폐
print("💰 암호화폐")
crypto = self.get_crypto_prices()
for coin, prices in crypto.items():
change = prices['usd_24h_change']
emoji = '📈' if change > 0 else '📉'
print(f" {emoji} {coin.upper()}: ${prices['usd']:,.2f} ({change:+.2f}%)")
print()
# 뉴스
print("📰 기술 뉴스")
news = self.get_news('technology')
for i, article in enumerate(news, 1):
print(f" {i}. {article['title']}")
print(f" {article['source']}\n")
# 실행
dashboard = DashboardAPI()
dashboard.display_dashboard()
API 키 관리 도구
환경 변수 관리
# .env.example (템플릿)
OPENWEATHER_API_KEY=your_key_here
GOOGLE_MAPS_API_KEY=your_key_here
NEWS_API_KEY=your_key_here
TMDB_API_KEY=your_key_here
# .env (실제 키, .gitignore에 추가)
OPENWEATHER_API_KEY=abc123def456
GOOGLE_MAPS_API_KEY=xyz789uvw012
NEWS_API_KEY=qwe456rty789
TMDB_API_KEY=asd789fgh012
Vercel/Netlify 환경 변수
# Vercel
vercel env add OPENWEATHER_API_KEY
# 값 입력: abc123def456
# Netlify
netlify env:set OPENWEATHER_API_KEY abc123def456
# GitHub Actions
# Settings → Secrets → New repository secret
# Name: OPENWEATHER_API_KEY
# Value: abc123def456
추가 무료 API 목록
유틸리티
📧 이메일 검증: https://hunter.io/ (50/month)
📱 SMS 전송: https://www.twilio.com/ ($15 credit)
🔗 URL 단축: https://tinyurl.com/app/dev (무제한)
📊 QR 코드: https://goqr.me/api/ (무제한)
🌐 번역: https://libretranslate.com/ (무제한)
데이터베이스
🗄️ Firebase: https://firebase.google.com/ (Spark 플랜)
🐘 Supabase: https://supabase.com/ (500MB)
🍃 MongoDB Atlas: https://www.mongodb.com/cloud/atlas (512MB)
🔥 PlanetScale: https://planetscale.com/ (5GB)
인증
// 실행 예제
🔐 Auth0: https://auth0.com/ (7,000 users)
🎫 Clerk: https://clerk.com/ (10,000 users)
👤 Firebase Auth: https://firebase.google.com/ (무제한)
🔑 Supabase Auth: https://supabase.com/ (50,000 users)
결제
💳 Stripe: https://stripe.com/ (테스트 모드 무제한)
💰 PayPal: https://developer.paypal.com/ (샌드박스)
🏦 Toss Payments: https://docs.tosspayments.com/ (테스트)
이메일
📬 SendGrid: https://sendgrid.com/ (100/day)
📮 Mailgun: https://www.mailgun.com/ (5,000/month)
✉️ Resend: https://resend.com/ (100/day)
분석
📈 Google Analytics: https://analytics.google.com/ (무제한)
📊 Plausible: https://plausible.io/ (10,000 pageviews)
🔍 PostHog: https://posthog.com/ (1M events)
API 테스트 도구
Postman
// Postman Collection 예시
{
"info": {
"name": "Weather API Collection",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Get Current Weather",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "https://api.openweathermap.org/data/2.5/weather?q=Seoul&appid={{API_KEY}}&units=metric",
"host": ["api", "openweathermap", "org"],
"path": ["data", "2.5", "weather"],
"query": [
{"key": "q", "value": "Seoul"},
{"key": "appid", "value": "{{API_KEY}}"},
{"key": "units", "value": "metric"}
]
}
}
}
]
}
curl 테스트
# GET 요청
curl "https://api.openweathermap.org/data/2.5/weather?q=Seoul&appid=YOUR_API_KEY"
# 헤더 포함
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://api.example.com/data"
# POST 요청
curl -X POST \
-H "Content-Type: application/json" \
-d '{"name":"John","email":"[email protected]"}' \
"https://api.example.com/users"
# 응답 저장
curl -o response.json "https://api.example.com/data"
# 진행률 표시
curl --progress-bar -o large_file.zip "https://example.com/file.zip"
제한사항 및 주의사항
Rate Limiting 전략
flowchart TB
Request[API 요청] --> Check{Rate Limit\n확인}
Check -->|OK| Call[API 호출]
Check -->|초과| Wait[대기]
Call --> Success{성공?}
Success -->|200| Cache[캐시 저장]
Success -->|429| Retry["재시도\nExponential Backoff"]
Success -->|5xx| Retry
Success -->|4xx| Error[에러 처리]
Wait --> Sleep[Sleep]
Sleep --> Check
Retry --> Sleep2[대기 시간 증가]
Sleep2 --> Call
Cache --> Return[결과 반환]
Error --> Return
무료 플랜 제한 비교
일일 요청 제한:
Alpha Vantage: 25 requests/day
NewsAPI: 100 requests/day
기상청: 1,000 requests/day
Kakao Map: 300,000 requests/day
월별 요청 제한:
OpenWeatherMap: 1,000,000 requests/month
WeatherAPI: 1,000,000 requests/month
ExchangeRate: 1,500 requests/month
RAWG: 20,000 requests/month
시간당 요청 제한:
Unsplash: 50 requests/hour
Pexels: 200 requests/hour
GitHub: 5,000 requests/hour (인증)
분당 요청 제한:
OpenWeatherMap: 60 requests/min
CoinGecko: 10-50 requests/min
Reddit: 60 requests/min
정리
API 선택 체크리스트
프로젝트 요구사항:
- [ ] 필요한 데이터 타입 확인
- [ ] 예상 요청 횟수 계산
- [ ] 상업적 사용 여부 확인
- [ ] 응답 속도 요구사항
- [ ] 데이터 신선도 (실시간 vs 캐싱)
API 평가:
- [ ] 무료 제한 확인
- [ ] 인증 방식 확인
- [ ] 문서 품질 확인
- [ ] 커뮤니티 활성도
- [ ] 안정성 (SLA)
보안:
- [ ] API 키 환경 변수 관리
- [ ] .gitignore에 .env 추가
- [ ] 프론트엔드는 백엔드 프록시 사용
- [ ] HTTPS 사용
- [ ] Rate limiting 구현
최적화:
- [ ] 캐싱 구현
- [ ] 에러 처리 및 재시도
- [ ] 요청 배칭
- [ ] 응답 압축
프로젝트 아이디어
초급:
✅ 날씨 앱 (OpenWeatherMap)
✅ 환율 계산기 (ExchangeRate-API)
✅ 영화 검색 (TMDB)
✅ 뉴스 리더 (NewsAPI)
중급:
✅ 암호화폐 트래커 (CoinGecko)
✅ 지도 기반 장소 검색 (Google Maps)
✅ 주식 차트 (Alpha Vantage)
✅ 이미지 갤러리 (Unsplash)
고급:
✅ AI 챗봇 (OpenAI)
✅ 영화 추천 시스템 (TMDB)
✅ 실시간 대시보드 (멀티 API)
✅ 데이터 시각화 (여러 API 통합)
핵심 팁
1. 무료 플랜 제한 확인
→ 일일/월별 요청 횟수 계산
2. API 키 보안
→ 환경 변수 사용, GitHub에 노출 금지
3. 캐싱 활용
→ 불필요한 요청 줄이기
4. 에러 처리
→ 429 (Rate Limit), 5xx (서버 오류) 재시도
5. 백엔드 프록시
→ 프론트엔드에서 API 키 숨기기
6. 약관 확인
→ 상업적 사용 제한 여부
7. 대체 API 준비
→ 메인 API 장애 시 폴백
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. 개인 프로젝트에 바로 사용할 수 있는 무료 공공 API 100개 이상. 날씨, 지도, 금융, AI, 이미지, 뉴스, 게임, 공공데이터까지. 인증 방법, 사용 예제, 제한사항 완벽 정리. Start now. 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
참고 자료
- Public APIs GitHub
- RapidAPI
- API List
- 공공데이터포털
- 서울 열린데이터광장 한 줄 요약: 개인 프로젝트는 OpenWeatherMap(날씨), CoinGecko(암호화폐), NewsAPI(뉴스), TMDB(영화), Unsplash(이미지) 등 무료 API를 활용하되, Rate Limiting과 API 키 보안을 반드시 고려하세요.
심화 부록: 구현·운영 관점
이 부록은 앞선 본문에서 다룬 주제(「무료 API 완벽 가이드 | 개인 프로젝트용 공공 API 100+ 총정리」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.
내부 동작과 핵심 메커니즘
flowchart TD A[입력·요청·이벤트] --> B[파싱·검증·디코딩] B --> C[핵심 연산·상태 전이] C --> D[부작용: I/O·네트워크·동시성] D --> E[결과·관측·저장]
sequenceDiagram participant C as 클라이언트/호출자 participant B as 경계(런타임·게이트웨이·프로세스) participant D as 의존성(API·DB·큐·파일) C->>B: 요청/이벤트 B->>D: 조회·쓰기·RPC D-->>B: 지연·부분 실패·재시도 가능 B-->>C: 응답 또는 오류(코드·상관 ID)
- 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
- 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
- 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
- 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.
프로덕션 운영 패턴
| 영역 | 운영 관점 질문 |
|---|---|
| 관측성 | 요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가 |
| 안전성 | 입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가 |
| 신뢰성 | 재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가 |
| 성능 | 캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가 |
| 배포 | 롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가 |
| 용량 | 피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가 |
스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.
확장 예시: 엔드투엔드 미니 시나리오
앞선 본문 주제(「무료 API 완벽 가이드 | 개인 프로젝트용 공공 API 100+ 총정리」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.
- 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
- 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
- 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
- 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
- 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
ctx = newCorrelationId()
validated = validateSchema(request)
authorize(validated, ctx)
result = domainCore(validated)
persistOrEmit(result, idempotentKey)
recordMetrics(ctx, latency, outcome)
return result
문제 해결(Troubleshooting)
| 증상 | 가능 원인 | 조치 |
|---|---|---|
| 간헐적 실패 | 레이스, 타임아웃, 외부 의존성, DNS | 최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검 |
| 성능 저하 | N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스 | 프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거 |
| 메모리 증가 | 캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납 | 상한·TTL·힙/FD 스냅샷 비교 |
| 빌드·배포만 실패 | 환경 변수, 권한, 플랫폼 차이, lockfile | CI 로그와 로컬 diff, 런타임·이미지 버전 핀 |
| 설정 불일치 | 프로필·시크릿·기본값, 리전 | 스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화 |
| 데이터 불일치 | 비멱등 재시도, 부분 쓰기, 캐시 무효화 누락 | 멱등 키·아웃박스·트랜잭션 경계 재검토 |
권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.
배포 전에는 git add → git commit → git push 후 npm run deploy 순서를 권장합니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 코드 커버리지 완벽 가이드 | gcov, lcov, Codecov 실전 활용
- C++ REST API 서버 만들기 | 라우팅·미들웨어·인증·Swagger 문서화 [#50-2]
- RAG 구현 완벽 가이드 | 검색 증강 생성으로 LLM 성능 향상
이 글에서 다루는 키워드 (관련 검색어)
API, 무료API, 공공API, REST, OpenAPI, 개인프로젝트 등으로 검색하시면 이 글이 도움이 됩니다.