๐ฏ ์ง์ค๋ ฅ ํ์ด๋จธ & ์ผ์ผ ๋ชฉํ ํธ๋์ปค - ํฌ๋ชจ๋๋ก ๊ธฐ๋ฒ with ์ฑ์ทจ ๋ฆฌ์๋
focus-timer-daily-goal-tracker
๋ฌธ์ ์ค๋ช
ํ๋ก์ ํธ ๊ฐ์
ํฌ๋ชจ๋๋ก ๊ธฐ๋ฒ(25๋ถ ์ง์ค + 5๋ถ ํด์)์ ํ์ฉํ ์ง์ค๋ ฅ ํ์ด๋จธ์ ์ผ์ผ ๋ชฉํ ๋ฌ์ฑ ๊ธฐ๋ก์ ํตํฉํ ๊ฐ์ธ ์์ฐ์ฑ ๋๊ตฌ์ ๋๋ค. ์ฌ์ฉ์๋ ์ค๋ ํด์ผ ํ ์์ ์ ๋ฑ๋กํ๊ณ , ํ์ด๋จธ๋ฅผ ๋๋ ค ์ง์ค ์ธ์ ์ ์๋ฃํ๋ฉฐ, ์๋ฃํ ์ธ์ ์์ ๋ฐ๋ผ ์ฑ์ทจ ๋ฐฐ์ง๋ฅผ ํ๋ํฉ๋๋ค.
ํ์ ๊ธฐ๋ฅ
-
์ผ์ผ ๋ชฉํ ๊ด๋ฆฌ
- ์ค๋ ํ ์์ (ํ์คํฌ) ๋ฑ๋ก/์์ /์ญ์ (SQLite ์ ์ฅ)
- ๊ฐ ํ์คํฌ๋ณ ์์ ํฌ๋ชจ๋๋ก ๊ฐ์ ์ค์
- ์๋ฃ ์ฒดํฌ๋ฐ์ค๋ก ํ์คํฌ ์๋ฃ ์ฒ๋ฆฌ
-
ํฌ๋ชจ๋๋ก ํ์ด๋จธ
- 25๋ถ ์ง์ค ๋ชจ๋ ํ์ด๋จธ (JavaScript setInterval)
- 5๋ถ ํด์ ๋ชจ๋ ์๋ ์ ํ
- ํ์ด๋จธ ์์/์ผ์์ ์ง/๋ฆฌ์ ๋ฒํผ
- ๋จ์ ์๊ฐ ์ค์๊ฐ ํ์ (๋ถ:์ด ํ์)
- ํ์ด๋จธ ์ข ๋ฃ ์ ๋ธ๋ผ์ฐ์ ์๋ฆผ ๋๋ ์ฌ์ด๋ ์ฌ์
-
์ธ์ ๊ธฐ๋ก & ํต๊ณ
- ์๋ฃํ ํฌ๋ชจ๋๋ก ์ธ์ DB ์ ์ฅ (๋ ์ง, ํ์คํฌ๋ช , ์๋ฃ ์๊ฐ)
- ์ค๋ ์๋ฃํ ์ด ์ธ์ ์ ํ์
- ์ฃผ๊ฐ/์๊ฐ ์ธ์ ํต๊ณ (๊ฐ๋จํ ์ฐจํธ ๋๋ ์ซ์)
-
์ฑ์ทจ ๋ฐฐ์ง ์์คํ
- ์ธ์ ์ ๊ธฐ์ค ๋ฐฐ์ง (์: 5์ธ์ , 10์ธ์ , 20์ธ์ )
- ์ฐ์ ๋ฌ์ฑ ์ผ์ ๋ฐฐ์ง (์: 3์ผ ์ฐ์, 7์ผ ์ฐ์)
- ๋ฐฐ์ง ๊ฐค๋ฌ๋ฆฌ ํ์ด์ง์์ ํ๋ํ ๋ฐฐ์ง ํ์ธ
-
UI/UX
- ํ์ด๋จธ ํ๋ฉด: ํฐ ์ซ์๋ก ๋จ์ ์๊ฐ ํ์, ์งํ ๋ฐ ๋๋ ์ํ ํ๋ก๊ทธ๋ ์ค
- ํ์คํฌ ๋ชฉ๋ก: ๋๋๊ทธ ์ค ๋๋กญ์ผ๋ก ์ฐ์ ์์ ๋ณ๊ฒฝ (์ ํ)
- ๋คํฌ ๋ชจ๋ ํ ๊ธ (CSS ๋ณ์ ํ์ฉ)
์ ํ ์ถ๊ฐ ๊ธฐ๋ฅ (๋ณด๋์ค)
- ์ปค์คํ ํ์ด๋จธ ๊ธธ์ด ์ค์ (25๋ถ ์ธ ๋ค๋ฅธ ์๊ฐ)
- ๋ฐฐ๊ฒฝ์์ ๋๋ ๋ฐฑ์์์ ์ฌ์ ๊ธฐ๋ฅ
- ์ฃผ๊ฐ ๋ฆฌํฌํธ PDF ๋ค์ด๋ก๋
- ํ์คํฌ๋ณ ๋์ ํฌ๋ชจ๋๋ก ์๊ฐ ์๊ฐํ (ํ์ด ์ฐจํธ)
- ์น๊ตฌ์ ์ธ์ ์ ๊ฒฝ์ (๊ฐ๋จํ ๋ฆฌ๋๋ณด๋)
๊ธฐ์ ์คํ
- ๋ฐฑ์๋: ์ผ๋ฐ PHP 8.3+ (ํ๋ ์์ํฌ ์์)
- ๋ฐ์ดํฐ๋ฒ ์ด์ค: PDO + SQLite
- ํ๋ก ํธ์๋: ์์ HTML/CSS/JavaScript (๋ฐ๋๋ผ JS)
- ๋ณด์: prepared statement, XSS ๋ฐฉ์ด (htmlspecialchars), CSRF ํ ํฐ
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค๊ณ
tasksํ ์ด๋ธ: id, title, estimated_pomodoros, completed, created_atsessionsํ ์ด๋ธ: id, task_id, completed_at, duration_minutesbadgesํ ์ด๋ธ: id, user_id, badge_type, earned_at (์ ํ)
ํ์ต ๋ชฉํ
- PHP ์ธ์ ๊ณผ SQLite๋ฅผ ํ์ฉํ ์ฌ์ฉ์ ๋ฐ์ดํฐ ์์ํ
- JavaScript ํ์ด๋จธ ๋ก์ง๊ณผ ๋น๋๊ธฐ UI ์ ๋ฐ์ดํธ
- AJAX(fetch API)๋ฅผ ํ์ฉํ ์๋ฒ-ํด๋ผ์ด์ธํธ ํต์
- ๊ฐ๋จํ ๊ฒ์ด๋ฏธํผ์ผ์ด์ ์์ ๊ตฌํ
์ ์ฝ ์กฐ๊ฑด
- โข ์ผ๋ฐ PHP 8.3+ ์ฌ์ฉ (Laravel, Symfony ๋ฑ ํ๋ ์์ํฌ ๊ธ์ง)
- โข PDO + SQLite๋ก ๋ฐ์ดํฐ ์ ์ฅ
- โข ์์ HTML/CSS/JavaScript (React, Vue ๋ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ธ์ง)
- โข php -S localhost:8000์ผ๋ก ์คํ ๊ฐ๋ฅํด์ผ ํจ
- โข Prepared statement๋ก SQL Injection ๋ฐฉ์ด
- โข htmlspecialchars()๋ก XSS ๋ฐฉ์ด
- โข CSRF ํ ํฐ์ผ๋ก ํผ ๋ณดํธ
- โข ํ์ด๋จธ๋ ํด๋ผ์ด์ธํธ ์ธก JavaScript๋ก ๊ตฌํ (์๋ฒ ํด๋ง ๊ธ์ง)
- โข ๋ฐ์ํ ๋์์ธ (๋ชจ๋ฐ์ผ์์๋ ์ฌ์ฉ ๊ฐ๋ฅ)
ํ๋กฌํํธ ๋ ์ํผ
์๋ ํ๋กฌํํธ๋ฅผ ์์๋๋ก AI์๊ฒ ๋ณด๋ด๋ฉด ๋ฉ๋๋ค. ๋ณต์ฌ ๋ฒํผ์ ๋๋ฌ ๋ฐ๋ก ์ฌ์ฉํ์ธ์.
mkdir focus-timer-daily-goal-tracker && cd focus-timer-daily-goal-tracker
์ด ํด๋์์ ํ๋ก์ ํธ๋ฅผ ์์ํฉ๋๋ค.
# 1๋จ๊ณ: ํ๋ก์ ํธ ๊ตฌ์กฐ ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๊ธฐํ
์ผ๋ฐ PHP 8.3+ ๊ธฐ๋ฐ ํฌ๋ชจ๋๋ก ํ์ด๋จธ & ์ผ์ผ ๋ชฉํ ํธ๋์ปค๋ฅผ ๋ง๋ญ๋๋ค. ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์์ PHP๋ก ๊ตฌํํ๋ฉฐ, SQLite๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํฉ๋๋ค.
## ํ์ผ ๊ตฌ์กฐ
```
focus-timer-daily-goal-tracker/
โโโ index.php # ๋ฉ์ธ ํ์ด์ง (ํ์ด๋จธ + ํ์คํฌ ๋ชฉ๋ก)
โโโ tasks.php # ํ์คํฌ CRUD API ์๋ํฌ์ธํธ
โโโ sessions.php # ์ธ์
๊ธฐ๋ก API ์๋ํฌ์ธํธ
โโโ stats.php # ํต๊ณ ํ์ด์ง
โโโ badges.php # ๋ฐฐ์ง ๊ฐค๋ฌ๋ฆฌ ํ์ด์ง
โโโ db.php # ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๋ฐ ์ด๊ธฐํ
โโโ config.php # ์ค์ ํ์ผ (DB ๊ฒฝ๋ก, CSRF ํ ํฐ ๋ฑ)
โโโ style.css # ์คํ์ผ์ํธ
โโโ app.js # ํ์ด๋จธ & AJAX ๋ก์ง
โโโ data/
โโโ pomodoro.db # SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์ผ
```
## ์๊ตฌ์ฌํญ
1. `config.php`: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฒฝ๋ก, ์ธ์
์ค์ , CSRF ํ ํฐ ์์ฑ ํจ์ ํฌํจ
2. `db.php`: SQLite ์ฐ๊ฒฐ ๋ฐ ํ
์ด๋ธ ์์ฑ (tasks, sessions, badges)
- `tasks`: id, title, estimated_pomodoros, completed, created_at
- `sessions`: id, task_id, completed_at, duration_minutes
- `badges`: id, badge_type, earned_at (์ ํ)
3. `index.php`: ํ์ด๋จธ UI + ์ค๋์ ํ์คํฌ ๋ชฉ๋ก ํ์. PHP ์ธ์
์์.
4. `tasks.php`: RESTful ์คํ์ผ API (GET: ๋ชฉ๋ก, POST: ์์ฑ, PUT: ์์ , DELETE: ์ญ์ )
5. `sessions.php`: ์ธ์
๊ธฐ๋ก ์ ์ฅ API (POST)
6. ๋ชจ๋ ์
๋ ฅ์ prepared statement๋ก ์ฒ๋ฆฌ
7. ์ถ๋ ฅ์ htmlspecialchars()๋ก ์ด์ค์ผ์ดํ
๊ฐ ํ์ผ์ ์ฃผ์์ผ๋ก ์ญํ ์ ๋ช
์ํ๊ณ , ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ด ์์ผ๋ฉด ์๋ ์์ฑ๋๋๋ก ๊ตฌํํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: `config.php`, `db.php`, `index.php`, `tasks.php`, `sessions.php`๊ฐ ์์ฑ๋๊ณ , SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ด๊ธฐํ๋ฉ๋๋ค. `php -S localhost:8000`์ผ๋ก ์๋ฒ๋ฅผ ์คํํ๋ฉด `index.php`์์ ํ์ด๋จธ์ ํ์คํฌ ๋ชฉ๋ก UI๊ฐ ๋ณด์ ๋๋ค (์์ง ๊ธฐ๋ฅ์ ๋ฏธ๊ตฌํ).
# 2๋จ๊ณ: ํ์คํฌ CRUD ๊ตฌํ ๋ฐ ๋ชฉ๋ก ํ์
`tasks.php`์์ ํ์คํฌ ์์ฑ/์กฐํ/์์ /์ญ์ API๋ฅผ ์์ฑํ๊ณ , `index.php`์์ AJAX๋ก ํ์คํฌ ๋ชฉ๋ก์ ๋ถ๋ฌ์ ํ์ํฉ๋๋ค.
## ์๊ตฌ์ฌํญ
1. **tasks.php**:
- `GET /tasks.php`: ์ค๋ ์์ฑ๋ ํ์คํฌ ๋ชฉ๋ก JSON ๋ฐํ (completed ์ํ ํฌํจ)
- `POST /tasks.php`: ์ ํ์คํฌ ์์ฑ (title, estimated_pomodoros ๋ฐ๊ธฐ)
- `PUT /tasks.php?id={id}`: ํ์คํฌ ์์ (title, estimated_pomodoros, completed ์
๋ฐ์ดํธ)
- `DELETE /tasks.php?id={id}`: ํ์คํฌ ์ญ์
- CSRF ํ ํฐ ๊ฒ์ฆ (POST/PUT/DELETE ์์ฒญ ์)
- JSON ์๋ต: `{"success": true, "data": ...}` ๋๋ `{"success": false, "error": "..."}`
2. **index.php**:
- ํ์คํฌ ์ถ๊ฐ ํผ (์ ๋ชฉ, ์์ ํฌ๋ชจ๋๋ก ๊ฐ์ ์
๋ ฅ)
- ํ์คํฌ ๋ชฉ๋ก์ `<ul>` ๋๋ `<div>` ๋ฆฌ์คํธ๋ก ํ์
- ๊ฐ ํ์คํฌ์ ์๋ฃ ์ฒดํฌ๋ฐ์ค, ์์ /์ญ์ ๋ฒํผ ์ถ๊ฐ
- ํ์ด์ง ๋ก๋ ์ `app.js`์์ `fetch('/tasks.php')`๋ก ๋ชฉ๋ก ๋ถ๋ฌ์ค๊ธฐ
3. **app.js**:
- `loadTasks()`: GET ์์ฒญ์ผ๋ก ํ์คํฌ ๋ชฉ๋ก ๊ฐ์ ธ์ DOM์ ๋ ๋๋ง
- `addTask(title, estimatedPomodoros)`: POST ์์ฒญ์ผ๋ก ํ์คํฌ ์์ฑ
- `updateTask(id, data)`: PUT ์์ฒญ์ผ๋ก ํ์คํฌ ์์
- `deleteTask(id)`: DELETE ์์ฒญ์ผ๋ก ํ์คํฌ ์ญ์
- CSRF ํ ํฐ์ ํค๋ ๋๋ ๋ฐ๋์ ํฌํจ
4. **style.css**:
- ํ์คํฌ ๋ชฉ๋ก ์คํ์ผ๋ง (์ฒดํฌ๋ฐ์ค, ๋ฒํผ ์ ๋ ฌ)
- ์๋ฃ๋ ํ์คํฌ๋ ํ
์คํธ ์ทจ์์ ๋๋ ํ์ ์ฒ๋ฆฌ
๋ชจ๋ API ์๋ต์ JSON ํ์์ด๋ฉฐ, ์๋ฌ ์ ์ ์ ํ HTTP ์ํ ์ฝ๋(400, 500 ๋ฑ)๋ฅผ ๋ฐํํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: `index.php`์์ ํ์คํฌ๋ฅผ ์ถ๊ฐํ๋ฉด ๋ชฉ๋ก์ ์ฆ์ ํ์๋๊ณ , ์๋ฃ ์ฒดํฌ๋ฐ์ค๋ฅผ ํด๋ฆญํ๋ฉด ์ทจ์์ ์ด ๊ทธ์ด์ง๋๋ค. ์์ /์ญ์ ๋ฒํผ๋ ์ ์ ์๋ํ๋ฉฐ, ์๋ก๊ณ ์นจ ํ์๋ ๋ฐ์ดํฐ๊ฐ ์ ์ง๋ฉ๋๋ค.
# 3๋จ๊ณ: ํฌ๋ชจ๋๋ก ํ์ด๋จธ ๊ตฌํ
`index.php`์ ํ์ด๋จธ UI๋ฅผ ์ถ๊ฐํ๊ณ , `app.js`์์ 25๋ถ ์ง์ค ๋ชจ๋์ 5๋ถ ํด์ ๋ชจ๋๋ฅผ ์๋ ์ ํํ๋ ํ์ด๋จธ ๋ก์ง์ ๊ตฌํํฉ๋๋ค.
## ์๊ตฌ์ฌํญ
1. **index.php ํ์ด๋จธ UI**:
- ํฐ ์ซ์๋ก ๋จ์ ์๊ฐ ํ์ (`<div id="timer">25:00</div>`)
- ์์/์ผ์์ ์ง/๋ฆฌ์
๋ฒํผ
- ํ์ฌ ๋ชจ๋ ํ์ ("์ง์ค ๋ชจ๋" ๋๋ "ํด์ ๋ชจ๋")
- ์งํ ๋ฐ ๋๋ ์ํ ํ๋ก๊ทธ๋ ์ค ๋ฐ (์ ํ)
- ํ์คํฌ ์ ํ ๋๋กญ๋ค์ด (ํ์ด๋จธ๋ฅผ ์ด๋ค ํ์คํฌ์ ์ฐ๊ฒฐํ ์ง ์ ํ)
2. **app.js ํ์ด๋จธ ๋ก์ง**:
- `startTimer()`: ํ์ด๋จธ ์์ (setInterval 1์ด๋ง๋ค ์นด์ดํธ๋ค์ด)
- `pauseTimer()`: ํ์ด๋จธ ์ผ์์ ์ง (clearInterval)
- `resetTimer()`: ํ์ด๋จธ ์ด๊ธฐํ (25:00์ผ๋ก ๋ฆฌ์
)
- 25๋ถ ์๋ฃ ์:
- ๋ธ๋ผ์ฐ์ ์๋ฆผ ๋๋ ์ค๋์ค ์ฌ์ ("์ง์ค ์ธ์
์๋ฃ!")
- ์๋์ผ๋ก 5๋ถ ํด์ ๋ชจ๋๋ก ์ ํ
- 5๋ถ ์๋ฃ ์:
- ์๋ฆผ ("ํด์ ๋! ๋ค์ ์ง์คํ์ธ์")
- ์๋์ผ๋ก 25๋ถ ์ง์ค ๋ชจ๋๋ก ์ ํ
- ํ์ด๋จธ ์ข
๋ฃ ์ ์ ํ๋ ํ์คํฌ์ ํจ๊ป ์ธ์
๊ธฐ๋ก ์ ์ฅ (POST `/sessions.php`)
3. **sessions.php**:
- `POST /sessions.php`: ์ธ์
๊ธฐ๋ก ์ ์ฅ (task_id, completed_at, duration_minutes)
- JSON ์๋ต: `{"success": true, "session_id": ...}`
4. **style.css**:
- ํ์ด๋จธ ์ซ์๋ฅผ ํ๋ฉด ์ค์์ ํฌ๊ฒ ํ์ (font-size: 4rem ์ด์)
- ์ง์ค ๋ชจ๋๋ ํ๋์, ํด์ ๋ชจ๋๋ ์ด๋ก์ ๋ฐฐ๊ฒฝ (์ ํ)
- ๋ฒํผ ์คํ์ผ๋ง (์์: ์ด๋ก์, ์ผ์์ ์ง: ๋
ธ๋์, ๋ฆฌ์
: ๋นจ๊ฐ์)
๋ธ๋ผ์ฐ์ ์๋ฆผ์ ์ฌ์ฉํ๋ ค๋ฉด `Notification.requestPermission()`์ผ๋ก ๊ถํ ์์ฒญ ํ `new Notification(...)`์ผ๋ก ์๋ฆผ์ ํ์ํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ํ์ด๋จธ๋ฅผ ์์ํ๋ฉด 25:00์์ ์นด์ดํธ๋ค์ด์ด ์์๋๊ณ , 0:00์ด ๋๋ฉด ์๋ฆผ์ด ๋จ๋ฉฐ ์๋์ผ๋ก 5๋ถ ํด์ ๋ชจ๋๋ก ์ ํ๋ฉ๋๋ค. ํด์ ์ข ๋ฃ ํ ๋ค์ 25๋ถ ์ง์ค ๋ชจ๋๋ก ๋์์ต๋๋ค. ์ธ์ ์๋ฃ ์ DB์ ๊ธฐ๋ก์ด ์ ์ฅ๋ฉ๋๋ค.
# 4๋จ๊ณ: ํต๊ณ ํ์ด์ง ๊ตฌํ `stats.php` ํ์ด์ง๋ฅผ ๋ง๋ค์ด ์ค๋/์ด๋ฒ ์ฃผ/์ด๋ฒ ๋ฌ ์๋ฃํ ํฌ๋ชจ๋๋ก ์ธ์ ํต๊ณ๋ฅผ ํ์ํฉ๋๋ค. ## ์๊ตฌ์ฌํญ 1. **stats.php**: - ํ์ด์ง ์๋จ์ "ํต๊ณ" ์ ๋ชฉ - ์ค๋ ์๋ฃํ ์ธ์ ์ ํ์ - ์ด๋ฒ ์ฃผ ์๋ฃํ ์ธ์ ์ ํ์ - ์ด๋ฒ ๋ฌ ์๋ฃํ ์ธ์ ์ ํ์ - ํ์คํฌ๋ณ ๋์ ์ธ์ ์๊ฐ ํ์ (ํ ์ด๋ธ ํ์) - ์ฃผ๊ฐ ์ธ์ ์ถ์ด ์ฐจํธ (์ ํ, ๊ฐ๋จํ ๋ง๋ ๊ทธ๋ํ) 2. **๋ฐ์ดํฐ ์กฐํ**: - `sessions` ํ ์ด๋ธ์์ `completed_at` ๊ธฐ์ค์ผ๋ก ์ค๋/์ด๋ฒ ์ฃผ/์ด๋ฒ ๋ฌ ํํฐ๋ง - `GROUP BY task_id`๋ก ํ์คํฌ๋ณ ํฉ๊ณ ๊ณ์ฐ - PHP์์ SQL ์ฟผ๋ฆฌ ์คํ ํ ๊ฒฐ๊ณผ๋ฅผ HTML ํ ์ด๋ธ๋ก ๋ ๋๋ง 3. **์ฐจํธ (์ ํ)**: - ์ต๊ทผ 7์ผ๊ฐ ์ผ๋ณ ์ธ์ ์๋ฅผ ๋ง๋ ๊ทธ๋ํ๋ก ํ์ - Chart.js ๋๋ ์์ CSS๋ก ๊ฐ๋จํ ๋ง๋ ๊ทธ๋ํ ๊ตฌํ 4. **๋ค๋น๊ฒ์ด์ **: - `index.php`์ "ํต๊ณ ๋ณด๊ธฐ" ๋งํฌ ์ถ๊ฐ - `stats.php`์ "ํ์ด๋จธ๋ก ๋์๊ฐ๊ธฐ" ๋งํฌ ์ถ๊ฐ 5. **style.css**: - ํต๊ณ ์ซ์๋ฅผ ๊ฐ์กฐ (ํฐ ํฐํธ, ์ปฌ๋ฌ ๋ฐ์ค) - ํ ์ด๋ธ ์คํ์ผ๋ง (ํค๋, ํ ๊ตฌ๋ถ) ๋ชจ๋ ๋ ์ง ๊ณ์ฐ์ PHP `strtotime()` ๋๋ `DateTime` ํด๋์ค๋ฅผ ์ฌ์ฉํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: `stats.php`์์ ์ค๋/์ด๋ฒ ์ฃผ/์ด๋ฒ ๋ฌ ์๋ฃํ ์ธ์ ์๊ฐ ํ์๋๊ณ , ํ์คํฌ๋ณ ๋์ ์๊ฐ ํ ์ด๋ธ์ด ๋ณด์ ๋๋ค. ์ฐจํธ๊ฐ ์๋ค๋ฉด ์ต๊ทผ 7์ผ๊ฐ ์ธ์ ์ถ์ด๊ฐ ์๊ฐํ๋ฉ๋๋ค.
# 5๋จ๊ณ: ๋ฐฐ์ง ์์คํ ๊ตฌํ ์ธ์ ์์ ์ฐ์ ๋ฌ์ฑ ์ผ์์ ๋ฐ๋ผ ๋ฐฐ์ง๋ฅผ ์๋์ผ๋ก ๋ถ์ฌํ๊ณ , `badges.php` ํ์ด์ง์์ ํ๋ํ ๋ฐฐ์ง๋ฅผ ํ์ํฉ๋๋ค. ## ์๊ตฌ์ฌํญ 1. **๋ฐฐ์ง ์ข ๋ฅ**: - ์ธ์ ์ ๋ฐฐ์ง: "5์ธ์ ๋ฌ์ฑ", "10์ธ์ ๋ฌ์ฑ", "20์ธ์ ๋ฌ์ฑ", "50์ธ์ ๋ฌ์ฑ" - ์ฐ์ ๋ฌ์ฑ ๋ฐฐ์ง: "3์ผ ์ฐ์", "7์ผ ์ฐ์", "30์ผ ์ฐ์" - ํน๋ณ ๋ฐฐ์ง: "์ฒซ ์ธ์ ์๋ฃ", "์๋ฒฝ ์ฌ๋นผ๋ฏธ (์๋ฒฝ 2~5์ ์ธ์ )" 2. **๋ฐฐ์ง ๋ถ์ฌ ๋ก์ง**: - `sessions.php`์์ ์ธ์ ์ ์ฅ ํ, ์ด ์ธ์ ์๋ฅผ ํ์ธํ์ฌ ๋ฐฐ์ง ์กฐ๊ฑด ์ถฉ์กฑ ์ `badges` ํ ์ด๋ธ์ ์ฝ์ - ์ฐ์ ๋ฌ์ฑ ์ผ์๋ `sessions` ํ ์ด๋ธ์์ ์ต๊ทผ ๋ ์ง ์ฐ์์ฑ์ ์ฒดํฌ - ์ด๋ฏธ ํ๋ํ ๋ฐฐ์ง๋ ์ค๋ณต ๋ถ์ฌํ์ง ์์ (UNIQUE ์ ์ฝ ๋๋ SELECT ํ ์ฒดํฌ) 3. **badges.php**: - ํ๋ํ ๋ฐฐ์ง ๋ชฉ๋ก์ ์นด๋ ํ์์ผ๋ก ํ์ (๋ฐฐ์ง ์์ด์ฝ + ์ด๋ฆ + ํ๋ ๋ ์ง) - ์์ง ํ๋ํ์ง ๋ชปํ ๋ฐฐ์ง๋ ํ์ ๋๋ ์ ๊ธ ์์ด์ฝ์ผ๋ก ํ์ (์ ํ) - ๋ฐฐ์ง๋ณ ์ค๋ช ํดํ (hover ์ ํ์) 4. **index.php**: - ์๋จ์ "ํ๋ํ ๋ฐฐ์ง ์" ํ์ (`count(badges)` ์ฟผ๋ฆฌ) - "๋ฐฐ์ง ๊ฐค๋ฌ๋ฆฌ" ๋งํฌ ์ถ๊ฐ 5. **style.css**: - ๋ฐฐ์ง ์นด๋ ์คํ์ผ๋ง (์์ด์ฝ, ๊ทธ๋ฆผ์, ํธ๋ฒ ํจ๊ณผ) - ํ๋ํ ๋ฐฐ์ง๋ ์ปฌ๋ฌํํ๊ฒ, ๋ฏธํ๋ ๋ฐฐ์ง๋ ํ์ ์ฒ๋ฆฌ ๋ฐฐ์ง ์์ด์ฝ์ ์ด๋ชจ์ง(๐, ๐ฏ, ๐ฅ ๋ฑ) ๋๋ Font Awesome ์์ด์ฝ์ ์ฌ์ฉํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ์ธ์ ์ ์๋ฃํ๋ฉด ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ฐฐ์ง๊ฐ ์๋์ผ๋ก ๋ถ์ฌ๋๊ณ , `badges.php`์์ ํ๋ํ ๋ฐฐ์ง๊ฐ ์นด๋ ํ์์ผ๋ก ํ์๋ฉ๋๋ค. ์์ง ํ๋ํ์ง ๋ชปํ ๋ฐฐ์ง๋ ์ ๊ธ ์ํ๋ก ๋ณด์ ๋๋ค.
# 6๋จ๊ณ: UI/UX ๊ฐ์ ๋ฐ ๋ฐ์ํ ๋์์ธ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๊ธฐ ์ํด UI๋ฅผ ๋ค๋ฌ๊ณ , ๋ชจ๋ฐ์ผ์์๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ๋ฐ์ํ ๋์์ธ์ ์ ์ฉํฉ๋๋ค. ## ์๊ตฌ์ฌํญ 1. **๋คํฌ ๋ชจ๋ ํ ๊ธ**: - `index.php` ์๋จ์ ๋คํฌ ๋ชจ๋ ํ ๊ธ ๋ฒํผ ์ถ๊ฐ - JavaScript๋ก `<body>`์ `dark-mode` ํด๋์ค ์ถ๊ฐ/์ ๊ฑฐ - CSS ๋ณ์๋ก ์์ ์ ์ (`:root`์ `.dark-mode`) - ๋คํฌ ๋ชจ๋ ์ํ๋ฅผ localStorage์ ์ ์ฅํ์ฌ ์๋ก๊ณ ์นจ ํ์๋ ์ ์ง 2. **๋ฐ์ํ ๋์์ธ**: - ๋ชจ๋ฐ์ผ(< 768px)์์ ํ์ด๋จธ์ ํ์คํฌ ๋ชฉ๋ก์ด ์ธ๋ก๋ก ๋ฐฐ์น - ํ๋ธ๋ฆฟ/๋ฐ์คํฌํ์์๋ ํ์ด๋จธ ์ผ์ชฝ, ํ์คํฌ ๋ชฉ๋ก ์ค๋ฅธ์ชฝ ๋ฐฐ์น - ๋ฒํผ ํฌ๊ธฐ๋ฅผ ํฐ์นํ๊ธฐ ํธํ๊ฒ ์กฐ์ (์ต์ 44x44px) 3. **์ ๋๋ฉ์ด์ & ์ธํฐ๋์ **: - ํ์คํฌ ์ถ๊ฐ/์ญ์ ์ fade-in/fade-out ์ ๋๋ฉ์ด์ - ํ์ด๋จธ ์ซ์ ๋ณ๊ฒฝ ์ ๋ถ๋๋ฌ์ด ์ ํ ํจ๊ณผ - ์๋ฃ ์ฒดํฌ๋ฐ์ค ํด๋ฆญ ์ ์ฒดํฌ ์ ๋๋ฉ์ด์ - ๋ฒํผ ํธ๋ฒ ์ ์์ ๋ณํ ๋๋ ํ๋ ํจ๊ณผ 4. **์งํ ๋ฐ ๊ฐ์ **: - ํ์ด๋จธ ์๋์ ์ํ ๋๋ ์ ํ ํ๋ก๊ทธ๋ ์ค ๋ฐ ์ถ๊ฐ - CSS `stroke-dashoffset` ๋๋ `width` ์ ๋๋ฉ์ด์ ์ผ๋ก ์งํ๋ฅ ํ์ 5. **์ ๊ทผ์ฑ ๊ฐ์ **: - ๋ฒํผ์ `aria-label` ์ถ๊ฐ - ํผ ์ ๋ ฅ ํ๋์ `<label>` ์ฐ๊ฒฐ - ํค๋ณด๋๋ก ๋ชจ๋ ๊ธฐ๋ฅ ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก `tabindex` ์ค์ 6. **์๋ฌ ๋ฉ์์ง & ๋ก๋ฉ ์ํ**: - AJAX ์์ฒญ ์คํจ ์ ์ฌ์ฉ์์๊ฒ ์๋ฌ ๋ฉ์์ง ํ์ - ๋ก๋ฉ ์ค์ผ ๋ ์คํผ๋ ๋๋ "๋ก๋ฉ ์ค..." ๋ฉ์์ง ํ์ ๋ชจ๋ ์ ๋๋ฉ์ด์ ์ CSS transition ๋๋ @keyframes๋ก ๊ตฌํํ์ธ์.
๊ธฐ๋ ๊ฒฐ๊ณผ: ๋คํฌ ๋ชจ๋ ํ ๊ธ์ด ์๋ํ๊ณ , ๋ชจ๋ฐ์ผ์์๋ UI๊ฐ ๊นจ์ง์ง ์์ต๋๋ค. ํ์คํฌ ์ถ๊ฐ/์ญ์ ์ ๋ถ๋๋ฌ์ด ์ ๋๋ฉ์ด์ ์ด ์ ์ฉ๋๊ณ , ํ์ด๋จธ ์งํ ๋ฐ๊ฐ ์ค์๊ฐ์ผ๋ก ์ ๋ฐ์ดํธ๋ฉ๋๋ค. ํค๋ณด๋๋ง์ผ๋ก๋ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ ์ถ๋ ์ํ (0)
๋ก๊ทธ์ธ ํ ์ ์ถ์์ง ์ ์ถ๋ ์ํ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋์ ์๊ฐ ๋์ด๋ณด์ธ์!