κ΄μ°°μκ° μΌμλλ¬Ό(μ¬μ§Β·μμΒ·μ리)μ κΈ°λ‘νλ©΄
λ°±μλκ° μ’νΒ·μ£Όμ(μΉ΄μΉ΄μ€ μ§λ)μ κ΄μ°° μκ°μ μ΄μ©ν΄ λ μ¨(OpenWeatherMap)Β·κ³ λ μ 보λ₯Ό κ²°ν©νμ¬ μ μ₯/μ‘°ννλ μλΉμ€μ
λλ€.
λ΄λΉ μν (λ³ΈμΈ): λ°±μλ κ°λ° Β· DB μ€κ³/κ΅¬μΆ Β· νλ‘ νΈμ μ°λ(React λ°μ΄ν° 곡κΈ) Β· μΈλΆ API ν΅ν©
- κΈ°μ μ€ν
- νλ©΄
- ν΅μ¬ κΈ°λ₯
- μν€ν μ²
- λ°μ΄ν° λͺ¨λΈ(ERD)
- λ‘컬 μ€ν
- ν΄λ ꡬ쑰
- λ΄ μν & μ±κ³Ό
- κ΄μ°° μΉ΄λ 리μ€νΈ: μ’ μ΄λ¦, κ΄μ°° μμΉ(μ£Όμ), κ΄μ°° μΌμλ₯Ό μΉ΄λλ‘ νμ. μΉ΄ν κ³ λ¦¬ ν/κ²μ μ 곡.
- μ λ‘λ νΌ: μλ¬Όμ’ λΆλ₯Β·μ’ μ΄λ¦Β·μμμ§ μ ν μ ν, μ¬μ§/λμμ/μ리 μ λ‘λ(λλκ·Έμ€λλ‘/νμΌ μ ν).
- μ§λ μ°λ: μΉ΄μΉ΄μ€ μ§λμμ λ§μ»€λ₯Ό ν΄λ¦νλ©΄ μ£Όμκ° μλ κ³μ° λμ΄ μ λ ₯λμ λ°μ.
- μμΈ νμ΄μ§: λ―Έλμ΄, μ£Όμ, κ³ λ/λ μ¨(κΈ°μ¨Β·κ°μΒ·νμ), κ΄μ°° μκ°, μ§λ, λκΈ(μ΄λ¦+λ΄μ©) νμ.
- λ€κ΅μ΄ ν κΈ: νκ΅μ΄/μμ΄ UI(νλ‘ νΈ μ 곡) Β· CORS/보μ μ€μ μΌλ‘ μλΉμ€ν μ€λΉ.
flowchart LR
A[React Client] -->|REST/JSON Β· multipart| B[FastAPI]
B -->|SQLAlchemy| C[(MySQL)]
B -->|OpenWeatherMap API| D[Weather Service]
B -->|Kakao Map JS SDK| E[Reverse Geocoding]
subgraph Storage
C
end
erDiagram
observations ||--o{ comments : has
observations {
int id
string species_category
string species_name
string habitat_type
datetime observation_date
string location
string memo
string image_url
string video_url
string audio_url
datetime created_at
float latitude
float longitude
}
comments {
int id
int observation_id
string username
string content
datetime created_at
}
- Python 3.10+
- MySQL 8.x
- Node.js 18+ (νλ‘ νΈ νμΈ μ)
루νΈμ .env μμ±:
DB_URL=mysql+pymysql://USER:PASSWORD@localhost:3306/wildlife?charset=utf8mb4
OPENWEATHER_API_KEY=YOUR_OPENWEATHERMAP_KEY
KAKAO_JS_KEY=YOUR_KAKAO_MAP_JS_KEY
CORS_ORIGINS=http://localhost:5173,http://localhost:3000python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt # λλ: pip install fastapi uvicorn sqlalchemy PyMySQL python-dotenv
uvicorn main:app --reload # μ€μ μνΈλ¦¬ν¬μΈνΈ λͺ¨λλͺ
:appnpm i
npm run dev.
βββ client/ # νλ‘ νΈμλ(React/Vite λ±)
β
βββ server/ # λ°±μλ(FastAPI) 루νΈ
β βββ main.py # FastAPI μνΈλ¦¬ν¬μΈνΈ (app, λΌμ°ν° include, CORS λ±)
β βββ db.py # DB μ°κ²°/μΈμ
, Base μ μΈ, νκ²½λ³μ λ‘λ©
β β
β βββ models/ # SQLAlchemy λͺ¨λΈ λ μ΄μ΄(ν
μ΄λΈ)
β β βββ comment.py # λκΈ(Comment) λͺ¨λΈ
β β βββ observation.py # κ΄μ°°(Observation) λͺ¨λΈ
β β
β βββ routers/ # λΌμ°ν
(μλν¬μΈνΈ) λͺ¨λ
β β βββ comment_router.py # /api/observations/{id}/comments λ± λκΈ API
β β βββ observation_router.py # /api/observations CRUD, νμΌ μ
λ‘λ, μ‘°ν λ±
β β
β βββ schemas/ # Pydantic μ€ν€λ§(μ
μΆλ ₯ κ²μ¦)
β β βββ comment_schema.py # λκΈ μμ²/μλ΅ μ€ν€λ§
β β βββ observation_schema.py # κ΄μ°°/λ―Έλμ΄/λ μ¨ μλ΅ μ€ν€λ§
β β
β βββ uploads/ # λ°νμ μ
λ‘λ μ μ₯ κ²½λ‘(μλΉμ€μ©)
β βββ audio/ # μ
λ‘λ μ€λμ€
β βββ img/ # μ
λ‘λ μ΄λ―Έμ§
β βββ mp4/ # μ
λ‘λ λΉλμ€
β
βββ Makefile # μμ£Ό μ°λ λͺ
λ Ή(μλ² μ€ν, ν¬λ§·ν
λ±) μ€ν¬λ¦½νΈν(μ ν)
βββ requirements.txt # λ°±μλ μμ‘΄μ±(ν¨ν€μ§) λͺ©λ‘
βββ readme.md # (μ μ₯μ μ€λͺ
) README
βββ .env # νκ²½λ³μ(λ‘컬) β μ»€λ° κΈμ§
βββ .gitignore # venv, __pycache__, .env, uploads λ± μ μΈ
βββ venv/ # κ°μνκ²½ β μ»€λ° κΈμ§
βββ assets/ # (README μ μ©) μ€ν¬λ¦°μ· ν΄λ
βββ main.png
βββ upload.png
βββ location.png
βββ detail.png
- DB μ€κ³Β·κ΅¬μΆ: κ΄μ°°(Observation)βλ―Έλμ΄(Media)βλκΈ(Comment) μ€ν€λ§/κ΄κ³ μ μ, μΈλ±μ€ μ΅μ ν.
- νμΌ μ λ‘λ νμ΄νλΌμΈ: μ¬μ§/μμ/μ€λμ€ λ©ν°ννΈ μ λ‘λ β μ μ₯ κ²½λ‘/λ©ν μ μ₯ β μλ΅ μ€ν€λ§ μ€κ³.
- μ€ν API ν΅ν©: κ΄μ°° μ’νΒ·μκ° κΈ°λ° OpenWeatherMap λ μ¨/κ³ λ κ³μ°, μΉ΄μΉ΄μ€ μ§λ μμ§μ€μ½λ©.
- νλ‘ νΈ μ°λ: React UIμ λ°μ΄ν° λ°μΈλ©, 리μ€νΈ/μμΈ/μ λ‘λ E2E νλ‘μ° κ²μ¦.
- μ½λ λͺ¨λν:
observation.py,observation_router.py,observation_schema.py,comment.py,comment_schema.py,db.pyλ±μΌλ‘ λλ©μΈΒ·λΌμ°ν Β·μ€ν€λ§ λΆλ¦¬. - μ΄μ κ³ λ €: CORS νμ΄νΈλ¦¬μ€νΈ,
.envλΆλ¦¬, λ‘κ·Έ κ°μΈμ 보 μ΅μν, μ λ‘λ νμΌ κ²μ¦.
λ΄λΆ ꡬνμ μ°κ΅¬μ€ Gitty(Private)μμ μ μ§λλ©°, μ΄ μ μ₯μλ λ¬Έμ·ꡬ쑰·μ±κ³Ό μ€μ¬μΌλ‘λ§ κ³΅κ°λ©λλ€.



