๊ทธ๋ฃน์ผ๋ก ์ง์์ผ์ ํจ๊ป ๊ด๋ฆฌํ๊ณ , ์๋ฆผ๊ณผ ์ฃผ๊ฐ ํต๊ณ๋ก ๊ธฐ์ฌ๋๋ฅผ ์ถ์ ํ๋ ๋ฐฑ์๋ API ์๋ฒ
![]()
https://play.google.com/store/apps/details?id=com.rfive.hatoo&hl=ko
Hatoo๋ ๊ฐ์กฑ, ๋ฃธ๋ฉ์ดํธ ๋ฑ ์๊ท๋ชจ ๊ทธ๋ฃน์ด ์ง์์ผ์ ํจ์จ์ ์ผ๋ก ๋ถ๋ดํ ์ ์๋๋ก ๋๋ ์๋น์ค์ ๋๋ค. ํ ์ผ ๋ฑ๋ก ๋ฐ ๋ฐฐ์ , FCM ํธ์ ์๋ฆผ, ์ฃผ๊ฐ ๊ธฐ์ฌ๋ ํต๊ณ, ์นด์นด์ค/๋ค์ด๋ฒ ์์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
| ์ญํ | ์ธ์ | ๋ด๋น |
|---|---|---|
| Backend | 1๋ช (๋ณธ์ธ) | API ์๋ฒ ์ค๊ณ ๋ฐ ๊ฐ๋ฐ, DB ์ค๊ณ, ๋ฐฐํฌ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ |
| Frontend | 1๋ช | Android(๊ฐ๋ฐํ ์คํธ ์ค) / iOS ์ฑ ๊ฐ๋ฐ(์์ ) |
| Designer | 1๋ช | UI/UX ๋์์ธ, ํ๋ฉด ์ค๊ณ |
| Project Manager | 1๋ช | ์ผ์ ๊ด๋ฆฌ, ์๊ตฌ์ฌํญ ์ ์, QA |
| ๋ถ๋ฅ | ๊ธฐ์ |
|---|---|
| Language | Java 17 |
| Framework | Spring Boot 3.4.3 |
| ORM | Spring Data JPA / Hibernate |
| DB | MySQL 8 |
| ์ธ์ฆ | JWT (Access + Refresh Token), Spring Security |
| ์์ ๋ก๊ทธ์ธ | Kakao OAuth2, Naver OAuth2 |
| ํธ์ ์๋ฆผ | Firebase Cloud Messaging (FCM) |
| ์ด๋ฉ์ผ | Spring Mail (Gmail SMTP) |
| ๋ฌธ์ํ | SpringDoc OpenAPI (Swagger UI) |
| ๋น๋ | Gradle |
| ๋ฐฐํฌ | GitHub Actions โ GHCR โ Self-hosted Runner (NAS) |
- ๊ทธ๋ฃน ์์ฑ / ์ด๋์ฝ๋ ๋ฐ๊ธ / ์ฐธ์ฌ / ํํด / ๊ฐ์ ํด์ฅ
- ๊ฐ์ธ ๊ทธ๋ฃน(ํผ์ ์ฌ์ฉ) / ์ผ๋ฐ ๊ทธ๋ฃน ๊ตฌ๋ถ
- ๊ทธ๋ฃน๋ณ ํ๋กํ ์ด๋ฏธ์ง ์ ํ
- ํ ์ผ ๋ฑ๋ก / ์์ / ์ญ์ / ์๋ฃ ์ฒ๋ฆฌ
- ๋ด๋น์ ๋ฐฐ์ (๊ทธ๋ฃน์ ์ค 1๋ช )
- ๋ฐ๋ณต ์ฃผ๊ธฐ ์ค์ : ์์ / ๋งค์๊ฐ / ๋งค์ผ / ๋งค์ฃผ / ๋งค๋ฌ
- ๋ง๊ฐ ์๋ฆผ ์ค์ : 10๋ถ ์ / 30๋ถ ์ / 1์๊ฐ ์ / 1์ผ ์ / 1์ฃผ ์
- ํ ์ผ ์์ ์๋ฆผ (30์ด ์ฃผ๊ธฐ ์ค์บ)
- ๋ง๊ฐ ์๋ฐ ์๋ฆผ (30์ด ์ฃผ๊ธฐ ์ค์บ)
- ๋ง๊ฐ ์ด๊ณผ ์๋ฆผ (30์ด ์ฃผ๊ธฐ ์ค์บ)
- ์ ์ง์์ผ ๋ฑ๋ก ์๋ฆผ (๊ทธ๋ฃน ์ ์ฒด)
- ํ ์ผ ๋ฐฐ์ ์๋ฆผ (๋ฐฐ์ ๋ฐ์ ๋ณธ์ธ์๊ฒ๋ง)
- ์ ๋ฉค๋ฒ ํฉ๋ฅ ์๋ฆผ
- ์ฃผ๊ฐ ํต๊ณ ๊ณต๊ฐ ์๋ฆผ (๋งค์ฃผ ์์์ผ ์ค์ 8์)
- ๋นํ์ฑ ๊ทธ๋ฃน ์๋ฆผ (๋งค์ 1์ผ)
- ์๋ฆผ ์์ ์ฌ๋ถ๋ฅผ ์ ์ฒด/๊ฐ์ธ/๊ทธ๋ฃน๋ณ๋ก ์ธ๋ถํํ์ฌ ์ค์ ๊ฐ๋ฅ
- ๋งค์ฃผ ์์์ผ ์ค์ 8์์ ์ง๋ ์ฃผ ๊ธฐ์ฌ๋ ์๋ ์ค๋ ์ท ์ ์ฅ
- ๊ทธ๋ฃน์๋ณ ์๋ฃ์จ ๋ญํน ์กฐํ (์ค์๊ฐ)
- ์ฃผ์ฐจ๋ณ ๊ธฐ์ฌ๋ ์ด๋ ฅ ์กฐํ
- ์์ฒด ํ์๊ฐ์ / ๋ก๊ทธ์ธ (BCrypt ์ํธํ)
- ์นด์นด์ค / ๋ค์ด๋ฒ ์์ ๋ก๊ทธ์ธ
- JWT Access Token + Refresh Token
- ์ด๋ฉ์ผ ์ธ์ฆ ์ฝ๋ (์์ด๋ ์ฐพ๊ธฐ / ๋น๋ฐ๋ฒํธ ์ฌ์ค์ )
[Android / iOS ์ฑ]
โ HTTPS
โผ
[Spring Boot API ์๋ฒ]
โ
โโโโโโดโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
[MySQL 8] [Firebase FCM]
โ
[์ฌ์ฉ์ ๋๋ฐ์ด์ค ํธ์]
[GitHub Actions]
โโโ Docker Image Build
โโโ GHCR (Container Registry)
โโโ Self-hosted Runner (NAS)
โโโ Docker Compose ๋ฐฐํฌ
src/main/java/com/hatoo/
โโโ domain/
โ โโโ auth/ # ํ์๊ฐ์
, ๋ก๊ทธ์ธ, JWT ์ธ์ฆ
โ โโโ user/ # ์ ์ ์ ๋ณด, ์๋ฆผ ์์ ๋์
โ โโโ groups/ # ๊ทธ๋ฃน ์์ฑ/์ฐธ์ฌ/๊ด๋ฆฌ
โ โโโ groupMember/ # ๊ทธ๋ฃน ๋ฉค๋ฒ
โ โโโ task/ # ํ ์ผ CRUD, ๋ฐ๋ณต ์ค์ผ์ค๋ฌ, ์ฃผ๊ฐ ํต๊ณ
โ โโโ alarm/ # FCM ์๋น์ค, ์๋ฆผ ์ค์ผ์ค๋ฌ, ์๋ฆผ ๋ด์ญ
โ โโโ alarmUserAgree/ # ์ ์ฒด/๊ฐ์ธ ์๋ฆผ ์์ ์ค์
โ โโโ groupAlarmSetting/ # ๊ทธ๋ฃน๋ณ ์๋ฆผ ์ค์
โ โโโ oAuth/ # ์นด์นด์ค/๋ค์ด๋ฒ ์์
๋ก๊ทธ์ธ
โ โโโ token/ # Refresh Token ๊ด๋ฆฌ
โ โโโ weeklyStats/ # ์ฃผ๊ฐ ๊ธฐ์ฌ๋ ์ค๋
์ท
โโโ common/
โ โโโ BaseEntity.java # createdAt, updatedAt ๊ณตํต
โ โโโ exception/ # ์ ์ญ ์์ธ ์ฒ๋ฆฌ
โ โโโ util/ # JWT ์ ํธ
โโโ config/
โโโ SecurityConfig.java # Spring Security, CORS
โโโ FirebaseConfig.java # FCM ์ด๊ธฐํ
์ฃผ์ ํ ์ด๋ธ:
| ํ ์ด๋ธ | ์ค๋ช |
|---|---|
users |
์ ์ ์ ๋ณด, FCM ํ ํฐ, ์์ ๋ก๊ทธ์ธ ID |
groups |
๊ทธ๋ฃน ์ ๋ณด, ์ด๋์ฝ๋ |
group_members |
์ ์ -๊ทธ๋ฃน N:M ๋งคํ |
tasks |
ํ ์ผ ์ ๋ณด, ๋ฐ๋ณต ์ฃผ๊ธฐ, ์๋ฆผ ๋ฐ์ก ์ฌ๋ถ |
task_assignees |
ํ ์ผ-์ ์ N:M ๋งคํ |
group_tasks |
ํ ์ผ-๊ทธ๋ฃน N:M ๋งคํ |
notification_history |
์๋ฆผ ์์ ๋ด์ญ |
alarm_user_agree |
์ ์ฒด/๊ฐ์ธ ์๋ฆผ ์์ ์ค์ |
group_alarm_settings |
๊ทธ๋ฃน๋ณ ์๋ฆผ ์ธ๋ถ ์ค์ |
weekly_stats |
์ฃผ๊ฐ ๊ธฐ์ฌ๋ ์ค๋ ์ท |
refresh_token |
JWT ๋ฆฌํ๋ ์ ํ ํฐ |
email_verification |
์ด๋ฉ์ผ ์ธ์ฆ ์ฝ๋ |
์ฝ๋ Push (main ๋ธ๋์น)
โโโ GitHub Actions ํธ๋ฆฌ๊ฑฐ
โโโ Docker Image ๋น๋
โโโ GHCR(GitHub Container Registry)์ Push
โโโ Self-hosted Runner (NAS ์๋ฒ)์์ Pull
โโโ Docker Compose๋ก ๋ฌด์ค๋จ ์ฌ๋ฐฐํฌ
๊ทธ๋ฃน ์ ์ฒด์๊ฒ ์๋ฆผ์ ๋ฐ์กํ ๋ ๋ฐฐ์ ๋ฐ์ ์ฌ๋์ด "์ ํ ์ผ ๋ฑ๋ก" + "๋์๊ฒ ๋ฐฐ์ " ๋ ๊ฐ์ ์๋ฆผ์ ๋์์ ๋ฐ๋ ์ค๋ณต ๋ฌธ์ ๊ฐ ์์์ต๋๋ค. sendTaskCreated ๋ฉ์๋์ assigneeId๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ถ๊ฐํ์ฌ ๋ฐฐ์ ๋ฐ์ ์ฌ๋์ ๊ทธ๋ฃน ์ ์ฒด ์๋ฆผ์์ ์คํธ๋ฆผ ํํฐ๋ก ์ ์ธํ๊ณ , ๊ฐ์ธ ๋ฐฐ์ ์๋ฆผ๋ง ์์ ํ๋๋ก ๋ถ๋ฆฌํ์ต๋๋ค.
ํ๋ก ํธ์๋๊ฐ "2026-04-28 13:55:01.884884" (๋ง์ดํฌ๋ก์ด ํฌํจ) ๋๋ "2026-04-27T01:56:04.689Z" (ISO 8601 UTC) ํ์์ผ๋ก ๋ ์ง๋ฅผ ์ ์กํ๋๋ฐ, ๋จ์ผ ํจํด ํ์ฑ์ผ๋ก๋ ์ฒ๋ฆฌ๊ฐ ๋ถ๊ฐ๋ฅํ์ต๋๋ค. Instant.parse() โ KST ๋ณํ, ๋ค์ค ํฌ๋งท ์์ฐจ ์๋, ๋ ์ง๋ง ์๋ ๊ฒฝ์ฐ ์์ ์ฒ๋ฆฌ๋ก ํด๋ฐฑํ๋ ๋ฐฉ์ด ๋ก์ง์ ๊ตฌํํ์ต๋๋ค.
๊ทธ๋ฃน ๋ฉค๋ฒ ๋ชฉ๋ก ์กฐํ ํ forEach์์ gm.getUser()๋ฅผ ์ ๊ทผํ๋ ๊ณผ์ ์์ @ManyToOne(fetch = LAZY) ๊ด๊ณ๋ก ์ธํด N+1 ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ์ต๋๋ค. JOIN FETCH ์ฟผ๋ฆฌ ๋๋ @EntityGraph๋ฅผ ํ์ฉํ ์ฆ์ ๋ก๋ฉ์ผ๋ก ๊ฐ์ ์ด ํ์ํ ์ง์ ์ผ๋ก ํ์
ํ๊ณ ์์ต๋๋ค.
์ค์๊ฐ ์กฐํ๋ DB ๋ถํ๊ฐ ํฌ๊ณ ๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ฅผ ๋ณด์กดํ ์ ์๋ค๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค. ๋งค์ฃผ ์์์ผ ์ค์ 8์์ ์ง๋ ์ฃผ ํต๊ณ๋ฅผ ์ค๋ ์ท์ผ๋ก ์ ์ฅํ๋ ๋ฐฉ์์ ์ฑํํ์ต๋๋ค. ๊ธฐ์กด ๊ทธ๋ฃน ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ ์ฌ์ฝ์ (delete-first)ํ์ฌ ์ค์ผ์ค๋ฌ ์ฌ์คํ ์ ์ค๋ณต ์ ์ฅ ์์ด ํญ์ ์ต์ ์ํ๋ฅผ ์ ์งํ๋๋ก ํ์ต๋๋ค.
- Java 17
- MySQL 8
- Firebase ์๋น์ค ๊ณ์ JSON
CREATE DATABASE hatoo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;ํ๋ก์ ํธ ๋ฃจํธ์ .env ํ์ผ ์์ฑ:
DB_URL=jdbc:mysql://localhost:3306/hatoo
DB_USERNAME=root
DB_PASSWORD=๋น๋ฐ๋ฒํธ
JWT_SECRET_KEY=์ํฌ๋ฆฟํค
FIREBASE_PATH=/๊ฒฝ๋ก/service-account.json./gradlew bootRunhttp://localhost:8080/swagger-ui.html
| Method | URL | ์ค๋ช |
|---|---|---|
| POST | /auth/sign |
ํ์๊ฐ์ |
| POST | /auth/login |
๋ก๊ทธ์ธ |
| POST | /auth/reissue |
ํ ํฐ ์ฌ๋ฐ๊ธ |
| GET | /groups |
๋ด ๊ทธ๋ฃน ๋ชฉ๋ก ์กฐํ |
| POST | /groups |
๊ทธ๋ฃน ์์ฑ |
| POST | /tasks |
ํ ์ผ ๋ฑ๋ก |
| GET | /tasks/group/{groupId} |
๊ทธ๋ฃน ํ ์ผ ๋ชฉ๋ก ์กฐํ |
| PATCH | /tasks/{taskId}/task-status |
ํ ์ผ ์๋ฃ ์ฒ๋ฆฌ |
| GET | /tasks/group/{groupId}/ranking |
์ค์๊ฐ ๋ญํน ์กฐํ |
| GET | /tasks/group/{groupId}/weekly-stats |
์ฃผ๊ฐ ๊ธฐ์ฌ๋ ํต๊ณ |
| GET | /users/notifications |
์๋ฆผ ๋ชฉ๋ก ์กฐํ |
| PATCH | /users/notifications/read-all |
์๋ฆผ ์ ์ฒด ์ฝ์ ์ฒ๋ฆฌ |
์ ์ฒด API ๋ชฉ๋ก์ Swagger UI์์ ํ์ธ ๊ฐ๋ฅํฉ๋๋ค.
