날씨 데이터 저장하고
API 서버 + DB까지 만들기

오늘 한 것

#01에서 날씨 API를 호출해 터미널에 출력하는 것까지 만들었다. 이번엔 네 가지를 더 붙였다.

01
 
날씨 API 호출 (지난 포스팅)
requests.get() 으로 wttr.in API 호출, 딕셔너리로 가공
02
 
JSON 파일로 저장
결과를 result.json 파일로 저장
03
 
FastAPI 서버 만들기
브라우저에서 /weather/Seoul 로 조회
04
 
여러 도시 HTML 리포트 자동 생성
Python이 HTML 파일을 자동으로 만들어서 브라우저에서 확인
05
 
MySQL에 날씨 기록 저장
pymysql로 DB 연결, weather_log 테이블에 INSERT
JSON 파일로 저장하기

터미널 출력만으로는 결과가 사라진다. 파일로 저장해두면 나중에 읽거나 다른 곳에서 활용할 수 있다.

 
weather.py — 추가한 부분
import json  # 맨 위에 추가

with open("result.json", "w", encoding="utf-8") as f:
    json.dump(w, f, ensure_ascii=False, indent=2)

print("result.json 저장 완료!")
open()
파일을 열거나 새로 만든다. "w"는 쓰기 모드.
open("파일명", "w", encoding="utf-8")
with 문
블록이 끝나면 파일을 자동으로 닫아준다. Java의 try-with-resources와 같다.
with open(...) as f: ...
json.dump()
딕셔너리를 JSON 형식으로 파일에 저장한다.
json.dump(w, f, indent=2)
ensure_ascii=False
한글이 깨지지 않게 한다.
ensure_ascii=False
indent=2
들여쓰기를 적용해서 사람이 읽기 좋게 만든다.
indent=2 → 공백 2칸
FastAPI로 API 서버 만들기

터미널에서만 쓰던 걸 브라우저에서 URL로 조회할 수 있게 만들었다. Java의 @GetMapping과 같은 개념이다.

 
terminal
pip install fastapi uvicorn
 
main.py
from fastapi import FastAPI
from weather import get_weather, get_emoji

app = FastAPI()

@app.get("/weather/{city}")
def weather(city: str):
    w          = get_weather(city)
    w["emoji"] = get_emoji(w["desc"])
    return w
 
terminal
uvicorn main:app --reload
GEThttp://127.0.0.1:8000/weather/Seoul
{ "city" : "Seoul", "temp_c": 18, "desc" : "맑음", "emoji" : "☀️" }
FastAPI()
앱 인스턴스 생성. Java의 Spring 컨테이너와 비슷한 역할.
app = FastAPI()
@app.get()
GET 요청을 처리하는 엔드포인트 등록. Java의 @GetMapping과 같다.
@app.get("/weather/{city}")
{city} 경로 변수
URL의 일부를 변수로 받는다. Java의 @PathVariable과 같다.
/weather/Seoul → city = "Seoul"
uvicorn
FastAPI를 실행하는 서버. Java의 내장 Tomcat과 같은 역할.
uvicorn main:app --reload
--reload
코드 수정 시 서버 자동 재시작. Spring DevTools와 같다.
파일 저장만 해도 반영됨
/docs
FastAPI가 자동으로 만들어주는 Swagger 문서.
http://127.0.0.1:8000/docs
여러 도시 HTML 리포트 자동 생성

여러 도시 날씨를 한 번에 가져와서 HTML 파일로 자동 생성했다. Python이 HTML을 직접 문자열로 만들어서 파일로 저장하는 방식이다.

 
report.py
import json
from weather import get_weather, get_emoji

cities = ["Seoul", "Incheon", "Busan", "Jeju"]

results = []
for city in cities:
    w          = get_weather(city)
    w["emoji"] = get_emoji(w["desc"])
    results.append(w)
    print(f"{w['emoji']} {w['city']} {w['temp_c']}°C 완료")

# HTML 파일 자동 생성
html = """<!DOCTYPE html>..."""  # 카드 HTML 생성

with open("weather_report.html", "w", encoding="utf-8") as f:
    f.write(html)

print("weather_report.html 생성 완료!")

실행 결과로 생성된 HTML을 브라우저로 열면 이렇게 카드 형태로 볼 수 있다.

☀️ Seoul 18°C 완료 ☀️ Munhaktong 12°C 완료 🌧️ Yangchong 2Dong 14°C 완료 🌤️ Minjondong 12°C 완료 weather_report.html 생성 완료!
list.append()
리스트에 항목을 추가한다. Java의 list.add()와 같다.
results.append(w)
f.write()
파일에 문자열을 쓴다. HTML을 문자열로 만들어서 파일로 저장.
f.write(html)
f-string in loop
반복문 안에서 f-string으로 HTML 카드를 동적으로 생성한다.
html += f"<div>{w['city']}</div>"
MySQL에 날씨 기록 저장하기

날씨 데이터를 MySQL DB에 저장했다. Python에서 MySQL을 연결할 때는 pymysql 라이브러리를 사용한다. Java의 JDBC와 같은 역할이다.

 
terminal — 설치
pip install pymysql
 
MySQL — 테이블 생성
CREATE DATABASE weather_db;
USE weather_db;

CREATE TABLE weather_log (
    id         INT AUTO_INCREMENT PRIMARY KEY,
    city       VARCHAR(100),
    country    VARCHAR(100),
    temp_c     INT,
    feels_like INT,
    humidity   INT,
    desc_text  VARCHAR(100),
    wind_kmph  INT,
    updated_at VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
 
db.py
import pymysql
import json

conn = pymysql.connect(
    host="localhost",
    user="root",
    password="****",
    database="weather_db",
    charset="utf8mb4"
)

cursor = conn.cursor()

with open("cities.json", "r", encoding="utf-8") as f:
    cities = json.load(f)

for w in cities:
    sql = """
        INSERT INTO weather_log
        (city, country, temp_c, feels_like, humidity, desc_text, wind_kmph, updated_at)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
    """
    cursor.execute(sql, (
        w["city"], w["country"], w["temp_c"], w["feels_like"],
        w["humidity"], w["desc"], w["wind_kmph"], w["updated_at"]
    ))
    print(f"저장 완료: {w['city']}")

conn.commit()
cursor.close()
conn.close()

DB에 저장된 결과를 MySQL에서 조회하면 이렇게 나온다.

id city temp_c desc_text humidity created_at
1 Seoul 18 맑음 45 2026-03-25 13:37
2 Munhaktong 12 맑음 67 2026-03-25 13:37
3 Yangchong 2Dong 14 가벼운 비 51 2026-03-25 13:37
4 Minjondong 12 흐린 82 2026-03-25 13:37
pymysql.connect()
MySQL에 연결한다. Java의 DriverManager.getConnection()과 같다.
conn = pymysql.connect(host, user, password, database)
conn.cursor()
SQL을 실행할 커서를 가져온다. Java의 Statement와 같다.
cursor = conn.cursor()
cursor.execute()
SQL을 실행한다. %s로 파라미터를 바인딩해서 SQL Injection을 방지한다.
cursor.execute(sql, (값1, 값2, ...))
conn.commit()
변경사항을 DB에 반영한다. 이걸 빠뜨리면 저장이 안 된다.
conn.commit()
json.load()
JSON 파일을 읽어서 딕셔너리로 변환한다. dump()의 반대.
json.load(f) ↔ json.dump(w, f)
cursor.close() / conn.close()
커서와 연결을 닫는다. Java의 finally 블록에서 close() 하는 것과 같다.
cursor.close() → conn.close()
이번에 익힌 것들
  • with open() 으로 파일 열고 자동으로 닫는 패턴
  • json.dump() 저장, json.load() 읽기
  • FastAPI로 GET 엔드포인트 만드는 방법 — @app.get()
  • URL 경로 변수 {city} 받는 방법 — Java의 @PathVariable
  • Python이 HTML을 문자열로 만들어서 파일로 저장하는 패턴
  • pymysql.connect() 로 MySQL 연결
  • cursor.execute(sql, (값...)) 로 INSERT 실행
  • conn.commit() 빠뜨리면 저장 안 된다는 것
다음에 만들 것
03FastAPI에서 DB 조회해서 반환하는 엔드포인트 추가
04React로 날씨 대시보드 만들기
05스케줄러로 자동 수집 + 알림