λ°°λ¬μ± νμ¬ ν¬νΈν΄λ¦¬μ€μ© MVP νλ‘μ νΈμ λλ€. λΌμ΄λμ ν¨μ¨μ μΈ λ°°μ‘κ³Ό κ΄μ μΌν°μ λ°°μ‘ κ΄λ¦¬λ₯Ό μ§μνλ λ°±μλ APIλ₯Ό μ 곡ν©λλ€.
-
JWT κΈ°λ° μΈμ¦ μμ€ν π
- νμκ°μ λ° λ‘κ·ΈμΈ
- JWT ν ν° κΈ°λ° μΈμ¦
- μν κΈ°λ° μ κ·Ό μ μ΄ (ADMIN, BACKOFFICE, RIDER, CUSTOMER)
- BCrypt λΉλ°λ²νΈ μνΈν
- Stateless μΈμ κ΄λ¦¬
-
λΌμ΄λ λͺ¨λ°μΌ μ± API π±
- λΌμ΄λ λ±λ‘ λ° κ΄λ¦¬
- μ€μκ° μμΉ μΆμ
- λ°°μ‘ νν© κ΄λ¦¬
- 근무 μκ° κ΄λ¦¬ (μΆ/ν΄κ·Ό)
- κ°μΈ λμ보λ
-
λ°°μ‘ κ΄λ¦¬ API π¦
- λ°°μ‘ μμ± λ° μλ λ°°μ
- μ€μκ° λ°°μ‘ μΆμ
- λ°°μ‘ μν μ λ°μ΄νΈ
- λ°°μ‘ μ΄λ ₯ κ΄λ¦¬
- λ°°μ‘ νκ° μμ€ν
-
μ€μκ° ν΅μ (WebSocket) π‘
- STOMP νλ‘ν μ½ κΈ°λ° μ€μκ° λ©μμ§
- λΌμ΄λ μμΉ μ€μκ° λΈλ‘λμΊμ€νΈ
- λ°°μ‘ μν μ€μκ° μ λ°μ΄νΈ
- 1:1 κ°μΈ λ©μμ§
- μ±ν λ° κΈ΄κΈ μλ¦Ό
-
λ°μ΄ν° λΆμ λ° μ΅μ ν π
- λ°°μ‘λ§ ν¨μ¨μ± λΆμ
- λΌμ΄λ μ±κ³Ό λΆμ
- μμ μμΈ‘
- κ²½λ‘ μ΅μ ν
- μ€μκ° λͺ¨λν°λ§
- Backend: Spring Boot 3.5.7
- Database: H2 (κ°λ°), PostgreSQL (νλ‘λμ )
- Security: Spring Security 6.x + JWT (jjwt 0.11.5)
- Real-time: WebSocket + STOMP + SockJS
- Documentation: SpringDoc OpenAPI 3.0 (Swagger UI)
- Monitoring: Spring Boot Actuator + Prometheus
- Build Tool: Gradle 8.x
- Java Version: 17
- Others: Lombok, MapStruct, Validation
quick-deliver/
βββ src/main/java/com/delivery/quickdeliver/
β βββ config/ # μ€μ ν΄λμ€
β β βββ DataLoader.java
β β βββ SecurityConfig.java
β β βββ SwaggerConfig.java
β β βββ WebConfig.java
β β βββ WebSocketConfig.java
β β βββ WebSocketEventListener.java
β βββ controller/ # REST API 컨νΈλ‘€λ¬
β β βββ AuthController.java
β β βββ RiderController.java
β β βββ DeliveryController.java
β β βββ WebSocketController.java
β βββ domain/
β β βββ entity/ # JPA μν°ν°
β β β βββ User.java
β β β βββ Rider.java
β β β βββ Delivery.java
β β β βββ DeliveryHistory.java
β β β βββ RiderPerformance.java
β β β βββ Merchant.java
β β β βββ Address.java
β β βββ enums/ # Enum νμ
β β βββ UserRole.java
β β βββ DeliveryStatus.java
β β βββ RiderStatus.java
β β βββ Priority.java
β β βββ VehicleType.java
β βββ dto/
β β βββ request/ # μμ² DTO
β β β βββ LoginRequest.java
β β β βββ SignupRequest.java
β β β βββ RiderRegisterRequest.java
β β β βββ DeliveryCreateRequest.java
β β β βββ DeliveryStatusUpdateRequest.java
β β βββ response/ # μλ΅ DTO
β β βββ JwtResponse.java
β β βββ RiderResponse.java
β βββ exception/ # μμΈ μ²λ¦¬
β β βββ GlobalExceptionHandler.java
β β βββ ResourceNotFoundException.java
β β βββ DuplicateResourceException.java
β β βββ InvalidRequestException.java
β βββ repository/ # JPA Repository
β β βββ UserRepository.java
β β βββ RiderRepository.java
β β βββ DeliveryRepository.java
β βββ security/ # 보μ κ΄λ ¨
β β βββ JwtTokenProvider.java
β β βββ JwtAuthenticationFilter.java
β β βββ CustomUserDetailsService.java
β β βββ JwtAuthenticationEntryPoint.java
β βββ service/ # λΉμ¦λμ€ λ‘μ§
β βββ RiderService.java
β βββ DeliveryService.java
β βββ AnalyticsService.java
β βββ BackOfficeService.java
β βββ RouteOptimizationService.java
β βββ NotificationService.java
β βββ WebSocketService.java
βββ src/main/resources/
βββ application.yml
βββ static/
βββ websocket-test.html
- Java 17 μ΄μ
- Gradle 8.x
- νλ‘μ νΈ ν΄λ‘
git clone [repository-url]
cd quick-deliver- μ ν리μΌμ΄μ μ€ν
./gradlew bootRun- H2 μ½μ μ μ (κ°λ°μ©)
URL: http://localhost:8080/h2-console
JDBC URL: jdbc:h2:mem:testdb
Username: sa
Password: (λΉ κ°)
- Swagger UI μ μ
http://localhost:8080/swagger-ui.html
- WebSocket ν μ€νΈ νμ΄μ§
http://localhost:8080/websocket-test.html
μ ν리μΌμ΄μ μμ μ μλμΌλ‘ μμ±λλ ν μ€νΈ κ³μ :
| Username | Password | Role | μ€λͺ |
|---|---|---|---|
admin |
admin123 |
ADMIN | μμ€ν κ΄λ¦¬μ |
backoffice |
backoffice123 |
BACKOFFICE | κ΄μ λ΄λΉμ |
rider1 |
rider123 |
RIDER | λΌμ΄λ κΉλ°°λ¬ |
rider2 |
rider123 |
RIDER | λΌμ΄λ μ΄ν΅ |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/auth/signup |
νμκ°μ | Public |
| POST | /api/auth/login |
λ‘κ·ΈμΈ (JWT λ°κΈ) | Public |
| GET | /api/auth/me |
λ΄ μ 보 μ‘°ν | Required |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/riders/register |
λΌμ΄λ λ±λ‘ | ADMIN, BACKOFFICE |
| POST | /api/riders/{riderId}/shift/start |
근무 μμ | RIDER |
| POST | /api/riders/{riderId}/shift/end |
근무 μ’ λ£ | RIDER |
| PUT | /api/riders/{riderId}/location |
μμΉ μ λ°μ΄νΈ | RIDER |
| PUT | /api/riders/{riderId}/status |
μν λ³κ²½ | RIDER |
| GET | /api/riders/{riderId}/dashboard |
λμ보λ μ‘°ν | RIDER |
| GET | /api/riders/{riderId}/deliveries |
λ΄ λ°°μ‘ λͺ©λ‘ | RIDER |
| GET | /api/riders/{riderId}/deliveries/active |
νμ± λ°°μ‘ μ‘°ν | RIDER |
| GET | /api/riders/available |
κ°μ© λΌμ΄λ λͺ©λ‘ | BACKOFFICE |
| GET | /api/riders/active |
κ·Όλ¬΄μ€ λΌμ΄λ λͺ©λ‘ | BACKOFFICE |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/deliveries |
λ°°μ‘ μμ± | Authenticated |
| PUT | /api/deliveries/{deliveryId}/status |
μν μ λ°μ΄νΈ | RIDER |
| GET | /api/deliveries/{deliveryId}/track |
λ°°μ‘ μΆμ | Authenticated |
| GET | /api/deliveries/pending |
λκΈ° λ°°μ‘ λͺ©λ‘ | BACKOFFICE |
| POST | /api/deliveries/{deliveryId}/cancel |
λ°°μ‘ μ·¨μ | BACKOFFICE |
| POST | /api/deliveries/{deliveryId}/rating |
λ°°μ‘ νκ° | Authenticated |
μ°κ²° μλν¬μΈνΈ
ws://localhost:8080/ws
ꡬλ (Subscribe) Topics
/topic/monitoring/riders- λΌμ΄λ μμΉ μ€μκ° μ λ°μ΄νΈ/topic/monitoring/deliveries- λ°°μ‘ μν μ€μκ° μ λ°μ΄νΈ/user/{riderId}/queue/connection- κ°μΈ μ°κ²° νμΈ/user/{riderId}/queue/urgent- κΈ΄κΈ μλ¦Ό/user/{riderId}/queue/chat- μ±ν λ©μμ§
λ°ν (Publish) Destinations
/app/rider/location- λΌμ΄λ μμΉ μ μ‘/app/delivery/status- λ°°μ‘ μν μ μ‘/app/rider/connect- λΌμ΄λ μ°κ²° λ±λ‘/app/admin/urgent- κΈ΄κΈ λ©μμ§ μ μ‘/app/chat/send- μ±ν λ©μμ§ μ μ‘
보μ κΈ°λ₯
- JWT ν ν° κΈ°λ° Stateless μΈμ¦
- BCryptλ₯Ό μ¬μ©ν λΉλ°λ²νΈ μνΈν
- ν ν° λ§λ£ μκ° κ΄λ¦¬ (24μκ°)
- μν κΈ°λ° μ κ·Ό μ μ΄ (RBAC)
μ¬μ© μμ
# 1. λ‘κ·ΈμΈνμ¬ JWT ν ν° λ°κΈ
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
# 2. λ°κΈλ°μ ν ν°μΌλ‘ API νΈμΆ
curl -X GET http://localhost:8080/api/riders/available \
-H "Authorization: Bearer {token}"κΆν ꡬ쑰
- ADMIN: λͺ¨λ API μ κ·Ό κ°λ₯
- BACKOFFICE: κ΄μ λ° λ°°μ‘ κ΄λ¦¬ API μ κ·Ό
- RIDER: λΌμ΄λ κ΄λ ¨ API μ κ·Ό
- CUSTOMER: κ³ κ° κ΄λ ¨ API μ κ·Ό (ν₯ν ꡬν)
STOMP over WebSocket
- SockJS Fallback μ§μμΌλ‘ λΈλΌμ°μ νΈνμ± ν보
- μ€μκ° μλ°©ν₯ ν΅μ
- ν ν½ κΈ°λ° λΈλ‘λμΊμ€ν
- κ°μΈ λ©μμ§ ν
μ€μκ° κΈ°λ₯
- λΌμ΄λ μμΉ μλ λΈλ‘λμΊμ€νΈ
- λ°°μ‘ μν λ³κ²½ μ¦μ μλ¦Ό
- κ΄μ μΌν° <-> λΌμ΄λ μ€μκ° μ±ν
- κΈ΄κΈ μλ¦Ό νΈμ
WebSocket μ°κ²° μμ
const socket = new SockJS('http://localhost:8080/ws');
const stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
// λΌμ΄λ μμΉ κ΅¬λ
stompClient.subscribe('/topic/monitoring/riders', function(message) {
const location = JSON.parse(message.body);
console.log('λΌμ΄λ μμΉ:', location);
});
// μμΉ μ 보 μ μ‘
stompClient.send("/app/rider/location", {}, JSON.stringify({
riderId: 'RDR-TEST001',
latitude: 37.5665,
longitude: 126.9780
}));
});λ°°μ‘ μμ± μ λ€μ μμλ₯Ό κ³ λ €νμ¬ μ΅μ μ λΌμ΄λλ₯Ό μλ λ°°μ ν©λλ€:
- λΌμ΄λμ ν½μ μμΉ κ° κ±°λ¦¬
- λΌμ΄λμ νμ¬ μν (AVAILABLE)
- μ°¨λ νμ λ³ μ΅λ μ μ¬ λ¬΄κ²
- λΌμ΄λμ νκ· νμ
- λΌμ΄λμ μμΉλ₯Ό μ€μκ°μΌλ‘ μ λ°μ΄νΈ
- WebSocketμ ν΅ν΄ κ΄μ μΌν°μ λΈλ‘λμΊμ€νΈ
- λ°°μ‘ μ΄λ ₯μ μμΉ μ 보 μλ κΈ°λ‘
λ°°μ‘ μν νλ¦:
PENDING β ASSIGNED β PICKING_UP β IN_TRANSIT β DELIVERED
β CANCELLED
- λΌμ΄λλ³ μΌμΌ/μ£Όκ°/μκ° μ±κ³Ό λΆμ
- λ°°μ‘ ν¨μ¨μ± μ§ν κ³μ°
- μκ°λλ³ μμ ν¨ν΄ λΆμ
- μμΈ‘ λΆμ λ° μ΅μ ν μ μ
- User: μμ€ν μ¬μ©μ (μΈμ¦, μν κ΄λ¦¬)
- Rider: λΌμ΄λ μ 보, μμΉ, μν, μ±κ³Ό μ§ν
- Delivery: λ°°μ‘ μ 보, μ£Όμ, μν, κ²½λ‘
- DeliveryHistory: λ°°μ‘ μν λ³κ²½ μ΄λ ₯
- RiderPerformance: λΌμ΄λ μΌμΌ μ±κ³Ό λ°μ΄ν°
- Merchant: μμ μ 보
μ ν리μΌμ΄μ μμ μ μλμΌλ‘ μμ±λλ λ°μ΄ν°:
- 4λͺ μ ν μ€νΈ μ¬μ©μ (admin, backoffice, rider1, rider2)
- 3λͺ μ ν μ€νΈ λΌμ΄λ
- 2건μ ν μ€νΈ λ°°μ‘
λΈλΌμ°μ μμ http://localhost:8080/websocket-test.html μ μνμ¬ μ€μκ° ν΅μ ν
μ€νΈ κ°λ₯
μμΈν API μ¬μ© μμ λ λ€μ λ¬Έμ μ°Έμ‘°:
API_EXAMPLES.md- μΌλ° API μμ AUTH_API_EXAMPLES.md- μΈμ¦ API μμ
Spring Boot Actuatorλ₯Ό ν΅ν ν¬μ€ μ²΄ν¬ λ° λ©νΈλ¦:
http://localhost:8080/actuator/health
http://localhost:8080/actuator/metrics
http://localhost:8080/actuator/prometheus
- β JWT ν ν° κΈ°λ° Stateless μΈμ¦
- β BCrypt λΉλ°λ²νΈ μνΈν (κ°λ 10)
- β μν κΈ°λ° μ κ·Ό μ μ΄ (RBAC)
- β CORS μ€μ (κ°λ° νκ²½: λͺ¨λ μΆμ² νμ©)
- β μΈμ¦ μ€ν¨ μμΈ μ²λ¦¬
- β Spring Security 6.x μ΅μ 보μ μ€μ
| μλν¬μΈνΈ | κΆν |
|---|---|
/api/auth/** |
Public |
/api/admin/** |
ADMIN |
/api/backoffice/** |
ADMIN, BACKOFFICE |
/api/riders/** |
ADMIN, BACKOFFICE, RIDER |
/api/deliveries/** |
Authenticated |
/ws/** |
Public |
/swagger-ui/** |
Public |
/h2-console/** |
Public (κ°λ° νκ²½) |
jwt:
secret: [Application Secret Key]
expiration: 86400000 # 24μκ° (λ°λ¦¬μ΄)- νλ‘μ νΈ μ΄κΈ° μ€μ
- λλ©μΈ μν°ν° μ€κ³
- Repository κ³μΈ΅ ꡬν
- κΈ°λ³Έ API μλν¬μΈνΈ
- λΌμ΄λ κ΄λ¦¬ API
- λ°°μ‘ κ΄λ¦¬ API
- μλ λ°°μ λ‘μ§
- μ€μκ° μμΉ μΆμ
- μ±κ³Ό λΆμ κΈ°λ₯
- JWT μΈμ¦ μμ€ν
- μν κΈ°λ° μ κ·Ό μ μ΄
- WebSocket μ€μκ° ν΅μ
- STOMP νλ‘ν μ½ κ΅¬ν
- μ€μκ° μμΉ/μν λΈλ‘λμΊμ€ν
- μ 체 λμ보λ API
- λΌμ΄λ κ΄λ¦¬ API
- λ°°μ‘ κ΄λ¦¬ API
- ν΅κ³ λ° λ¦¬ν¬νΈ API
- μ€μκ° λΆμ λμ보λ
- μμΈ‘ λΆμ μκ³ λ¦¬μ¦
- κ²½λ‘ μ΅μ ν κ°μ
- μ±κ³Ό 리ν¬νΈ μλν
- λ¨μ ν μ€νΈ μμ±
- ν΅ν© ν μ€νΈ μμ±
- E2E ν μ€νΈ
- PostgreSQL νλ‘λμ μ€μ
- Docker 컨ν μ΄λν
- CI/CD νμ΄νλΌμΈ ꡬμΆ
μμΈν API λ¬Έμλ μ ν리μΌμ΄μ μ€ν ν Swagger UIμμ νμΈν μ μμ΅λλ€:
http://localhost:8080/swagger-ui.html
PHASE3_COMPLETE_SUMMARY.md- Phase 3 ꡬν μλ£ μμ½PHASE3_SECURITY_WEBSOCKET.md- Security & WebSocket μμΈ κ°μ΄λAPI_EXAMPLES.md- REST API μ¬μ© μμ AUTH_API_EXAMPLES.md- μΈμ¦ API μ¬μ© μμ IMPLEMENTATION_SUMMARY.md- μ 체 ꡬν μμ½
ββββββββββββββββββββ
β Client App β
β (Web/Mobile) β
ββββββββββ¬ββββββββββ
β
β HTTP REST API
β + JWT Token
β WebSocket/STOMP
β
ββββββββββΌββββββββββββββββββββββ
β Spring Security Filter β
β - JwtAuthenticationFilter β
β - CORS Filter β
ββββββββββ¬ββββββββββββββββββββββ
β
ββββββββββΌββββββββββββββββββββββ
β Controllers β
β - AuthController β
β - RiderController β
β - DeliveryController β
β - WebSocketController β
ββββββββββ¬ββββββββββββββββββββββ
β
ββββββββββΌββββββββββββββββββββββ
β WebSocket Message Broker β
β - STOMP Protocol β
β - SockJS Fallback β
ββββββββββ¬ββββββββββββββββββββββ
β
ββββββββββΌββββββββββββββββββββββ
β Services β
β - RiderService β
β - DeliveryService β
β - WebSocketService β
β - AnalyticsService β
ββββββββββ¬ββββββββββββββββββββββ
β
ββββββββββΌββββββββββββββββββββββ
β Repositories (JPA) β
β - UserRepository β
β - RiderRepository β
β - DeliveryRepository β
ββββββββββ¬ββββββββββββββββββββββ
β
ββββββββββΌββββββββββββββββββββββ
β Database (H2) β
β - users β
β - riders β
β - deliveries β
ββββββββββββββββββββββββββββββββ
μ΄ νλ‘μ νΈλ₯Ό ν΅ν΄ λ€μ κΈ°μ λ€μ νμ΅νκ³ μ μ©νμ΅λλ€:
- Spring Boot 3.x μ΅μ κΈ°λ₯ νμ©
- Spring Security 6.x 보μ μ€μ
- JWT κΈ°λ° Stateless μΈμ¦ ꡬν
- WebSocket + STOMP μ€μκ° ν΅μ
- RESTful API μ€κ³ λ° κ΅¬ν
- JPA/Hibernateλ₯Ό νμ©ν λ°μ΄ν°λ² μ΄μ€ μ€κ³
- κ³μΈ΅ν μν€ν μ² (Controller-Service-Repository)
- DTO ν¨ν΄μ νμ©ν κ³μΈ΅ λΆλ¦¬
- μ μ μμΈ μ²λ¦¬ (Global Exception Handler)
- μν κΈ°λ° μ κ·Ό μ μ΄ (RBAC)
- μ€μκ° μ΄λ²€νΈ κΈ°λ° μν€ν μ²
- OpenAPI 3.0 (Swagger) λ¬Έμν
- μμΈν κΈ°μ λ¬Έμ μμ±
- ν μ€νΈ κ°λ₯ν ꡬ쑰 μ€κ³
μ΄ νλ‘μ νΈλ ν¬νΈν΄λ¦¬μ€ λͺ©μ μΌλ‘ μ μλμμ΅λλ€.
κ°μ μ¬νμ΄λ λ²κ·Έλ₯Ό λ°κ²¬νμλ©΄ μ΄μλ₯Ό λ±λ‘ν΄ μ£ΌμΈμ.
μ΄ νλ‘μ νΈλ Apache License 2.0 λΌμ΄μ μ€ νμ λ°°ν¬λ©λλ€.
κ°λ°μ: readra
μ΄λ©μΌ: yong9976@naver.com
κ°λ° κΈ°κ°: 2025.11
νλ‘μ νΈ μν: Phase 3 μλ£ (Security & WebSocket)
- Backend: Spring Boot 3.5.7, Spring Security 6.x, JWT
- Database: H2
- Real-time: WebSocket, STOMP, SockJS
- Monitoring: Actuator, Prometheus
- Documentation: Swagger/OpenAPI 3.0
- β JWT κΈ°λ° μΈμ¦ μμ€ν ꡬν
- β μν κΈ°λ° μ κ·Ό μ μ΄ (RBAC) ꡬν
- β WebSocket μ€μκ° μλ°©ν₯ ν΅μ ꡬν
- β μλ λΌμ΄λ λ°°μ μκ³ λ¦¬μ¦ κ΅¬ν
- β RESTful API μ€κ³ λ° 19κ° μλν¬μΈνΈ ꡬν
- β μμΈν κΈ°μ λ¬Έμ μμ±