π£ ν΄λ£¨νΈ (Haeruhand) - μμ ν ν΄λ£¨μ§ μλΉμ€
곡ν΅νλ‘μ νΈ μ§νμν© μμ½
ν΅μ¬ κΈ°λ₯ μ κ°λ°μ΄ νλ‘ νΈ/λ°±μλ κ°μ μλ£λμμΌλ©°, μ°λμμ
μ μ§νμ€μ
λλ€.
μ΄λ²μ£Ό μ£Όλ§κΉμ§ ν΅μ¬ κΈ°λ₯μ MVP κ°λ°μ μλ£νκ³ ν
μ€νΈλ₯Ό μ§νν μμ μ
λλ€.
ν΄λ£¨νΈ(Haeruhand)λ ν΄λ£¨μ§μ νκ³ μΆμ΄νλ μ΄λ³΄μλ₯Ό μν μ’
ν© μ€λ§νΈ μλΉμ€ νλ«νΌμ
λλ€. μ€μκ° μμΉ κ³΅μ , AI κΈ°λ° μ΄μ’
νλ³, κΈμ΄κΈ° μ 보 μ 곡, ν΄μ κΈ°μ μ 보 λ± ν΄λ£¨μ§μ νμν λͺ¨λ μ 보λ₯Ό ν κ³³μμ μ 곡ν©λλ€.
μμ ν ν΄λ£¨μ§ νλ μ§μμ μν μ€μκ° μμΉ κ³΅μ μμ€ν
AI κΈ°μ μ νμ©ν μ¦κ°μ μΈ μ΄μ’
νλ³ λ° κΈμ΄κΈ° μ 보 μ 곡
ν΄μ κΈ°μ νΉλ³΄ μλ¦Όμ ν΅ν μ¬κ³ μλ°©
ν΄λ£¨μ§ κ΄λ ¨ λ²κ· μ€μλ₯Ό μν μ 보 μ 곡
1. πΊοΈ μ€μκ° μμΉ κ³΅μ μμ€ν
WebSocket/STOMP κΈ°λ° μ€μκ° ν΅μ
μ΅λ 4λͺ
κΉμ§ λμ μμΉ κ³΅μ
νΈμ€νΈ κΈ°λ° λ°© κ΄λ¦¬ μμ€ν
λ₯λ§ν¬λ₯Ό ν΅ν κ°νΈν λ°© μ°Έκ°
λ©€λ²λ³ μμ κ΅¬λΆ μμ€ν
λμμ 보기
2. π€ AI μ΄μ’
νλ³ μμ€ν
FastAPI AI μλ² μ°λ
μ΄λ―Έμ§ μ
λ‘λ β μ΄μ’
μλ νλ³
νλ³λ μ΄μ’
μ κΈμ΄κΈ° μ 보 μ¦μ μ 곡
3. π« κΈμ΄κΈ° μ 보 κ΄λ¦¬
μ΄μ’
λ³ ν¬ν κΈμ§ κΈ°κ° DB
μ§μλ³(κΆμλ³) μ°¨λ³νλ κΈμ΄κΈ° μ μ©
μ΅μ 체μ₯/μ²΄μ€ μ ν μ 보 μ 곡
μ€μκ° κΈμ΄κΈ° μλ° μ¬λΆ νλ³
4. π± νΈμ μλ¦Ό μμ€ν
Firebase Cloud Messaging (FCM) κΈ°λ°
λλ£ μ΄ν μλ¦Ό
ν΄μ κΈ°μ νΉλ³΄ μλ¦Ό
λ€μ€ λλ°μ΄μ€ μ§μ
5. π ν΄μ μ 보 μ 곡
μ‘°μ μ 보 (λ§μ‘°/κ°μ‘° μκ°)
κΈ°μ νΉλ³΄ (κ°ν, νλ λ±)
μ΄μ₯ μ 보 (μμΉ, νΈμμμ€)
6. π 보μ μΈμ¦ μμ€ν
Kakao OAuth 2.0 μμ
λ‘κ·ΈμΈ
JWT κΈ°λ° μΈμ¦ (Access Token + Refresh Token)
Redis κΈ°λ° ν ν° κ΄λ¦¬
ποΈ μμ€ν
μν€ν
μ²
λ°±μλ μν€ν
μ² - λλ©μΈ μ€μ¬ μ€κ³ (DDD)
backend/spring-business/haeruhand/
βββ src/main/java/com/ssafy/haeruhand/
βββ domain/
β βββ user/ # μ¬μ©μ κ΄λ¦¬
β βββ location/ # μ€μκ° μμΉ κ³΅μ
β βββ fish/ # μ΄μ’
λ° κΈμ΄κΈ°
β βββ ai/ # AI μ΄μ’
νλ³
β βββ notification/ # νΈμ μλ¦Ό
β βββ fishery/ # μ΄μ₯ μ 보
β βββ marine/ # ν΄μ μ 보
β βββ weather/ # λ μ¨ μ 보
βββ global/ # κ³΅ν΅ μ€μ λ° μ νΈλ¦¬ν°
βββ config/ # μ€μ ν΄λμ€
βββ jwt/ # JWT κ΄λ ¨
βββ websocket/ # WebSocket μ€μ
βββ exception/ # μμΈ μ²λ¦¬
νλ‘ νΈμλ μν€ν
μ² - Atomic Design Pattern
frontend/
βββ src/
βββ components/
β βββ atoms/ # κΈ°λ³Έ UI μμ
β β βββ Button/
β β βββ Input/
β β βββ Text/
β β βββ Icon/
β βββ molecules/ # λ³΅ν© μ»΄ν¬λνΈ
β βββ Card/
β βββ Form/
β βββ Header/
β βββ Modal/
βββ pages/ # νμ΄μ§ μ»΄ν¬λνΈ
β βββ MainPage/ # λ©μΈ νλ©΄
β βββ LoginPage/ # λ‘κ·ΈμΈ
β βββ WeatherAlertPage/ # λ μ¨ μλ¦Ό
β βββ LocationSharePage/ # μμΉ κ³΅μ
β βββ FishDetectPage/ # μ΄μ’
νλ³
β βββ FisheryMapPage/ # μ΄μ₯ μ§λ
β βββ NotificationPage/ # μλ¦Ό μ€μ
βββ routes/ # λΌμ°ν
μ€μ
βββ api/ # API ν΅μ
βββ hooks/ # 컀μ€ν
ν
βββ stores/ # μ μ μν κ΄λ¦¬
βββ types/ # TypeScript νμ
μ μ
βββ utils/ # μ νΈλ¦¬ν° ν¨μ
Spring Boot 3.5.4 - μ΅μ λ²μ μ μ©
Java 17 - LTS λ²μ
Spring Data JPA - ORM
Spring Security - 보μ
Spring WebSocket - μ€μκ° ν΅μ
MySQL - μ£Ό λ°μ΄ν°λ² μ΄μ€
Redis - μΊμ± λ° μΈμ
κ΄λ¦¬
Firebase Admin SDK - FCM νΈμ μλ¦Ό
Google Cloud Storage - μ΄λ―Έμ§ μ μ₯μ
Kakao OAuth 2.0 - μμ
λ‘κ·ΈμΈ
FastAPI - AI μλ² μ°λ
Swagger/OpenAPI 3.0 - API λ¬Έμν
Lombok - μ½λ κ°μν
JWT (io.jsonwebtoken) - ν ν° κ΄λ¦¬
React 19.1.0 - μ΅μ λ²μ UI λΌμ΄λΈλ¬λ¦¬
TypeScript 5.8.3 - νμ
μμ μ±
Vite 7.0.4 - λΉ λ₯Έ λΉλ λꡬ
Capacitor 7.4.2 - ν¬λ‘μ€ νλ«νΌ λͺ¨λ°μΌ μ±
TanStack Router - νμ
μμ λΌμ°ν
TanStack Query - μλ² μν κ΄λ¦¬
Zustand - ν΄λΌμ΄μΈνΈ μν κ΄λ¦¬
Axios - HTTP ν΄λΌμ΄μΈνΈ
CSS Modules - μ€νμΌ μΊ‘μν
React Hook Form - νΌ κ΄λ¦¬
Framer Motion - μ λλ©μ΄μ
Socket.io Client - WebSocket ν΅μ
Method
Endpoint
Description
POST
/v1/user/issue/kakao
μΉ΄μΉ΄μ€ OAuth λ‘κ·ΈμΈ
POST
/v1/user/reissue
ν ν° μ¬λ°κΈ
GET
/v1/user/userinfo
μ¬μ©μ μ 보 μ‘°ν
Method
Endpoint
Description
POST
/v1/location/rooms
μμΉ κ³΅μ λ°© μμ±
GET
/v1/location/rooms/{roomCode}
λ°© μ 보 μ‘°ν
DELETE
/v1/location/rooms/{roomCode}
λ°© κ°μ μ’
λ£
WebSocket μλν¬μΈνΈ
Endpoint
Description
/v1/ws
WebSocket μ°κ²°
/pub/location.join
λ°© μ°Έκ°
/pub/location.update
μμΉ μ
λ°μ΄νΈ
/pub/location.leave
λ°© ν΄μ₯
/sub/location.{roomCode}
λ°© ꡬλ
Method
Endpoint
Description
POST
/v1/seafood-detect
μ΄μ’
νλ³ μμ²
POST
/v1/storage/signed-urls
μ΄λ―Έμ§ μ
λ‘λ URL μμ±
Method
Endpoint
Description
POST
/v1/notifications/tokens
FCM ν ν° λ±λ‘
PUT
/v1/notifications/tokens/{tokenId}
FCM ν ν° κ°±μ
DELETE
/v1/notifications/tokens/{tokenId}
FCM ν ν° μμ
{
"accessToken" : {
"expiry" : " 1μκ°" ,
"usage" : " API μμ² μΈμ¦"
},
"refreshToken" : {
"expiry" : " 7μΌ" ,
"usage" : " Access Token κ°±μ " ,
"storage" : " Redis"
},
"joinToken" : {
"expiry" : " 30λΆ" ,
"usage" : " μμΉ κ³΅μ λ°© μ°Έκ°"
}
}
Redis κΈ°λ° Refresh Token κ΄λ¦¬
μλ² μ€ν ν Swagger UI μ μ:
http://haeruhand.o-r.kr/api/swagger-ui/index.html
π± λͺ¨λ°μΌ μ± μ§μ
ν¬λ‘μ€ νλ«νΌ μ§μ
Capacitor 7.4.2 κΈ°λ° νμ΄λΈλ¦¬λ μ±
iOS/Android λ€μ΄ν°λΈ μ± λΉλ μ§μ
Progressive Web App (PWA) λμ
λ€μ΄ν°λΈ κΈ°λ₯ μ°λ
GPS/μμΉ μλΉμ€ - μ€μκ° μμΉ μΆμ
μΉ΄λ©λΌ - μ΄μ’
촬μ λ° νλ³
νΈμ μλ¦Ό - FCM μ°λ
λ₯λ§ν¬ - μ± κ° μ°λ (seafeet://)
λ‘컬 μ€ν λ¦¬μ§ - μ€νλΌμΈ λ°μ΄ν° μ μ₯
STOMP νλ‘ν μ½ μ¬μ©μΌλ‘ λ©μμ§ λΌμ°ν
ν¨μ¨ν
λ°°μΉ μ²λ¦¬ λ₯Ό ν΅ν μμΉ λ°μ΄ν° μ μ₯ μ΅μ ν
@Async μ΄λ
Έν
μ΄μ
μ ν΅ν FCM μλ¦Ό λΉλκΈ° μ μ‘
νλ‘ νΈμλ μ΅μ ν
Code Splitting - λΌμ°νΈ κΈ°λ° μ½λ λΆν
Lazy Loading - μ»΄ν¬λνΈ μ§μ° λ‘λ©
μ΄λ―Έμ§ μ΅μ ν - WebP ν¬λ§· λ° responsive images
Virtual Scrolling - λλ λ°μ΄ν° λ λλ§ μ΅μ ν
React.memo - λΆνμν 리λ λλ§ λ°©μ§