- ๋ฒ ์ด์ค URL:
http://localhost:5000 - ํฌํธ: 5000
- ์๋ต ํ์: JSON
GET /health์ค๋ช
: ์๋ฒ ์ํ ํ์ธ
์๋ต:
{
"status": "healthy",
"timestamp": "2025-10-17T16:00:00.000000"
}POST /api/auth/register์์ฒญ:
{
"name": "๊น์ฒ ์",
"email": "kim@example.com",
"password": "SecurePass123!",
"phone": "010-1234-5678",
"location": "์์ธํน๋ณ์ ์ค๊ตฌ"
}์๋ต:
{
"message": "ํ์๊ฐ์
์ด ์๋ฃ๋์์ต๋๋ค.",
"user": {
"id": 1,
"name": "๊น์ฒ ์",
"email": "kim@example.com",
"phone": "010-1234-5678",
"location": "์์ธํน๋ณ์ ์ค๊ตฌ",
"is_active": true,
"role": "user",
"email_verified": false,
"created_at": "2025-10-19T13:00:00.000000"
},
"tokens": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 86400
}
}POST /api/auth/login์์ฒญ:
{
"email": "kim@example.com",
"password": "SecurePass123!"
}์๋ต:
{
"message": "๋ก๊ทธ์ธ์ ์ฑ๊ณตํ์ต๋๋ค.",
"user": {
"id": 1,
"name": "๊น์ฒ ์",
"email": "kim@example.com",
"last_login": "2025-10-19T13:30:00.000000"
},
"tokens": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 86400
}
}GET /api/auth/me
Authorization: Bearer {access_token}์๋ต:
{
"user": {
"id": 1,
"name": "๊น์ฒ ์",
"email": "kim@example.com",
"phone": "010-1234-5678",
"location": "์์ธํน๋ณ์ ์ค๊ตฌ",
"is_active": true,
"email_verified": false,
"last_login": "2025-10-19T13:30:00.000000"
}
}POST /api/auth/refresh์์ฒญ:
{
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}์๋ต:
{
"message": "ํ ํฐ์ด ๊ฐฑ์ ๋์์ต๋๋ค.",
"tokens": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 86400
}
}POST /api/auth/logout์๋ต:
{
"message": "๋ก๊ทธ์์๋์์ต๋๋ค."
}GET /api/users
Authorization: Bearer {admin_access_token}์๋ต:
[
{
"id": 1,
"name": "๊น์ฒ ์",
"email": "kim@example.com",
"phone": "010-1234-5678",
"location": "์์ธํน๋ณ์ ์ค๊ตฌ",
"is_active": true,
"email_verified": false,
"last_login": "2025-10-19T13:30:00.000000",
"created_at": "2025-10-19T13:00:00.000000"
}
]POST /api/admin/users
Authorization: Bearer {admin_access_token}์์ฒญ:
{
"name": "์ด์ํฌ",
"email": "lee@example.com",
"password": "TempPass123!",
"phone": "010-2345-6789",
"location": "๋ถ์ฐ๊ด์ญ์ ์ค๊ตฌ",
"role": "user"
}GET /api/markets
POST /api/marketsGET ์๋ต:
[
{
"id": 1,
"name": "๋๋๋ฌธ์์ฅ",
"location": "์์ธํน๋ณ์ ์ค๊ตฌ ์ฐฝ์ ๋",
"latitude": 37.5707,
"longitude": 127.0087,
"nx": 60,
"ny": 127,
"category": "์ ํต์์ฅ",
"is_active": true,
"created_at": "2025-10-17T07:00:00.000000"
}
]POST ์์ฒญ:
{
"name": "์๋ก์ด์์ฅ",
"location": "๋์ ๊ด์ญ์ ์ค๊ตฌ",
"latitude": 36.3214,
"longitude": 127.4214,
"category": "์ ํต์์ฅ"
}GET /api/markets/search?q={๊ฒ์์ด}&limit={๊ฐ์}์์ฒญ ํ๋ผ๋ฏธํฐ:
q: ๊ฒ์์ด (์ต์ 2๊ธ์)limit: ๊ฒฐ๊ณผ ๊ฐ์ (๊ธฐ๋ณธ๊ฐ: 20)
์๋ต:
{
"query": "๋๋๋ฌธ",
"count": 2,
"markets": [
{
"id": 1,
"name": "๋๋๋ฌธ์์ฅ",
"location": "์์ธํน๋ณ์ ์ค๊ตฌ ์ฐฝ์ ๋",
"latitude": 37.5707,
"longitude": 127.0087,
"nx": 60,
"ny": 127,
"category": "์ ํต์์ฅ",
"is_active": true
}
]
}GET /api/watchlist
Authorization: Bearer {access_token}์๋ต:
{
"count": 2,
"watchlist": [
{
"id": 1,
"user_id": 1,
"market_id": 1,
"market_name": "๋๋๋ฌธ์์ฅ",
"market_location": "์์ธํน๋ณ์ ์ค๊ตฌ ์ฐฝ์ ๋",
"market_coordinates": {
"latitude": 37.5707,
"longitude": 127.0087,
"nx": 60,
"ny": 127
},
"created_at": "2025-10-22T08:00:00.000000",
"is_active": true,
"notification_enabled": true
}
]
}POST /api/watchlist
Authorization: Bearer {access_token}์์ฒญ:
{
"market_id": 1
}์๋ต:
{
"message": "๋๋๋ฌธ์์ฅ์ด(๊ฐ) ๊ด์ฌ ๋ชฉ๋ก์ ์ถ๊ฐ๋์์ต๋๋ค.",
"interest": {
"id": 1,
"user_id": 1,
"market_id": 1,
"market_name": "๋๋๋ฌธ์์ฅ",
"is_active": true,
"notification_enabled": true,
"created_at": "2025-10-22T08:00:00.000000"
}
}DELETE /api/watchlist/{market_id}
Authorization: Bearer {access_token}์๋ต:
{
"message": "๊ด์ฌ ๋ชฉ๋ก์์ ์ ๊ฑฐ๋์์ต๋๋ค.",
"market_id": 1
}PUT /api/watchlist/{interest_id}/notification
Authorization: Bearer {access_token}์๋ต:
{
"message": "์๋ฆผ์ด ํ์ฑํ๋์์ต๋๋ค.",
"interest": {
"id": 1,
"notification_enabled": true,
"market_name": "๋๋๋ฌธ์์ฅ"
}
}GET /api/damage-status
POST /api/damage-statusPOST ์์ฒญ:
{
"market_id": 1,
"weather_event": "ํํ",
"damage_level": "์ฌ๊ฐ",
"description": "์ง๋ถ ์ผ๋ถ ์์",
"estimated_recovery_time": "2025-10-20T10:00:00"
}GET /api/markets/{market_id}/rain-forecast?hours={์๊ฐ}์์ฒญ ํ๋ผ๋ฏธํฐ:
market_id: ์์ฅ IDhours: ์๋ณด ํ์ธ ์๊ฐ (๊ธฐ๋ณธ๊ฐ: 24์๊ฐ)
์๋ต:
{
"status": "success",
"market_id": 1,
"forecast": {
"has_rain": true,
"market_name": "๋๋๋ฌธ์์ฅ",
"alerts": [
{
"datetime": "2025-10-22T14:00:00",
"pop": 60,
"pty": "1",
"description": "๋น"
}
],
"checked_hours": 24
}
}POST /api/admin/rain-alerts/check
Authorization: Bearer {admin_access_token}์์ฒญ:
{
"hours": 24
}์๋ต:
{
"status": "success",
"message": "๋น ์๋ณด ์๋ฆผ ํ์ธ ์๋ฃ",
"result": {
"success": true,
"checked_markets": 15,
"alerts_sent": 3,
"results": [
{
"market": "๋๋๋ฌธ์์ฅ",
"rain_forecast": true,
"alert_result": {
"success": true,
"sent_count": 5
}
}
]
}
}POST /api/weather/current์์ฒญ:
{
"latitude": 37.5665,
"longitude": 126.9780,
"location_name": "์์ธ์์ฒญ"
}์๋ต:
{
"status": "success",
"data": {
"base_date": "20251017",
"base_time": "1600",
"nx": 60,
"ny": 127,
"temp": 23.1,
"humidity": 65.0,
"rain_1h": 0.0,
"wind_speed": 2.1,
"wind_direction": 120.0,
"api_type": "current",
"location_name": "์์ธ์์ฒญ"
}
}POST /api/weather/forecast์์ฒญ:
{
"latitude": 37.5665,
"longitude": 126.9780,
"location_name": "์์ธ์์ฒญ"
}์๋ต:
{
"status": "success",
"data": [
{
"base_date": "20251017",
"base_time": "1630",
"fcst_date": "20251017",
"fcst_time": "1700",
"temp": 22.0,
"humidity": 70.0,
"rain_1h": 0.0,
"pty": "0",
"sky": "3",
"api_type": "forecast"
}
]
}GET /api/weather
GET /api/weather?location_name=๋๋๋ฌธ
GET /api/weather?api_type=current
GET /api/weather?limit=50์๋ต:
{
"status": "success",
"count": 21,
"data": [
{
"id": 1,
"location_name": "๋๋๋ฌธ์์ฅ (์์ธํน๋ณ์ ์ค๊ตฌ ์ฐฝ์ ๋)",
"api_type": "current",
"temp": 23.1,
"humidity": 65.0,
"created_at": "2025-10-17T07:55:12.000000"
}
]
}POST /api/scheduler/start์๋ต:
{
"status": "success",
"message": "๋ ์จ ์ค์ผ์ค๋ฌ๊ฐ ์์๋์์ต๋๋ค."
}POST /api/scheduler/stopGET /api/scheduler/status์๋ต:
{
"scheduler_running": true,
"job_count": 1,
"jobs": [
{
"id": "weather_collection_job",
"name": "์์ฅ๋ณ ๋ ์จ ๋ฐ์ดํฐ ์์ง",
"next_run": "2025-10-17T17:25:00.000000"
}
]
}GET /api/scheduler/stats์๋ต:
{
"total_weather_records": 21,
"current_weather_records": 3,
"forecast_weather_records": 18,
"active_markets": 3,
"markets_with_coordinates": 3,
"latest_weather_update": "2025-10-17T07:55:12.000000"
}POST /api/scheduler/collect์๋ต:
{
"status": "success",
"message": "๋ ์จ ๋ฐ์ดํฐ ์์ง์ด ์๋ฃ๋์์ต๋๋ค."
}POST /api/fcm/register
Authorization: Bearer {access_token}์์ฒญ:
{
"token": "FCM_REGISTRATION_TOKEN",
"device_info": {
"platform": "web",
"browser": "Chrome 119.0.0.0",
"timestamp": "2025-10-19T13:00:00Z"
},
"subscribe_topics": ["weather_alerts", "severe_weather"]
}์๋ต:
{
"message": "FCM ํ ํฐ์ด ๋ฑ๋ก๋์์ต๋๋ค.",
"fcm_enabled": true,
"subscribed_topics": ["weather_alerts", "severe_weather"]
}GET /api/fcm/settings
POST /api/fcm/settings
Authorization: Bearer {access_token}GET ์๋ต:
{
"fcm_enabled": true,
"fcm_topics": ["weather_alerts", "severe_weather"],
"device_info": {
"platform": "web",
"browser": "Chrome 119.0.0.0"
},
"has_token": true
}POST ์์ฒญ (์ค์ ์ ๋ฐ์ดํธ):
{
"enabled": true,
"subscribe_topics": ["weather_alerts"],
"unsubscribe_topics": ["severe_weather"]
}POST /api/fcm/test
Authorization: Bearer {access_token}์๋ต:
{
"message": "ํ
์คํธ ์๋ฆผ์ด ์ ์ก๋์์ต๋๋ค."
}POST /api/admin/fcm/send
Authorization: Bearer {admin_access_token}์์ฒญ (์ฃผ์ ๋ก ์ ์ก):
{
"title": "๊ธฐ์ ํน๋ณด",
"body": "์์ธ ์ง์ญ์ ํธ์ฐ ๊ฒฝ๋ณด๊ฐ ๋ฐ๋ น๋์์ต๋๋ค.",
"topic": "severe_weather",
"data": {
"type": "severe_weather",
"location": "์์ธ"
}
}์์ฒญ (ํน์ ์ฌ์ฉ์๋ค์๊ฒ ์ ์ก):
{
"title": "๋ ์จ ์๋ฆผ",
"body": "๋ด์ผ ๋น๊ฐ ์์๋ฉ๋๋ค.",
"user_ids": [1, 2, 3],
"data": {
"type": "weather_forecast"
}
}์์ฒญ (์ ์ฒด ์ฌ์ฉ์์๊ฒ ์ ์ก):
{
"title": "์ ์ฒด ๊ณต์ง",
"body": "๋ ์จ ์๋น์ค๊ฐ ์
๋ฐ์ดํธ๋์์ต๋๋ค.",
"data": {
"type": "announcement"
}
}์๋ต:
{
"message": "์ ์ฒด 15๋ช
์ ์ฌ์ฉ์์๊ฒ ์๋ฆผ์ด ์ ์ก๋์์ต๋๋ค.",
"result": {
"success_count": 14,
"failure_count": 1,
"failed_tokens": ["invalid_token_example"]
}
}GET /db-viewer์ค๋ช : ๋ธ๋ผ์ฐ์ ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด์ฉ์ ํ์ธํ ์ ์๋ ์น ์ธํฐํ์ด์ค
GET /db-viewer/api/stats # ํต๊ณ
GET /db-viewer/api/users # ์ฌ์ฉ์ ๋ฐ์ดํฐ
GET /db-viewer/api/markets # ์์ฅ ๋ฐ์ดํฐ
GET /db-viewer/api/weather # ๋ ์จ ๋ฐ์ดํฐ
GET /db-viewer/api/damage # ํผํด์ํ ๋ฐ์ดํฐ# ํฌ์ค ์ฒดํฌ
curl http://localhost:5000/health
# ํ์๊ฐ์
curl -X POST http://localhost:5000/api/auth/register \
-H "Content-Type: application/json" \
-d '{"name": "๊น์ฒ ์", "email": "kim@example.com", "password": "SecurePass123!"}'
# ๋ก๊ทธ์ธ
curl -X POST http://localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "kim@example.com", "password": "SecurePass123!"}'
# ์ธ์ฆ๋ ํ๋กํ ์กฐํ (ํ ํฐ ํ์)
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
http://localhost:5000/api/auth/me
# ๋ชจ๋ ์์ฅ ์กฐํ
curl http://localhost:5000/api/markets
# ํ์ฌ ๋ ์จ ์กฐํ
curl -X POST http://localhost:5000/api/weather/current \
-H "Content-Type: application/json" \
-d '{"latitude": 37.5665, "longitude": 126.9780, "location_name": "์์ธ"}'
# ์ค์ผ์ค๋ฌ ์์
curl -X POST http://localhost:5000/api/scheduler/start
# ๋ ์จ ํต๊ณ ์กฐํ
curl http://localhost:5000/api/scheduler/stats
# FCM ํ ํฐ ๋ฑ๋ก (์ธ์ฆ ํ์)
curl -X POST http://localhost:5000/api/fcm/register \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{"token": "FCM_TOKEN", "device_info": {"platform": "web"}}'
# FCM ํ
์คํธ ์๋ฆผ ์ ์ก (์ธ์ฆ ํ์)
curl -X POST http://localhost:5000/api/fcm/test \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# ๊ด๋ฆฌ์์ฉ ์ ์ฒด FCM ์๋ฆผ ์ ์ก
curl -X POST http://localhost:5000/api/admin/fcm/send \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-d '{"title": "๊ธฐ์ ํน๋ณด", "body": "ํธ์ฐ ๊ฒฝ๋ณด ๋ฐ๋ น"}'
# ์์ฅ ๊ฒ์
curl "http://localhost:5000/api/markets/search?q=๋๋๋ฌธ&limit=10"
# ๊ด์ฌ๋ชฉ๋ก ์กฐํ (์ธ์ฆ ํ์)
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
http://localhost:5000/api/watchlist
# ๊ด์ฌ๋ชฉ๋ก์ ์์ฅ ์ถ๊ฐ (์ธ์ฆ ํ์)
curl -X POST http://localhost:5000/api/watchlist \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{"market_id": 1}'
# ๊ด์ฌ๋ชฉ๋ก์์ ์์ฅ ์ ๊ฑฐ (์ธ์ฆ ํ์)
curl -X DELETE http://localhost:5000/api/watchlist/1 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# ์์ฅ์ ๋น ์๋ณด ํ์ธ
curl "http://localhost:5000/api/markets/1/rain-forecast?hours=24"
# ๊ด๋ฆฌ์์ฉ ์๋ ๋น ์๋ณด ์๋ฆผ ํ์ธ (๊ด๋ฆฌ์ ๊ถํ ํ์)
curl -X POST http://localhost:5000/api/admin/rain-alerts/check \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-d '{"hours": 24}'// ํ์๊ฐ์
const registerResponse = await fetch('http://localhost:5000/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: '๊น์ฒ ์',
email: 'kim@example.com',
password: 'SecurePass123!'
})
});
const registerData = await registerResponse.json();
const accessToken = registerData.tokens.access_token;
// ๋ก๊ทธ์ธ
const loginResponse = await fetch('http://localhost:5000/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'kim@example.com',
password: 'SecurePass123!'
})
});
const loginData = await loginResponse.json();
// ์ธ์ฆ๋ ํ๋กํ ์กฐํ
const profileResponse = await fetch('http://localhost:5000/api/auth/me', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
const profile = await profileResponse.json();
// ์์ฅ ๋ชฉ๋ก ์กฐํ
fetch('http://localhost:5000/api/markets')
.then(response => response.json())
.then(data => console.log(data));
// ํ์ฌ ๋ ์จ ์กฐํ
fetch('http://localhost:5000/api/weather/current', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
latitude: 37.5665,
longitude: 126.9780,
location_name: '์์ธ์์ฒญ'
})
})
.then(response => response.json())
.then(data => console.log(data));
// FCM ํ ํฐ ๋ฑ๋ก
fetch('http://localhost:5000/api/fcm/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
token: 'FCM_REGISTRATION_TOKEN',
device_info: { platform: 'web', browser: 'Chrome' },
subscribe_topics: ['weather_alerts']
})
})
.then(response => response.json())
.then(data => console.log(data));
// FCM ํ
์คํธ ์๋ฆผ
fetch('http://localhost:5000/api/fcm/test', {
method: 'POST',
headers: { 'Authorization': `Bearer ${accessToken}` }
})
.then(response => response.json())
.then(data => console.log(data));
// ์์ฅ ๊ฒ์
fetch('http://localhost:5000/api/markets/search?q=๋๋๋ฌธ&limit=10')
.then(response => response.json())
.then(data => console.log(data));
// ๊ด์ฌ๋ชฉ๋ก ์กฐํ
fetch('http://localhost:5000/api/watchlist', {
headers: { 'Authorization': `Bearer ${accessToken}` }
})
.then(response => response.json())
.then(data => console.log(data));
// ๊ด์ฌ๋ชฉ๋ก์ ์์ฅ ์ถ๊ฐ
fetch('http://localhost:5000/api/watchlist', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({ market_id: 1 })
})
.then(response => response.json())
.then(data => console.log(data));
// ์์ฅ์ ๋น ์๋ณด ํ์ธ
fetch('http://localhost:5000/api/markets/1/rain-forecast?hours=24')
.then(response => response.json())
.then(data => console.log(data));import requests
# ํ์๊ฐ์
register_data = {
"name": "๊น์ฒ ์",
"email": "kim@example.com",
"password": "SecurePass123!"
}
register_response = requests.post('http://localhost:5000/api/auth/register',
json=register_data)
register_result = register_response.json()
access_token = register_result['tokens']['access_token']
# ๋ก๊ทธ์ธ
login_data = {
"email": "kim@example.com",
"password": "SecurePass123!"
}
login_response = requests.post('http://localhost:5000/api/auth/login',
json=login_data)
login_result = login_response.json()
# ์ธ์ฆ๋ ํ๋กํ ์กฐํ
headers = {"Authorization": f"Bearer {access_token}"}
profile_response = requests.get('http://localhost:5000/api/auth/me',
headers=headers)
profile = profile_response.json()
# ์์ฅ ๋ชฉ๋ก ์กฐํ
response = requests.get('http://localhost:5000/api/markets')
markets = response.json()
# ํ์ฌ ๋ ์จ ์กฐํ
weather_data = {
"latitude": 37.5665,
"longitude": 126.9780,
"location_name": "์์ธ์์ฒญ"
}
response = requests.post('http://localhost:5000/api/weather/current',
json=weather_data)
weather = response.json()
# FCM ํ ํฐ ๋ฑ๋ก
fcm_data = {
"token": "FCM_REGISTRATION_TOKEN",
"device_info": {"platform": "python", "version": "3.9"},
"subscribe_topics": ["weather_alerts"]
}
fcm_response = requests.post('http://localhost:5000/api/fcm/register',
json=fcm_data, headers=headers)
fcm_result = fcm_response.json()
# FCM ํ
์คํธ ์๋ฆผ
test_response = requests.post('http://localhost:5000/api/fcm/test',
headers=headers)
test_result = test_response.json()-
์ธ์ฆ ์์คํ : JWT ํ ํฐ ๊ธฐ๋ฐ ์ธ์ฆ์ ์ฌ์ฉํฉ๋๋ค
- ํ์๊ฐ์ /๋ก๊ทธ์ธ ์ access_token๊ณผ refresh_token์ ๋ฐ๊ธ๋ฐ์ต๋๋ค
- access_token์ 24์๊ฐ, refresh_token์ 30์ผ๊ฐ ์ ํจํฉ๋๋ค
- ์ธ์ฆ์ด ํ์ํ API๋
Authorization: Bearer {access_token}ํค๋๋ฅผ ์ฌ์ฉํฉ๋๋ค
-
์ฌ์ฉ์ ๊ถํ ์์คํ :
user: ์ผ๋ฐ ์ฌ์ฉ์ (ํ์๊ฐ์ ์ผ๋ก ์์ฑ)admin: ๊ด๋ฆฌ์ (์๋ฒ ์คํฌ๋ฆฝํธ๋ก๋ง ์์ฑ, ID: snslab / PW: snslab@cu)- ๊ด๋ฆฌ์ ๊ถํ์ด ํ์ํ API๋ ๋ณ๋ ํ์
-
๊ด์ฌ๋ชฉ๋ก ์์คํ :
- ์ฌ์ฉ์๋ ์ฌ๋ฌ ์์ฅ์ ๊ด์ฌ๋ชฉ๋ก์ ์ถ๊ฐ ๊ฐ๋ฅ
- ๊ฐ ์์ฅ๋ณ๋ก ๊ฐ๋ณ ์๋ฆผ ์ค์ ๊ฐ๋ฅ
- ๊ด์ฌ ์์ฅ์ ๋น ์๋ณด ์ ์๋ FCM ์๋ฆผ ์ ์ก
-
๋น ์๋ณด ์๋ฆผ:
- ๋งค ์๊ฐ๋ง๋ค ์๋์ผ๋ก ํฅํ 24์๊ฐ ๋น ์๋ณด ํ์ธ
- ๊ฐ์ํ๋ฅ 30% ์ด์ ๋๋ ๊ฐ์ํํ ์กด์ฌ ์ ์๋ฆผ
- ๊ด์ฌ ์์ฅ ๋ฑ๋ก ์ฌ์ฉ์์๊ฒ๋ง ๊ฐ๋ณ ์๋ฆผ ์ ์ก
-
ํจ์ค์๋ ์๊ตฌ์ฌํญ:
- ์ต์ 8์ ์ด์
- ๋๋ฌธ์, ์๋ฌธ์, ์ซ์ ํฌํจ ํ์
-
CORS: ๋ค๋ฅธ ๋๋ฉ์ธ์์ ํธ์ถ ์ CORS ์ค์ ์ด ํ์ํ ์ ์์ต๋๋ค
-
Rate Limit: ๊ธฐ์์ฒญ API ํธ์ถ ์ ํ์ด ์์ผ๋ฏ๋ก ๋๋ฌด ์์ฃผ ํธ์ถํ์ง ๋ง์ธ์
-
์๋ฌ ์ฒ๋ฆฌ: ๋ชจ๋ API๋ ์คํจ ์
{"error": "๋ฉ์์ง"}ํํ๋ก ์๋ตํฉ๋๋ค -
FCM ์ค์ :
- Firebase ํ๋ก์ ํธ ์ค์ ๋ฐ ์๋น์ค ๊ณ์ ํค๊ฐ ํ์ํฉ๋๋ค
- ํ๊ฒฝ๋ณ์
FIREBASE_SERVICE_ACCOUNT_KEY๋๋FIREBASE_SERVICE_ACCOUNT_JSON์ค์ ํ์ - ํด๋ผ์ด์ธํธ๋ณ FCM SDK ์ค์ ์
client_fcm_config/๋๋ ํ ๋ฆฌ ์ฐธ์กฐ
-
๋ ์จ ์๋ฆผ:
- ์๋ ์ค์ผ์ค๋ฌ๊ฐ ์ฌ๊ฐํ ๋ ์จ ์กฐ๊ฑด ๊ฐ์ง ์ FCM ์๋ฆผ ์๋ ์ ์ก
- ํญ์ผ(35ยฐC ์ด์), ํํ(-10ยฐC ์ดํ), ํธ์ฐ(10mm/h ์ด์), ๊ฐํ(14m/s ์ด์) ์กฐ๊ฑด
- ๊ด์ฌ ์์ฅ ๊ธฐ๋ฐ ๋น ์๋ณด ์๋ฆผ (๊ฐ์ํ๋ฅ 30% ์ด์)
python app.py
# ์๋ฒ ์ฃผ์: http://localhost:5000