콘텐츠로 이동

13장: 웹 API 맛보기 - 데이터 받아오기

🎯 이 장에서 배우는 것

  • [ ] HTTP 요청의 기본 개념을 설명할 수 있다
  • [ ] urequests로 API를 호출할 수 있다
  • [ ] JSON 데이터를 파싱하여 활용할 수 있다

💡 왜 이걸 배우나요?

스마트폰 날씨 앱은 어떻게 전 세계 날씨를 알까요? 직접 측정할까요? 아닙니다! 인터넷에서 데이터를 받아오는 거예요.

인터넷에는 날씨, 뉴스, 주가, 환율 등 수많은 데이터가 API(Application Programming Interface)라는 형태로 제공됩니다. API는 마치 데이터 자판기와 같아요. 요청을 보내면 원하는 데이터를 돌려줍니다.

우리 피코도 WiFi에 연결했으니, 이제 전 세계의 데이터를 받아올 수 있어요! 오늘은 실제로 OpenWeatherMap에서 현재 날씨를 받아와 볼 거예요. 🌤️


📚 핵심 개념

개념 1: HTTP 요청과 응답

  1. 비유로 시작: "HTTP는 마치 식당 주문 시스템과 같아요. 손님(피코)이 주문서(요청)를 보내면, 주방(서버)이 음식(데이터)을 내어줍니다."

  2. 정확한 정의: HTTP(HyperText Transfer Protocol)는 웹에서 데이터를 주고받는 규칙입니다. 클라이언트가 요청(Request)을 보내면, 서버가 응답(Response)을 돌려줍니다.

  3. 예시로 확인:

    피코: "서울 날씨 주세요!" (요청)
    서버: "현재 15도, 맑음입니다" (응답)
    

쉽게 말하면: 질문하면 답을 주는 대화 방식이에요!

sequenceDiagram participant P as 🔌 피코 participant S as 🌐 날씨 서버 P->>S: GET /weather?city=Seoul (요청) S->>P: {"temp": 15, "sky": "맑음"} (응답)

개념 2: API와 API 키

  1. 비유로 시작: "API는 데이터 자판기예요. 동전(API 키)을 넣고 버튼(요청)을 누르면 음료(데이터)가 나와요."

  2. 정확한 정의: API는 프로그램끼리 데이터를 주고받는 약속된 방법입니다. API 키는 "누가 요청했는지" 확인하는 신분증이에요.

  3. 예시로 확인: OpenWeatherMap은 무료로 날씨 데이터를 제공하지만, API 키가 있어야 사용할 수 있어요.

쉽게 말하면: API는 데이터 창구, API 키는 입장권!


개념 3: JSON 데이터 형식

  1. 비유로 시작: "JSON은 정리된 서랍장과 같아요. 각 서랍에 이름표가 붙어있고, 그 안에 물건(데이터)이 들어있죠."

  2. 정확한 정의: JSON(JavaScript Object Notation)은 데이터를 {"키": "값"} 형태로 저장하는 형식입니다. 사람도 읽기 쉽고, 컴퓨터도 처리하기 쉬워요.

  3. 예시로 확인:

    {
        "city": "Seoul",
        "temp": 15,
        "weather": "맑음"
    }
    

쉽게 말하면: 이름표가 붙은 데이터 묶음이에요!


🔨 따라하기

Step 1: HTTP 요청 보내기

목표: urequests 라이브러리로 웹 페이지 받아오기

먼저 간단한 테스트 사이트에 요청을 보내봅시다.

코드 (step1_http_request.py):

# === WHAT: 웹 서버에 HTTP 요청 보내기 ===
# 인터넷의 데이터를 받아오는 첫 걸음!

# --- WHY: 왜 필요한지 ---
# API를 사용하려면 먼저 HTTP 요청을 보낼 줄 알아야 해요

import network      # WiFi 연결용
import urequests    # HTTP 요청용
import time

# HOW: 어떻게 동작하는지

# 1. WiFi 연결 (11차시에서 배운 내용)
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("your_wifi_name", "your_wifi_password")  # 🔧 수정하세요!

print("WiFi 연결 중...")
while not wlan.isconnected():
    time.sleep(1)
print("WiFi 연결 완료!")
print(f"IP: {wlan.ifconfig()[0]}")

# 2. HTTP GET 요청 보내기
url = "http://httpbin.org/ip"  # 테스트용 사이트
print(f"\n{url} 에 요청 중...")

response = urequests.get(url)  # GET 요청!

# 3. 응답 확인
print(f"상태 코드: {response.status_code}")  # 200이면 성공
print(f"응답 내용: {response.text}")

response.close()  # 연결 닫기 (중요!)

실행 결과:

WiFi 연결 완료!
IP: 192.168.0.15

http://httpbin.org/ip 에 요청 중...
상태 코드: 200
응답 내용: {
  "origin": "123.45.67.89"
}

여기서 잠깐! 🤔 - 상태 코드 200은 "성공"이라는 뜻이에요 - response.close()를 꼭 호출해야 메모리 누수를 막아요!


Step 2: 날씨 API 호출하기

목표: OpenWeatherMap API로 실제 날씨 데이터 받아오기

📌 사전 준비: openweathermap.org에서 무료 계정을 만들고 API 키를 받으세요! (가입 후 이메일 인증 필요)

코드 (step2_weather_api.py):

# === WHAT: OpenWeatherMap 날씨 API 호출 ===
# 실제 서울의 현재 날씨를 받아옵니다!

import network
import urequests
import time

# WiFi 연결 (간략화)
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("your_wifi_name", "your_wifi_password")  # 🔧 수정!

while not wlan.isconnected():
    time.sleep(1)
print("WiFi 연결 완료!")

# --- 날씨 API 설정 ---
API_KEY = "your_api_key_here"  # 🔧 여기에 API 키 입력!
CITY = "Seoul"

# API URL 만들기
url = f"http://api.openweathermap.org/data/2.5/weather?q={CITY}&appid={API_KEY}&units=metric"
# units=metric: 섭씨 온도로 받기

print(f"\n{CITY} 날씨 요청 중...")
response = urequests.get(url)

if response.status_code == 200:
    print("✅ 성공!")
    print(response.text[:200])  # 처음 200자만 출력
else:
    print(f"❌ 오류: {response.status_code}")

response.close()

실행 결과:

WiFi 연결 완료!

Seoul 날씨 요청 중...
✅ 성공!
{"coord":{"lon":126.9778,"lat":37.5683},"weather":[{"id":800,"main":"Clear","description":"clear sky"}],"main":{"temp":15.2,"feels_like":14.1}...

여기서 잠깐! 🤔 받아온 데이터가 복잡해 보이죠? 이게 바로 JSON이에요! 다음 단계에서 필요한 정보만 뽑아볼게요.


Step 3: JSON 파싱하여 날씨 표시하기

목표: JSON에서 온도와 날씨 정보 추출하기

코드 (step3_json_parse.py):

# === WHAT: JSON 데이터 파싱하기 ===
# 복잡한 JSON에서 원하는 정보만 쏙쏙 뽑아요!

import network
import urequests
import time

# WiFi 연결
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("your_wifi_name", "your_wifi_password")  # 🔧 수정!

while not wlan.isconnected():
    time.sleep(1)
print("WiFi 연결 완료!\n")

# 날씨 API 호출
API_KEY = "your_api_key_here"  # 🔧 수정!
CITY = "Seoul"

url = f"http://api.openweathermap.org/data/2.5/weather?q={CITY}&appid={API_KEY}&units=metric"
response = urequests.get(url)

if response.status_code == 200:
    # --- JSON 파싱하기 ---
    data = response.json()  # 문자열 → 딕셔너리 변환!

    # 원하는 정보 추출
    temp = data["main"]["temp"]           # 현재 온도
    feels_like = data["main"]["feels_like"]  # 체감 온도
    humidity = data["main"]["humidity"]    # 습도
    weather = data["weather"][0]["main"]   # 날씨 상태
    description = data["weather"][0]["description"]  # 상세 설명

    # 예쁘게 출력
    print("=" * 30)
    print(f"🌍 {CITY} 현재 날씨")
    print("=" * 30)
    print(f"🌡️ 온도: {temp}°C")
    print(f"🤔 체감: {feels_like}°C")
    print(f"💧 습도: {humidity}%")
    print(f"☁️ 날씨: {weather}")
    print(f"📝 설명: {description}")
    print("=" * 30)
else:
    print(f"오류 발생: {response.status_code}")

response.close()

실행 결과:

WiFi 연결 완료!

==============================
🌍 Seoul 현재 날씨
==============================
🌡️ 온도: 15.2°C
🤔 체감: 14.1°C
💧 습도: 62%
☁️ 날씨: Clear
📝 설명: clear sky
==============================

여기서 잠깐! 🤔 - response.json()이 마법처럼 JSON을 파이썬 딕셔너리로 바꿔줘요 - data["main"]["temp"]는 "main 서랍 안의 temp 값"을 의미해요

flowchart TD A["📥 JSON 응답"] --> B["🔄 response.json()"] B --> C["📦 Python 딕셔너리"] C --> D["🌡️ data['main']['temp']"] C --> E["☁️ data['weather'][0]['main']"] style A fill:#e3f2fd,stroke:#1976d2 style B fill:#fff3e0,stroke:#f57c00 style C fill:#e8f5e9,stroke:#388e3c style D fill:#fce4ec,stroke:#c2185b style E fill:#fce4ec,stroke:#c2185b

📝 전체 코드

# === 서울 날씨 알리미 완성판 ===
# WiFi 연결 → API 호출 → JSON 파싱 → 출력

import network
import urequests
import time

# ====== 설정 (여기만 수정하세요!) ======
WIFI_SSID = "your_wifi_name"      # 🔧 WiFi 이름
WIFI_PASSWORD = "your_password"    # 🔧 WiFi 비밀번호
API_KEY = "your_api_key"          # 🔧 OpenWeatherMap API 키
CITY = "Seoul"                     # 도시 이름 (영어)
# ======================================

def connect_wifi():
    """WiFi에 연결합니다"""
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(WIFI_SSID, WIFI_PASSWORD)

    print("WiFi 연결 중", end="")
    timeout = 10
    while not wlan.isconnected() and timeout > 0:
        print(".", end="")
        time.sleep(1)
        timeout -= 1

    if wlan.isconnected():
        print("\n✅ WiFi 연결 성공!")
        return True
    else:
        print("\n❌ WiFi 연결 실패")
        return False

def get_weather():
    """날씨 정보를 가져옵니다"""
    url = f"http://api.openweathermap.org/data/2.5/weather?q={CITY}&appid={API_KEY}&units=metric"

    try:
        response = urequests.get(url)

        if response.status_code == 200:
            data = response.json()
            weather_info = {
                "temp": data["main"]["temp"],
                "feels_like": data["main"]["feels_like"],
                "humidity": data["main"]["humidity"],
                "weather": data["weather"][0]["main"],
                "description": data["weather"][0]["description"]
            }
            response.close()
            return weather_info
        else:
            response.close()
            return None
    except Exception as e:
        print(f"오류: {e}")
        return None

def display_weather(info):
    """날씨 정보를 예쁘게 출력합니다"""
    print("\n" + "=" * 30)
    print(f"🌍 {CITY} 현재 날씨")
    print("=" * 30)
    print(f"🌡️ 온도: {info['temp']}°C")
    print(f"🤔 체감: {info['feels_like']}°C")
    print(f"💧 습도: {info['humidity']}%")
    print(f"☁️ 날씨: {info['weather']}")
    print(f"📝 설명: {info['description']}")
    print("=" * 30)

# 메인 실행
if connect_wifi():
    weather = get_weather()
    if weather:
        display_weather(weather)
    else:
        print("날씨 정보를 가져오지 못했습니다.")

⚠️ 자주 하는 실수

실수 1: API 키를 그대로 사용

증상: {"cod": 401, "message": "Invalid API key"}

원인: API 키를 발급받지 않았거나, 복사할 때 공백이 포함됨

해결:

# 잘못된 코드
API_KEY = "your_api_key_here"  # 예시 그대로 사용

# 올바른 코드
API_KEY = "a1b2c3d4e5f6..."  # 본인의 실제 API 키!


실수 2: response.close() 누락

증상: 여러 번 실행하면 OSError: [Errno 12] ENOMEM (메모리 부족)

원인: 연결을 닫지 않아 메모리 누수 발생

해결:

# 잘못된 코드
response = urequests.get(url)
data = response.json()
# close() 없이 끝남

# 올바른 코드
response = urequests.get(url)
data = response.json()
response.close()  # 반드시 닫기!


실수 3: JSON 키 이름 오타

증상: KeyError: 'temprature'

원인: JSON 키 이름을 잘못 입력 (temp를 temprature로)

해결:

# 잘못된 코드
temp = data["main"]["temprature"]  # 오타!

# 올바른 코드
temp = data["main"]["temp"]  # 정확한 키 이름

# 💡 팁: 먼저 전체 데이터를 출력해서 키 이름 확인!
print(data)


✅ 스스로 점검하기

  1. HTTP GET 요청에서 "GET"의 의미는 무엇인가요?

  2. 다음 JSON에서 도시 이름을 추출하는 코드는?

    {"location": {"city": "Seoul", "country": "KR"}}
    

  3. response.close()를 꼭 호출해야 하는 이유는?

정답 확인 1. **GET**은 "데이터를 가져오다"라는 의미예요. 서버에서 정보를 **읽어오는** 요청입니다. (POST는 데이터를 **보내는** 요청) 2. ```python data = response.json() city = data["location"]["city"] # "Seoul" ``` 3. 피코는 메모리가 제한적이에요. `close()`를 호출하지 않으면 연결이 계속 열려있어 **메모리 누수**가 발생하고, 결국 프로그램이 멈춥니다!

🚀 더 해보기

도전 1: 다른 도시 날씨 받아오기

CITY = "Seoul"을 다른 도시로 바꿔보세요! - Tokyo, London, New York, Paris 등


도전 2: 날씨에 따른 이모지 표시

날씨 상태에 맞는 이모지를 출력하세요:

weather = data["weather"][0]["main"]

if weather == "Clear":
    emoji = "☀️"
elif weather == "Clouds":
    emoji = "☁️"
elif weather == "Rain":
    emoji = "🌧️"
# ... 더 추가해보세요!


도전 3: OLED에 날씨 표시하기 ⭐

10차시에서 배운 OLED를 활용해서 날씨를 화면에 표시해보세요!

힌트:

oled.fill(0)
oled.text(f"Seoul", 0, 0)
oled.text(f"Temp: {temp}C", 0, 16)
oled.text(f"Weather: {weather}", 0, 32)
oled.show()


🔗 다음 장으로

오늘 배운 것: - ✅ HTTP 요청으로 인터넷 데이터 받아오기 - ✅ API 키로 날씨 서비스 사용하기 - ✅ JSON 파싱으로 원하는 정보 추출하기

🎉 이제 우리 피코가 인터넷의 정보를 활용할 수 있게 됐어요!

다음 장에서는 12차시의 시계오늘의 날씨 API를 합쳐서 IoT 날씨 시계를 완성합니다. 시간과 날씨가 함께 표시되는 스마트 기기를 만들어봐요! ⏰🌤️