Skip to content

EarlyExpress/hub-driver-service

Repository files navigation

Hub Driver Service

Early Express 물류 플랫폼의 허브 간 배송 담당자 관리 서비스입니다. 허브와 허브 사이의 대량 물류 운송을 담당하는 드라이버를 관리하며, 전사 단위의 자동 배정 알고리즘을 통해 효율적인 허브 간 물류 운영을 지원합니다.


📋 목차

  1. 개요
  2. 기술 스택
  3. 아키텍처
  4. 도메인 모델
  5. API 엔드포인트
  6. 서비스 연동
  7. 환경 설정
  8. 실행 방법
  9. 프로젝트 구조

개요

Hub Driver Service는 허브 간 배송(Hub-to-Hub Delivery)을 담당하는 드라이버를 관리하는 서비스입니다.

주요 기능

  • 드라이버 관리: 허브 간 배송 담당자 등록, 상태 관리, 근무 관리
  • 전사 자동 배정: 우선순위 기반 드라이버 자동 배정 (Round-Robin)
  • 배송 통계: 총 배송 건수, 평균 배송 시간 등 성과 지표 관리
  • 마스터 관리: 전사 드라이버 현황 모니터링

Hub Driver vs Last Mile Driver

구분 Hub Driver Last Mile Driver
담당 구간 허브 ↔ 허브 허브 → 최종 목적지
소속 전사 소속 (hubId 없음) 특정 허브 소속
배정 범위 전국 허브 간 이동 가능 소속 허브 내 배송만
배정 방식 전사 우선순위 기반 허브별 우선순위 기반
차량 유형 대형 화물차 소형 배송 차량

기술 스택

구분 기술
Framework Spring Boot 3.x
Language Java 21
Database PostgreSQL
Service Discovery Netflix Eureka
Security OAuth 2.0 Resource Server (Keycloak)
Build Tool Gradle

아키텍처

시스템 아키텍처

┌─────────────────────────────────────────────────────────────────────────────────────┐
│                              Hub Driver Service                                      │
├─────────────────────────────────────────────────────────────────────────────────────┤
│  Presentation Layer                                                                  │
│  ┌───────────────────────────────────┐  ┌───────────────────────────────────┐       │
│  │      MasterController             │  │      InternalController           │       │
│  │      /web/master                  │  │      /internal                    │       │
│  │                                   │  │                                   │       │
│  │  • 전체 드라이버 목록 조회         │  │  • 드라이버 생성                   │       │
│  │  • 드라이버 상세 조회              │  │  • 자동 배정                       │       │
│  │  • 상태별 필터링                   │  │  • 배송 완료/취소 통지             │       │
│  └───────────────────────────────────┘  └───────────────────────────────────┘       │
├─────────────────────────────────────────────────────────────────────────────────────┤
│  Application Layer                                                                   │
│  ┌──────────────────────────────┐  ┌──────────────────────────────┐                 │
│  │   HubDriverCommandService    │  │   HubDriverQueryService      │                 │
│  │   • create()                 │  │   • findAll()                │                 │
│  │   • assignDelivery()         │  │   • findById()               │                 │
│  │   • completeDelivery()       │  │   • findByStatus()           │                 │
│  │   • cancelDelivery()         │  │                              │                 │
│  └──────────────────────────────┘  └──────────────────────────────┘                 │
├─────────────────────────────────────────────────────────────────────────────────────┤
│  Domain Layer                                                                        │
│  ┌─────────────────────────────────────────────────────────────────────────────┐    │
│  │  HubDriver (Aggregate Root)                                                 │    │
│  │  ┌─────────────────┐  ┌─────────────────────┐                               │    │
│  │  │ HubDriverId     │  │ HubDriverStatus     │                               │    │
│  │  │ (Value Object)  │  │ (Enum)              │                               │    │
│  │  └─────────────────┘  └─────────────────────┘                               │    │
│  └─────────────────────────────────────────────────────────────────────────────┘    │
├─────────────────────────────────────────────────────────────────────────────────────┤
│  Infrastructure Layer                                                                │
│  ┌────────────────────────────┐                                                     │
│  │       PostgreSQL           │                                                     │
│  │       Repository           │                                                     │
│  └────────────────────────────┘                                                     │
└─────────────────────────────────────────────────────────────────────────────────────┘

서비스 연동 구조

┌─────────────────┐                    ┌─────────────────────────────┐
│   User Service  │                    │     Hub Driver Service      │
│                 │───── Feign ───────▶│                             │
│ (드라이버 등록)  │  POST /internal/   │  • 드라이버 생성            │
│                 │  drivers           │  • 상태: AVAILABLE          │
└─────────────────┘                    └─────────────────────────────┘
                                                     ▲
                                                     │
                                                     │ Feign
                                                     │
┌─────────────────────────────────────────────────────────────────────┐
│                      Hub Delivery Service                            │
│                                                                      │
│  허브 배송 생성 시:                                                   │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ POST /internal/drivers/assign                                │    │
│  │ → 전사 AVAILABLE 드라이버 중 우선순위가 가장 낮은 드라이버 선택│    │
│  │ → 드라이버 상태: AVAILABLE → ON_DELIVERY                     │    │
│  │ → currentDeliveryId에 hubDeliveryId 저장                     │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  허브 배송 완료 시:                                                   │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ PUT /internal/drivers/{driverId}/complete                    │    │
│  │ → 통계 업데이트 (totalDeliveries, averageDeliveryTimeMin)    │    │
│  │ → 드라이버 상태: ON_DELIVERY → AVAILABLE                     │    │
│  │ → assignmentPriority 초기화 (0)                              │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  허브 배송 취소 시:                                                   │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ PUT /internal/drivers/{driverId}/cancel                      │    │
│  │ → 배정 해제, 드라이버 상태: ON_DELIVERY → AVAILABLE          │    │
│  └─────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘

물류 흐름에서의 위치

┌─────────────────────────────────────────────────────────────────────────────────────┐
│                              Early Express 물류 흐름                                 │
└─────────────────────────────────────────────────────────────────────────────────────┘

[공급업체]                                                              [수령업체]
    │                                                                       ▲
    │ 상품 출고                                                             │ 상품 수령
    ▼                                                                       │
┌─────────┐                                                           ┌─────────┐
│ 출발    │    ┌─────────┐    ┌─────────┐    ┌─────────┐             │ 도착    │
│ 허브    │───▶│ 경유    │───▶│ 경유    │───▶│ 경유    │────────────▶│ 허브    │
│ (서울)  │    │ 허브    │    │ 허브    │    │ 허브    │             │ (부산)  │
└─────────┘    │ (대전)  │    │ (대구)  │    │ (울산)  │             └─────────┘
               └─────────┘    └─────────┘    └─────────┘                  │
                                                                          │
    ◀──────────────────────────────────────────────────────────▶          │
                        Hub Driver 담당 구간                               │
                    (허브 간 대량 물류 운송)                                │
                                                                          ▼
                                                                    [최종 목적지]
                                                                          ▲
                                                                          │
                                                      Last Mile Driver 담당│
                                                      (허브 → 최종 배송)    │

도메인 모델

HubDriver (Aggregate Root)

public class HubDriver {
    // 식별자
    private HubDriverId id;               // UUID
    
    // 기본 정보
    private String userId;                // 사용자 ID (User Service 연동)
    private String name;                  // 드라이버 이름
    // ※ hubId 없음 - 전사 소속
    
    // 상태 정보
    private HubDriverStatus status;       // 드라이버 상태
    private String currentDeliveryId;     // 현재 배송 ID (배송 중일 때)
    
    // 배정 관련
    private Integer assignmentPriority;   // 배정 우선순위 (낮을수록 우선 배정)
    private LocalDateTime availableFrom;  // 배정 가능 시점
    
    // 통계
    private Long totalDeliveries;         // 총 배송 건수
    private Long totalDeliveryTimeMin;    // 총 배송 시간 (분)
    private Long averageDeliveryTimeMin;  // 평균 배송 시간 (분)
    private LocalDateTime lastDeliveryCompletedAt;  // 마지막 배송 완료 시간
    
    // Audit
    private LocalDateTime createdAt;
    private String createdBy;
    private LocalDateTime updatedAt;
    private String updatedBy;
    private LocalDateTime deletedAt;
    private String deletedBy;
    private boolean isDeleted;
}

Value Objects

Value Object 설명 주요 필드
HubDriverId 드라이버 고유 ID UUID 기반 value

HubDriverStatus (드라이버 상태)

┌─────────────────────────────────────────────────────────────────────────────────────┐
│                           드라이버 상태 흐름도                                        │
└─────────────────────────────────────────────────────────────────────────────────────┘

[드라이버 생성]
      │
      ▼
┌─────────────┐    assignDelivery()    ┌─────────────┐
│  AVAILABLE  │───────────────────────▶│ ON_DELIVERY │
│  배정 가능   │◀───────────────────────│  배송 중     │
└─────────────┘  completeDelivery()    └─────────────┘
      │           cancelDelivery()            │
      │                                       │
      │ endWork()                             │ (배송 중에는 불가)
      ▼                                       │
┌─────────────┐                               │
│  OFF_DUTY   │                               │
│  비활성      │                               │
└─────────────┘                               │
      │                                       │
      │ deactivate()                          │
      ▼                                       │
┌─────────────┐                               │
│  INACTIVE   │◀──────────────────────────────┘
│  휴직        │   (배송 중에는 휴직 불가)
└─────────────┘
      │
      │ activate()
      ▼
┌─────────────┐
│  AVAILABLE  │
└─────────────┘
상태 설명 배정 가능 근무 중
AVAILABLE 배정 가능
ON_DELIVERY 배송 중
OFF_DUTY 비활성 (퇴근)
INACTIVE 휴직

비즈니스 규칙

  1. 배송 배정: AVAILABLE 상태에서만 배정 가능
  2. 배송 완료/취소: ON_DELIVERY 상태에서만 가능
  3. 근무 종료: ON_DELIVERY 상태에서는 근무 종료 불가
  4. 휴직 처리: ON_DELIVERY 상태에서는 휴직 불가
  5. 삭제: ON_DELIVERY 상태에서는 삭제 불가
  6. 우선순위: 배정 시 assignmentPriority 증가, 완료 시 0으로 초기화

API 엔드포인트

Internal API (내부 서비스 간 통신)

Base Path: /v1/hub-driver/internal

⚠️ 이 API는 내부 서비스 간 통신용입니다. Gateway를 통해 외부에 노출되지 않습니다.

Method Endpoint 설명 호출 서비스
POST /drivers 드라이버 생성 User Service
POST /drivers/assign 드라이버 자동 배정 HubDelivery Service
PUT /drivers/{driverId}/complete 배송 완료 통지 HubDelivery Service
PUT /drivers/{driverId}/cancel 배송 취소 통지 HubDelivery Service

드라이버 생성

User Service에서 허브 간 배송 담당자 등록 시 호출됩니다.

POST /v1/hub-driver/internal/drivers
X-User-Id: admin-001
Content-Type: application/json

Request

{
  "userId": "user-hub-driver-001",
  "name": "박허브"
}

Response (200 OK)

{
  "driverId": "hub-driver-uuid-001",
  "userId": "user-hub-driver-001",
  "name": "박허브",
  "status": "AVAILABLE"
}

💡 LastMileDriver와의 차이: hubId 필드가 없습니다. Hub Driver는 전사 소속이므로 특정 허브에 종속되지 않습니다.


드라이버 자동 배정

HubDelivery 생성 시 전사의 가용 드라이버 중 우선순위가 가장 낮은 드라이버를 자동 배정합니다.

POST /v1/hub-driver/internal/drivers/assign
Content-Type: application/json

Request

{
  "hubDeliveryId": "hub-delivery-001"
}

Response (200 OK)

{
  "driverId": "hub-driver-uuid-001",
  "userId": "user-hub-driver-001",
  "driverName": "박허브",
  "status": "ON_DELIVERY",
  "assignedAt": "2025-01-15T10:30:00"
}

배정 알고리즘:

  1. 전사 드라이버 중 AVAILABLE 상태인 드라이버 조회
  2. assignmentPriority가 가장 낮은 드라이버 선택
  3. 선택된 드라이버의 assignmentPriority 증가 (Round-Robin 효과)
  4. 드라이버 상태를 ON_DELIVERY로 변경
  5. currentDeliveryId에 hubDeliveryId 저장

💡 LastMileDriver와의 차이: hubId로 필터링하지 않고 전사 범위에서 배정합니다.


배송 완료 통지

허브 배송 완료 시 드라이버 통계를 업데이트하고 상태를 변경합니다.

PUT /v1/hub-driver/internal/drivers/{driverId}/complete
Content-Type: application/json

Request

{
  "deliveryTimeMin": 180
}

Response (200 OK)

{
  "driverId": "hub-driver-uuid-001",
  "status": "AVAILABLE",
  "message": "배송이 완료되었습니다.",
  "timestamp": 1705312200000
}

처리 내용:

  • totalDeliveries 증가
  • totalDeliveryTimeMin에 배송 시간 추가
  • averageDeliveryTimeMin 재계산
  • lastDeliveryCompletedAt 업데이트
  • currentDeliveryId 초기화 (null)
  • assignmentPriority 0으로 리셋
  • 상태를 AVAILABLE로 변경

배송 취소 통지

허브 배송 취소 시 드라이버 배정을 해제합니다.

PUT /v1/hub-driver/internal/drivers/{driverId}/cancel
Content-Type: application/json

Request (Optional)

{
  "reason": "경로 변경으로 인한 취소"
}

Response (200 OK)

{
  "driverId": "hub-driver-uuid-001",
  "status": "AVAILABLE",
  "message": "배송이 취소되었습니다.",
  "timestamp": 1705312200000
}

Master API (마스터 관리자용)

Base Path: /v1/hub-driver/web/master

Method Endpoint 설명
GET /drivers 전체 드라이버 목록 조회
GET /drivers/{driverId} 드라이버 상세 조회

전체 드라이버 목록 조회

GET /v1/hub-driver/web/master/drivers?status=AVAILABLE
X-User-Id: master-001
X-User-Roles: MASTER

Query Parameters

파라미터 타입 필수 설명
status Enum 드라이버 상태 필터
page Integer 페이지 번호 (기본값: 0)
size Integer 페이지 크기 (기본값: 20)
sort String 정렬 기준 (기본값: createdAt,DESC)

Response (200 OK)

{
  "success": true,
  "data": {
    "content": [
      {
        "driverId": "hub-driver-uuid-001",
        "userId": "user-hub-driver-001",
        "name": "박허브",
        "status": "AVAILABLE",
        "statusDescription": "배정 가능",
        "currentDeliveryId": null,
        "assignmentPriority": 0,
        "totalDeliveries": 500,
        "totalDeliveryTimeMin": 90000,
        "averageDeliveryTimeMin": 180,
        "lastDeliveryCompletedAt": "2025-01-15T14:30:00",
        "availableFrom": "2025-01-15T14:30:00",
        "createdAt": "2024-01-15T10:00:00"
      },
      {
        "driverId": "hub-driver-uuid-002",
        "userId": "user-hub-driver-002",
        "name": "김간선",
        "status": "ON_DELIVERY",
        "statusDescription": "배송 중",
        "currentDeliveryId": "hub-delivery-003",
        "assignmentPriority": 2,
        "totalDeliveries": 450,
        "totalDeliveryTimeMin": 81000,
        "averageDeliveryTimeMin": 180,
        "lastDeliveryCompletedAt": "2025-01-15T10:00:00",
        "availableFrom": "2025-01-15T09:00:00",
        "createdAt": "2024-02-01T10:00:00"
      }
    ],
    "pageInfo": {
      "page": 0,
      "size": 20,
      "totalElements": 50,
      "totalPages": 3
    }
  }
}

드라이버 상세 조회

GET /v1/hub-driver/web/master/drivers/{driverId}
X-User-Id: master-001
X-User-Roles: MASTER

Response (200 OK)

{
  "success": true,
  "data": {
    "driverId": "hub-driver-uuid-001",
    "userId": "user-hub-driver-001",
    "name": "박허브",
    "status": "AVAILABLE",
    "statusDescription": "배정 가능",
    "currentDeliveryId": null,
    "assignmentPriority": 0,
    "totalDeliveries": 500,
    "totalDeliveryTimeMin": 90000,
    "averageDeliveryTimeMin": 180,
    "lastDeliveryCompletedAt": "2025-01-15T14:30:00",
    "availableFrom": "2025-01-15T14:30:00",
    "isAvailable": true,
    "isOnDelivery": false,
    "createdAt": "2024-01-15T10:00:00",
    "createdBy": "admin-001"
  }
}

서비스 연동

연동 서비스 목록

서비스 연동 방식 역할
User Service Feign (호출받음) 드라이버 생성 요청
HubDelivery Service Feign (호출받음) 배정/완료/취소 통지

시퀀스 다이어그램

드라이버 등록 플로우

User Service                Hub Driver Service
     │                              │
     │  POST /internal/drivers      │
     │  {userId, name}              │
     │─────────────────────────────▶│
     │                              │ create HubDriver
     │                              │ status: AVAILABLE
     │                              │ assignmentPriority: 0
     │     {driverId, status}       │
     │◀─────────────────────────────│
     │                              │

허브 배송 배정 플로우

HubDelivery Service            Hub Driver Service
        │                              │
        │  POST /internal/drivers/     │
        │       assign                 │
        │  {hubDeliveryId}             │
        │─────────────────────────────▶│
        │                              │ 1. 전사 AVAILABLE 드라이버 조회
        │                              │ 2. assignmentPriority 오름차순 정렬
        │                              │ 3. 첫 번째 드라이버 선택
        │                              │ 4. assignDelivery() 호출
        │                              │    - status: ON_DELIVERY
        │                              │    - currentDeliveryId 설정
        │                              │    - assignmentPriority++
        │   {driverId, driverName,     │
        │    status, assignedAt}       │
        │◀─────────────────────────────│
        │                              │

허브 배송 완료 플로우

HubDelivery Service            Hub Driver Service
        │                              │
        │  PUT /internal/drivers/      │
        │      {driverId}/complete     │
        │  {deliveryTimeMin: 180}      │
        │─────────────────────────────▶│
        │                              │ completeDelivery() 호출
        │                              │ - totalDeliveries++
        │                              │ - totalDeliveryTimeMin += 180
        │                              │ - averageDeliveryTimeMin 재계산
        │                              │ - status: AVAILABLE
        │                              │ - currentDeliveryId: null
        │                              │ - assignmentPriority: 0
        │   {driverId, status,         │
        │    message: "완료"}          │
        │◀─────────────────────────────│
        │                              │

환경 설정

환경 변수 (.env)

# 서버 설정
APP_PORT=4020

# 데이터베이스
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/hub_driver_db
SPRING_DATASOURCE_USERNAME=postgres
SPRING_DATASOURCE_PASSWORD=password

# Eureka
EUREKA_DEFAULT_ZONE=http://localhost:8761/eureka/

# Keycloak
KEYCLOAK_ISSUER_URI=http://localhost:8080/realms/early-express

application.yml 주요 설정

spring:
  application:
    name: hub-driver-service
  
  jpa:
    hibernate:
      ddl-auto: validate
    properties:
      hibernate:
        default_schema: hub_driver

eureka:
  client:
    service-url:
      defaultZone: ${EUREKA_DEFAULT_ZONE}
  instance:
    prefer-ip-address: true

실행 방법

사전 요구사항

  • Java 21
  • PostgreSQL 15+
  • Eureka Server
  • Keycloak

로컬 실행

# 1. 데이터베이스 생성
createdb hub_driver_db

# 2. 환경 변수 설정
export $(cat .env | xargs)

# 3. 애플리케이션 실행
./gradlew bootRun

Docker 실행

docker-compose up -d hub-driver-service

Health Check

curl http://localhost:4020/actuator/health

프로젝트 구조

src/main/java/com/early_express/hub_driver_service/
├── domain/hub_driver/
│   ├── application/
│   │   ├── command/
│   │   │   ├── HubDriverCommandService.java
│   │   │   └── dto/
│   │   │       └── HubDriverCommandDto.java
│   │   │           ├── CreateCommand
│   │   │           ├── CreateResult
│   │   │           ├── AssignDeliveryCommand
│   │   │           ├── DriverAssignResult
│   │   │           ├── CompleteDeliveryCommand
│   │   │           └── CancelDeliveryCommand
│   │   └── query/
│   │       ├── HubDriverQueryService.java
│   │       └── dto/
│   │           └── HubDriverQueryDto.java
│   │               ├── HubDriverResponse
│   │               └── HubDriverDetailResponse
│   │
│   ├── domain/
│   │   ├── exception/
│   │   │   ├── HubDriverErrorCode.java
│   │   │   └── HubDriverException.java
│   │   ├── model/
│   │   │   ├── HubDriver.java                # Aggregate Root
│   │   │   └── vo/
│   │   │       ├── HubDriverId.java
│   │   │       └── HubDriverStatus.java
│   │   └── repository/
│   │       └── HubDriverRepository.java
│   │
│   ├── infrastructure/
│   │   └── persistence/
│   │       ├── HubDriverEntity.java
│   │       ├── HubDriverJpaRepository.java
│   │       └── HubDriverRepositoryImpl.java
│   │
│   └── presentation/
│       ├── internal/
│       │   ├── HubDriverInternalController.java
│       │   └── dto/
│       │       ├── request/
│       │       │   ├── HubDriverCreateRequest.java
│       │       │   ├── DriverAssignRequest.java
│       │       │   ├── DriverCompleteRequest.java
│       │       │   └── DriverCancelRequest.java
│       │       └── response/
│       │           ├── HubDriverCreateResponse.java
│       │           ├── DriverAssignResponse.java
│       │           └── DriverOperationResponse.java
│       └── web/
│           └── master/
│               ├── HubDriverMasterController.java
│               └── dto/response/
│                   ├── MasterHubDriverResponse.java
│                   └── MasterHubDriverDetailResponse.java
│
└── global/
    ├── common/dto/
    │   └── PageInfo.java
    └── presentation/dto/
        ├── ApiResponse.java
        └── PageResponse.java

에러 코드

에러 코드 HTTP Status 설명
DRIVER_NOT_FOUND 404 드라이버를 찾을 수 없음
DRIVER_NOT_AVAILABLE 400 드라이버가 배정 가능한 상태가 아님
DRIVER_NOT_ON_DELIVERY 400 드라이버가 배송 중이 아님
DRIVER_ALREADY_ON_DELIVERY 400 드라이버가 이미 배송 중
NO_AVAILABLE_DRIVER 400 배정 가능한 드라이버가 없음
INVALID_HUB_ID 400 유효하지 않은 허브 ID

보안

  • OAuth 2.0 Resource Server: Keycloak JWT 토큰 검증
  • 접근 제어:
    • Internal API: 서비스 간 통신만 허용 (Gateway 미노출)
    • Master API: MASTER 역할 필요, 전사 드라이버 관리
  • Hub Manager API 미제공: 전사 소속이므로 허브 관리자 권한 불필요

모니터링

  • Actuator: /actuator/health, /actuator/info
  • 주요 메트릭:
    • 전사 가용 드라이버 수
    • 평균 허브 간 배송 시간
    • 일별 허브 배송 완료 건수

관련 서비스

서비스 연동 방식 역할
User Service Feign 드라이버 등록 시 생성 요청
HubDelivery Service Feign 허브 배송 생성/완료/취소 시 드라이버 상태 관리

Hub Driver vs Last Mile Driver 비교 요약

항목 Hub Driver Last Mile Driver
소속 전사 특정 허브
hubId ❌ 없음 ✅ 있음
배정 범위 전국 허브 내
배정 요청 {hubDeliveryId} {hubId, deliveryId}
API 구성 Internal + Master Internal + HubManager + Master
평균 배송 시간 수 시간 (장거리) 수십 분 (단거리)

About

허브간 운전자 관리 서비스

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors