🌦 WeatherKit 완전정복
Apple 공식 날씨 API로 정확한 날씨 정보를 제공하세요. 현재 날씨부터 10일 예보까지!
⭐ 난이도: ⭐⭐
⏱️ 예상 시간: 1h
📂 App Services
✨ WeatherKit이란?
WeatherKit은 Apple이 제공하는 공식 날씨 API입니다. iOS 16+에서 사용 가능하며, 월 50만 건까지 무료입니다.
🔑 설정 (Apple Developer)
Setup Steps
1. Apple Developer 콘솔 접속
2. Certificates, Identifiers & Profiles
3. Keys 탭에서 새 키 생성
4. "WeatherKit" 체크박스 활성화
5. 다운로드한 .p8 파일 안전하게 보관
⚠️ 키는 한 번만 다운로드 가능하므로 백업 필수!🌡️ 현재 날씨 가져오기
CurrentWeather.swift
import WeatherKit import CoreLocation let weatherService = WeatherService() func fetchCurrentWeather(for location: CLLocation) async throws -> CurrentWeather { let weather = try await weatherService.weather(for: location) return weather.currentWeather } // 사용 예시 Task { let location = CLLocation(latitude: 37.5665, longitude: 126.9780) // 서울 let current = try await fetchCurrentWeather(for: location) print("온도: \(current.temperature.value)°C") print("체감 온도: \(current.apparentTemperature.value)°C") print("습도: \(current.humidity * 100)%") print("날씨: \(current.condition.description)") print("바람: \(current.wind.speed.value) m/s") }
📅 시간별 예보 (24시간)
HourlyForecast.swift
func fetchHourlyForecast(for location: CLLocation) async throws -> Forecast<HourWeather> { let weather = try await weatherService.weather(for: location) return weather.hourlyForecast } // 사용 예시 Task { let location = CLLocation(latitude: 37.5665, longitude: 126.9780) let hourly = try await fetchHourlyForecast(for: location) // 다음 12시간 예보 for hour in hourly.prefix(12) { print("\(hour.date) - \(hour.temperature.value)°C - \(hour.condition)") } }
📆 일별 예보 (10일)
DailyForecast.swift
func fetchDailyForecast(for location: CLLocation) async throws -> Forecast<DayWeather> { let weather = try await weatherService.weather(for: location) return weather.dailyForecast } // 사용 예시 Task { let location = CLLocation(latitude: 37.5665, longitude: 126.9780) let daily = try await fetchDailyForecast(for: location) for day in daily { print("\(day.date)") print("최고: \(day.highTemperature.value)°C") print("최저: \(day.lowTemperature.value)°C") print("강수 확률: \(day.precipitationChance * 100)%") print("일출: \(day.sun.sunrise!)") print("일몰: \(day.sun.sunset!)") } }
☔ 분별 강수 예보 (다음 1시간)
MinuteForecast.swift
func fetchMinuteForecast(for location: CLLocation) async throws -> Forecast<MinuteWeather>? { let weather = try await weatherService.weather(for: location) return weather.minuteForecast } // 사용 예시 (미국만 지원) Task { let location = CLLocation(latitude: 37.7749, longitude: -122.4194) // 샌프란시스코 if let minute = try await fetchMinuteForecast(for: location) { for m in minute { print("\(m.date) - 강수 강도: \(m.precipitation.value)") } } else { print("분별 예보 지원 안 함 (미국만 가능)") } }
🚨 기상 경보
WeatherAlerts.swift
func fetchWeatherAlerts(for location: CLLocation) async throws -> [WeatherAlert]? { let weather = try await weatherService.weather(for: location) return weather.weatherAlerts } // 사용 예시 Task { let location = CLLocation(latitude: 37.5665, longitude: 126.9780) if let alerts = try await fetchWeatherAlerts(for: location) { for alert in alerts { print("⚠️ \(alert.summary)") print("경보 수준: \(alert.severity)") print("발효: \(alert.effectiveTime)") print("만료: \(alert.expireTime)") } } }
🌤 날씨 상태 (WeatherCondition)
WeatherCondition.swift
import WeatherKit func getWeatherIcon(_ condition: WeatherCondition) -> String { switch condition { case .clear: return "sun.max.fill" case .cloudy: return "cloud.fill" case .rain: return "cloud.rain.fill" case .snow: return "snow" case .sleet: return "cloud.sleet.fill" case .hail: return "cloud.hail.fill" case .thunderstorms: return "cloud.bolt.fill" case .heavyRain: return "cloud.heavyrain.fill" case .strongStorms: return "cloud.bolt.rain.fill" case .foggy: return "cloud.fog.fill" case .windy: return "wind" @unknown default: return "questionmark" } } // 한글 설명 func getWeatherDescription(_ condition: WeatherCondition) -> String { switch condition { case .clear: return "맑음" case .cloudy: return "흐림" case .rain: return "비" case .snow: return "눈" case .thunderstorms: return "뇌우" @unknown default: return "알 수 없음" } }
📱 SwiftUI 통합
WeatherView.swift
import SwiftUI import WeatherKit import CoreLocation @Observable class WeatherViewModel { var currentWeather: CurrentWeather? var hourlyForecast: Forecast<HourWeather>? var isLoading = false let weatherService = WeatherService() func loadWeather(for location: CLLocation) async { isLoading = true defer { isLoading = false } do { let weather = try await weatherService.weather(for: location) currentWeather = weather.currentWeather hourlyForecast = weather.hourlyForecast } catch { print("날씨 로드 실패: \(error)") } } } struct WeatherView: View { var viewModel = WeatherViewModel() let location = CLLocation(latitude: 37.5665, longitude: 126.9780) var body: some View { ScrollView { VStack(spacing: 20) { if viewModel.isLoading { ProgressView() } else if let current = viewModel.currentWeather { // 현재 날씨 VStack { Image(systemName: getWeatherIcon(current.condition)) .font(.system(size: 80)) .symbolRenderingMode(.multicolor) Text("\(Int(current.temperature.value))°") .font(.system(size: 72, weight: .thin)) Text(getWeatherDescription(current.condition)) .font(.title2) } // 시간별 예보 if let hourly = viewModel.hourlyForecast { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 16) { ForEach(hourly.prefix(24), id: \.date) { hour in VStack { Text(hour.date, format: .dateTime.hour()) .font(.caption) Image(systemName: getWeatherIcon(hour.condition)) .font(.title2) Text("\(Int(hour.temperature.value))°") .font(.headline) } .frame(width: 60) } } .padding() } } } } } .task { await viewModel.loadWeather(for: location) } } }
⚡ 에러 처리
ErrorHandling.swift
do { let weather = try await weatherService.weather(for: location) } catch { if let weatherError = error as? WeatherError { switch weatherError { case .locationUnavailable: print("위치 정보 없음") case .locationOutOfRange: print("지원하지 않는 지역") case .networkUnavailable: print("네트워크 연결 없음") @unknown default: print("알 수 없는 에러") } } }
💰 사용량 제한
Rate Limits
WeatherKit API 제한:
- 월 50만 건 무료
- 초과 시: $0.0001/call (100만 건당 $100)
- Rate limit: 분당 500 requests
⚠️ 캐싱을 적극 활용하세요!
- 현재 날씨: 10분 캐시
- 시간별 예보: 30분 캐시
- 일별 예보: 1시간 캐시💡 HIG 체크리스트
✅ Apple 제공 날씨 아이콘 사용 (SF Symbols)
✅ 데이터 출처 표시 ("Weather data provided by Apple")
✅ 적절한 캐싱으로 API 호출 최소화
✅ 로딩 상태 표시
✅ 네트워크 에러 처리