EComμ νλμ μΈ λ§μ΄ν¬λ‘μλΉμ€ μν€ν μ²λ₯Ό κΈ°λ°μΌλ‘ ꡬμΆλ E-commerce νλ«νΌμ λλ€. Spring Boot 3.xμ Java 17μ μ¬μ©νμ¬ κ°λ°λμμΌλ©°, νμ₯μ±κ³Ό μ μ§λ³΄μμ±μ κ³ λ €ν λ©ν°λͺ¨λ κ΅¬μ‘°λ‘ μ€κ³λμμ΅λλ€.
- μ¬μ©μ κ΄λ¦¬: JWT κΈ°λ° μΈμ¦/μΈκ° μμ€ν
- RBAC 보μ: μν κΈ°λ° μ κ·Ό μ μ΄ (User, Admin, Super Admin)
- μν κ΄λ¦¬: μΉ΄ν κ³ λ¦¬λ³ μν κ΄λ¦¬, μ΄λ―Έμ§ μ λ‘λ, κ²μ λ° νν°λ§
- μ₯λ°κ΅¬λ: μ€μκ° μ₯λ°κ΅¬λ κ΄λ¦¬ λ° μΈμ μ μ§
- μ£Όλ¬Έ μ²λ¦¬: μ£Όλ¬Έ μμ±, κ²°μ μ²λ¦¬, μ£Όλ¬Έ μΆμ
- λ°°μ‘ κ΄λ¦¬: λ°°μ‘μ£Όμ κ΄λ¦¬, κΈ°λ³Έ μ£Όμ μ€μ
- κ΄λ¦¬μ μμ€ν : μν, μ£Όλ¬Έ, μ¬μ©μ κ΄λ¦¬
- Framework: Spring Boot 3.4.3, Spring Security, Spring Data JPA
- Language: Java 17
- Build Tool: Gradle 8.x
- Authentication: JWT (jjwt 0.11.5)
- Authorization: RBAC (Role-Based Access Control)
- Query: QueryDSL 5.0.0
- Validation: Spring Boot Starter Validation
- Primary DB: MySQL 8.0
- Cache: Redis
- Connection Pool: HikariCP
- Cloud: AWS S3
- Local Development: MinIO
- Containerization: Docker, Docker Compose
- Testing: JUnit 5, Spring Boot Test, H2 (Test DB)
graph TB
subgraph "Client Layer"
WEB[Web Client]
MOBILE[Mobile App]
ADMIN[Admin Panel]
end
subgraph "Gateway Layer"
LB[Load Balancer]
end
subgraph "Application Layer"
API[API Module]
ADMIN_API[Admin Module]
end
subgraph "Core Layer"
CORE[Core Module]
DOMAIN[Domain Entities]
REPO[Repositories]
end
subgraph "Infrastructure Layer"
DB[(MySQL 8.0)]
REDIS[(Redis Cache)]
S3[AWS S3/MinIO]
end
WEB --> LB
MOBILE --> LB
ADMIN --> LB
LB --> API
LB --> ADMIN_API
API --> CORE
ADMIN_API --> CORE
CORE --> DB
CORE --> REDIS
CORE --> S3
classDef client fill:#e1f5fe
classDef app fill:#f3e5f5
classDef core fill:#e8f5e8
classDef infra fill:#fff3e0
class WEB,MOBILE client
class API,ADMIN_API app
class CORE core
class DB,REDIS,S3 infra
ECom/
βββ api/ # API λͺ¨λ (μ¬μ©μ λμ)
β βββ src/main/java/com/example/api/
β βββ ApiApplication.java
β βββ core/config/ # API μ€μ
β βββ module/ # κΈ°λ₯λ³ λͺ¨λ
β βββ auth/ # μΈμ¦
β βββ cart/ # μ₯λ°κ΅¬λ
β βββ order/ # μ£Όλ¬Έ
β βββ product/ # μν
β βββ payment/ # κ²°μ
β βββ shipping_address/ # λ°°μ‘μ£Όμ
βββ admin/ # κ΄λ¦¬μ λͺ¨λ
β βββ src/main/java/com/example/admin/
β βββ AdminApplication.java
β βββ module/
β βββ auth/ # κ΄λ¦¬μ μΈμ¦
βββ core/ # ν΅μ¬ λͺ¨λ (곡ν΅)
β βββ src/main/java/com/example/core/
β βββ CoreApplication.java
β βββ config/ # κ³΅ν΅ μ€μ
β βββ domain/ # λλ©μΈ μν°ν°
β βββ dto/ # λ°μ΄ν° μ μ‘ κ°μ²΄
β βββ enums/ # μ΄κ±°ν
β βββ exception/ # μμΈ μ²λ¦¬
β βββ model/ # μλ΅ λͺ¨λΈ
β βββ utils/ # μ νΈλ¦¬ν°
βββ docker-compose.yml # λ‘컬 κ°λ° νκ²½
βββ build.gradle # λΉλ μ€μ
- Java 17+
- Docker & Docker Compose
- Gradle 8.x+
- μ μ₯μ ν΄λ‘
git clone https://github.com/f-lab-edu/ECom.git
cd ECom
- μΈνλΌ μλΉμ€ μμ
docker-compose up -d
- μ ν리μΌμ΄μ λΉλ λ° μ€ν
# Core λͺ¨λ λΉλ
./gradlew :core:build
# API μλ² μ€ν
./gradlew :api:bootRun
# κ΄λ¦¬μ μλ² μ€ν (λ³λ ν°λ―Έλ)
./gradlew :admin:bootRun
- μλΉμ€ νμΈ
- API μλ²: http://localhost:8080
- κ΄λ¦¬μ μλ²: http://localhost:8081
- MinIO μΉ UI: http://localhost:9001 (minioadmin/minioadmin)
Method | Endpoint | Description | Auth Required |
---|---|---|---|
POST | /api/v1/auth/signup |
νμκ°μ | β |
POST | /api/v1/auth/login |
λ‘κ·ΈμΈ | β |
POST | /api/v1/auth/refresh |
ν ν° κ°±μ | β |
Method | Endpoint | Description | Auth Required |
---|---|---|---|
GET | /api/v1/products |
μν κ²μ (μΉ΄ν κ³ λ¦¬, κ°κ²©, μ λ ¬, νμ΄μ§) | β |
GET | /api/v1/products/{productId} |
μν μμΈ μ‘°ν | β |
POST | /api/v1/products/image |
μ΄λ―Έμ§ μ λ‘λ | β (ADMIN/SUPER_ADMIN) |
POST | /api/v1/products |
μν μμ± | β (ADMIN/SUPER_ADMIN) |
PUT | /api/v1/products/{productId} |
μν μμ | β (ADMIN/SUPER_ADMIN) |
DELETE | /api/v1/products/{productId} |
μν μμ | β (ADMIN/SUPER_ADMIN) |
Method | Endpoint | Description | Auth Required |
---|---|---|---|
GET | /api/v1/cart |
μ₯λ°κ΅¬λ μ‘°ν | β (USER) |
POST | /api/v1/cart/products |
μν μΆκ° | β (USER) |
PUT | /api/v1/cart/products/{productId} |
μλ μμ | β (USER) |
DELETE | /api/v1/cart/products/{productId} |
μν μμ | β (USER) |
Method | Endpoint | Description | Auth Required |
---|---|---|---|
POST | /api/v1/order/product |
μν μ£Όλ¬Έ | β (USER) |
GET | /api/v1/order |
μ£Όλ¬Έ λͺ©λ‘ μ‘°ν | β (USER) |
GET | /api/v1/order/{orderId} |
μ£Όλ¬Έ μμΈ μ‘°ν | β (USER) |
Method | Endpoint | Description | Auth Required |
---|---|---|---|
GET | /api/v1/shipping-address |
λ°°μ‘μ£Όμ λͺ©λ‘ | β (USER) |
POST | /api/v1/shipping-address |
λ°°μ‘μ£Όμ μμ± | β (USER) |
PUT | /api/v1/shipping-address/{addressId} |
λ°°μ‘μ£Όμ μμ | β (USER) |
PUT | /api/v1/shipping-address/{addressId}/default |
κΈ°λ³Έ μ£Όμ μ€μ | β (USER) |
DELETE | /api/v1/shipping-address/{addressId} |
λ°°μ‘μ£Όμ μμ | β (USER) |
Method | Endpoint | Description | Auth Required |
---|---|---|---|
POST | /admin/v1/auth/login |
κ΄λ¦¬μ λ‘κ·ΈμΈ | β |
POST | /admin/v1/auth/refresh |
ν ν° κ°±μ | β |
POST | /admin/v1/auth/admins |
κ΄λ¦¬μ μμ± | β (SUPER_ADMIN) |
κΈ°λ₯ | USER | ADMIN | SUPER_ADMIN |
---|---|---|---|
νμκ°μ /λ‘κ·ΈμΈ | β | β | β |
μν μ‘°ν | β | β | β |
μ₯λ°κ΅¬λ κ΄λ¦¬ | β | β | β |
μ£Όλ¬Έ μμ±/μ‘°ν | β | β | β |
λ°°μ‘μ£Όμ κ΄λ¦¬ | β | β | β |
μν κ΄λ¦¬ (CRUD) | β | β | β |
κ΄λ¦¬μ μμ± | β | β | β |
- Method-Level Security:
@PreAuthorize
μ΄λ Έν μ΄μ νμ© - URL-Level Security: Spring Security νν° μ²΄μΈ νμ©
- JWT ν΅ν©: ν ν°μ μν μ 보 ν¬ν¨
- μλ μν ν λΉ: νμκ°μ
μ
ROLE_USER
μλ λΆμ¬
- μΊμ±: Redisλ₯Ό νμ©ν μ‘°ν μ±λ₯ ν₯μ
- Pessimistic Lock: μ¬κ³ κ΄λ¦¬ λμμ± μ μ΄
- RBAC (Role-Based Access Control): μν κΈ°λ° μ κ·Ό μ μ΄ μμ€ν
ROLE_USER
: μΌλ° μ¬μ©μ κΆν (μ₯λ°κ΅¬λ, μ£Όλ¬Έ, λ°°μ‘μ£Όμ κ΄λ¦¬)ROLE_ADMIN
: κ΄λ¦¬μ κΆν (μν κ΄λ¦¬, μ¬μ©μ μ‘°ν)ROLE_SUPER_ADMIN
: μ΅κ³ κ΄λ¦¬μ κΆν (κ΄λ¦¬μ μμ±, μ 체 μμ€ν κ΄λ¦¬)
- JWT μΈμ¦: ν ν° κΈ°λ° μΈμ¦/μΈκ°, μν μ 보 ν¬ν¨
- Method-Level Security:
@PreAuthorize
μ΄λ Έν μ΄μ μ ν΅ν μΈλ°ν κΆν μ μ΄ - URL-Level Security: Spring Security μ€μ μ ν΅ν μλν¬μΈνΈλ³ μ κ·Ό μ μ΄
- λΉλ°λ²νΈ Salt + Hash μ μ₯