무료 API 완벽 가이드 | 개인 프로젝트용 공공 API 100+ 총정리

무료 API 완벽 가이드 | 개인 프로젝트용 공공 API 100+ 총정리

이 글의 핵심

개인 프로젝트에 바로 사용할 수 있는 무료 공공 API 100개 이상. 날씨, 지도, 금융, AI, 이미지, 뉴스, 게임, 공공데이터까지. 인증 방법, 사용 예제, 제한사항 완벽 정리.

들어가며: “프로젝트에 실제 데이터를 넣고 싶어요”

토이 프로젝트의 고민

포트폴리오용 날씨 앱을 만들고 싶은데 실제 날씨 데이터가 없거나, 지도 서비스를 구현하고 싶은데 지도 API가 비싸거나, 주식 차트를 그리고 싶은데 금융 데이터를 구할 수 없어 고민한 적 있으신가요? 다행히도 수많은 기업과 정부 기관이 무료 공공 API를 제공하여 개인 개발자도 실전 같은 프로젝트를 만들 수 있습니다.

이 글에서 다루는 것:

  • 100개 이상의 무료 API 카테고리별 정리
  • 각 API의 특징, 제한사항, 사용 예제
  • API 키 발급 및 인증 방법
  • 실전 프로젝트 아이디어

목차

  1. 날씨 API
  2. 지도 및 위치 API
  3. 금융 및 암호화폐 API
  4. AI 및 머신러닝 API
  5. 이미지 및 미디어 API
  6. 뉴스 및 소셜 API
  7. 게임 및 엔터테인먼트 API
  8. 한국 공공데이터 API
  9. 기타 유용한 API
  10. 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}`);
    });
  });

문서: https://pokeapi.co/

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)

문서: https://ipapi.co/

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/minAPI Key
WeatherAPI날씨, 공기질1M/monthAPI Key
기상청한국 날씨1,000/dayAPI Key
AirVisual공기질10,000/monthAPI Key

지도 및 위치

API특징제한인증
Google Maps지도, 경로$200 creditAPI Key
Kakao Map한국 지도300,000/dayAPI Key
Naver Map한국 지도제한 있음Client ID
OpenStreetMap오픈소스 지도1/sec불필요

금융 및 경제

API특징제한인증
Alpha Vantage주식, 환율25/dayAPI Key
CoinGecko암호화폐10-50/min불필요
ExchangeRate-API환율1,500/monthAPI Key
한국은행경제 통계제한 있음API Key

AI 및 머신러닝

API특징제한인증
OpenAIGPT, DALL-E$5 creditAPI Key
Hugging FaceAI 모델30,000/monthToken
Google GeminiGemini Pro60/minAPI Key
Stability AIStable Diffusion제한 있음API Key

이미지 및 미디어

API특징제한인증
Unsplash고품질 이미지50/hourAccess Key
Pexels이미지, 비디오200/hourAPI Key
Pixabay무료 이미지100/minAPI Key
Lorem Picsum플레이스홀더무제한불필요

뉴스 및 소셜

API특징제한인증
NewsAPI전 세계 뉴스100/dayAPI Key
RedditReddit 데이터60/minOAuth2
Twitter트윗, 타임라인제한 있음OAuth
YouTube비디오 검색10,000 units/dayAPI Key

게임 및 엔터테인먼트

API특징제한인증
PokeAPI포켓몬무제한불필요
RAWG게임 DB20,000/monthAPI Key
TMDB영화, TV무제한API Key
Spotify음악 메타데이터제한 있음OAuth

개발 도구

API특징제한인증
GitHub저장소, 이슈5,000/hourToken
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<br/>확인}
    
    Check -->|OK| Call[API 호출]
    Check -->|초과| Wait[대기]
    
    Call --> Success{성공?}
    Success -->|200| Cache[캐시 저장]
    Success -->|429| Retry[재시도<br/>Exponential 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 장애 시 폴백

참고 자료

한 줄 요약: 개인 프로젝트는 OpenWeatherMap(날씨), CoinGecko(암호화폐), NewsAPI(뉴스), TMDB(영화), Unsplash(이미지) 등 무료 API를 활용하되, Rate Limiting과 API 키 보안을 반드시 고려하세요.

---
... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3