Skip to content

Latest commit

Β 

History

629 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ›΅ BaedalSodae β€” μŒμ„± 메뉴 μΆ”μ²œ 배달 ν”Œλž«νΌ

μŒμ„± κ°€κ²Œ μΆ”μ²œμ„ μ§€μ›ν•˜λŠ” 배달 μ„œλΉ„μŠ€ ν”Œλž«νΌμž…λ‹ˆλ‹€. μ „κ΅­ 지역을 λŒ€μƒμœΌλ‘œ ν•˜λ©°, ν˜„μž¬λŠ” κ΄‘ν™”λ¬Έ λ‚΄ μ§€μ—­μ—μ„œλ§Œ μ„œλΉ„μŠ€λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€. Spring Boot λ°±μ—”λ“œ ν”„λ‘œμ νŠΈμž…λ‹ˆλ‹€.


πŸ“‘ λͺ©μ°¨


πŸ‘₯ νŒ€μ› μ—­ν• λΆ„λ‹΄

νŒ€μ› λ‹΄λ‹Ή 도메인
μ‹ ν˜œμ› κ°€κ²Œ, κ°€κ²Œ μΉ΄ν…Œκ³ λ¦¬, 지도 API λ„μž…
μ΄ν˜„λΉˆ 둜그인 및 JWT 인증, νšŒμ›, νšŒμ› μ§€μ—­, κ΄€λ¦¬μž
μ •μž¬λΉˆ μ£Όλ¬Έ, μž₯λ°”κ΅¬λ‹ˆ, μ„œλ²„ ꡬ좕 및 배포
ν•˜μ§€ν˜œ 메뉴, νƒœκ·Έ, κ°€κ²Œ μš΄μ˜μ‹œκ°„, 운영 ν—ˆμš© μ§€μ—­
ν•œλ³‘λ‘ 결제, 리뷰, μŒμ„± μΆ”μ²œ AI

πŸš€ μ‹€ν–‰ 방법

펼쳐보기

방법 1: Docker Compose (ꢌμž₯)

1. 사전 μš”κ΅¬μ‚¬ν•­

docker --version          # Docker 24+ ꢌμž₯
docker compose version    # Docker Compose v2+

2. ν™˜κ²½ λ³€μˆ˜ μ„€μ •

cp .env.example .env
# .env νŒŒμΌμ„ μ—΄μ–΄ ν•„μš”ν•œ 값을 μ„€μ •ν•˜μ„Έμš”

.env μ£Όμš” ν•­λͺ©:

# PostgreSQL
POSTGRES_DB=baedalsodae
POSTGRES_USER=baedal_user
POSTGRES_PASSWORD=your_password

# Redis
SPRING_DATA_REDIS_PASSWORD=your_redis_password

# JWT
JWT_SECRET=your_jwt_secret_key_at_least_32_chars
JWT_EXPIRATION=3600000
JWT_REFRESH_EXPIRATION=604800000

3. λΉŒλ“œ 및 μ‹€ν–‰

# 전체 μ„œλΉ„μŠ€ λΉŒλ“œ ν›„ μ‹€ν–‰
docker compose up --build

# λ°±κ·ΈλΌμš΄λ“œ μ‹€ν–‰
docker compose up -d

# 둜그 확인
docker compose logs -f backend
docker compose logs -f db
docker compose logs -f redis

4. 접속 URL

μ„œλΉ„μŠ€ URL
λ°±μ—”λ“œ API http://localhost:8080/api/v1
PostgreSQL localhost:5432
Redis localhost:6379

5. μ„œλΉ„μŠ€ μ’…λ£Œ

# 쀑지
docker compose down

# 쀑지 + λ³Όλ₯¨ μ‚­μ œ (DB μ΄ˆκΈ°ν™”)
docker compose down -v

방법 2: 둜컬 직접 μ‹€ν–‰

사전 μš”κ΅¬μ‚¬ν•­

  • Java 17+
  • PostgreSQL 16
  • Redis 7

μ‹€ν–‰

# 1. ν™˜κ²½ λ³€μˆ˜ μ„€μ •
cp .env.example .env

# 2. Docker둜 PostgreSQL Β· Redis μ‹€ν–‰
docker-compose up -d

# 3. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ (IntelliJ ν™˜κ²½ λ³€μˆ˜ μ„€μ • ν›„ μ‹€ν–‰ λ˜λŠ” μ•„λž˜ λͺ…λ Ήμ–΄)
export POSTGRES_DB=baedalsodae
export POSTGRES_DB_URL=jdbc:postgresql://localhost:5432/baedalsodae
export POSTGRES_USER=baedal_user
export POSTGRES_PASSWORD=your_password
export SPRING_DATA_REDIS_HOST=localhost
export JWT_SECRET=your_jwt_secret

./gradlew bootRun

πŸ›  기술 μŠ€νƒ

ꡬ뢄 기술
Backend Spring Boot 3.5 Β· Java 17 Β· Spring Data JPA Β· Spring Security
Database PostgreSQL 16
Cache / Session Redis 7
Query QueryDSL 5.0 (μ»€μ„œ 기반 νŽ˜μ΄μ§€λ„€μ΄μ…˜)
AI Spring AI (OpenAI Β· pgvector)
Infrastructure Docker Β· Docker Compose
Logging Logback Β· logstash-logback-encoder (JSON)
API λ¬Έμ„œ Spring REST Docs
Testing JUnit 5 Β· Mockito
Build Tool Gradle

🎯 ν”„λ‘œμ νŠΈ λͺ©μ 

BaedalSodaeλŠ” μŒμ„± 주문을 핡심 κΈ°λŠ₯으둜 ν•˜λŠ” 배달 μ„œλΉ„μŠ€ ν”Œλž«νΌμž…λ‹ˆλ‹€.

  • μŒμ„± μ£Όλ¬Έ: Spring AI(OpenAI)λ₯Ό ν™œμš©ν•΄ μ‚¬μš©μžμ˜ μŒμ„± μž…λ ₯을 λΆ„μ„ν•˜κ³  μ ν•©ν•œ 메뉴λ₯Ό μΆ”μ²œ
  • μ„œλΉ„μŠ€ μ§€μ—­: μ „κ΅­ μ‹œ/도 Β· μ‹œ/κ΅°/ꡬ 기반으둜 μ„€κ³„λ˜μ—ˆμœΌλ‚˜, ν˜„μž¬λŠ” κ΄‘ν™”λ¬Έ λ‚΄ μ§€μ—­μ—μ„œλ§Œ μ„œλΉ„μŠ€ 운영 쀑

πŸ— μ•„ν‚€ν…μ²˜

Architecture

AWS VPC λ‚΄ Public/Private Subnet 뢄리 ꡬ성. HAProxy(80 port)κ°€ νŠΈλž˜ν”½μ„ 2개의 Spring Boot μΈμŠ€ν„΄μŠ€(8080)둜 λΆ„μ‚°ν•˜λ©°, PostgreSQL RDS와 ElastiCache RedisλŠ” Private Subnet에 κ²©λ¦¬λ©λ‹ˆλ‹€.

디렉터리 ꡬ쑰

펼쳐보기
baedalsodae/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main/
β”‚   β”‚   β”œβ”€β”€ java/com/project/baedalsodae/
β”‚   β”‚   β”‚   β”œβ”€β”€ allowedRegion/   # ν—ˆμš© μ§€μ—­ 관리
β”‚   β”‚   β”‚   β”œβ”€β”€ auth/            # 인증/인가 (JWT)
β”‚   β”‚   β”‚   β”œβ”€β”€ cart/            # μž₯λ°”κ΅¬λ‹ˆ
β”‚   β”‚   β”‚   β”œβ”€β”€ event/           # 도메인 이벀트 λ°œν–‰/ꡬ독
β”‚   β”‚   β”‚   β”œβ”€β”€ global/          # 곡톡 응닡, μ˜ˆμ™Έ, μœ ν‹Έ
β”‚   β”‚   β”‚   β”œβ”€β”€ menu/            # 메뉴 μΉ΄ν…Œκ³ λ¦¬ Β· 메뉴 μ•„μ΄ν…œ
β”‚   β”‚   β”‚   β”œβ”€β”€ order/           # μ£Όλ¬Έ
β”‚   β”‚   β”‚   β”œβ”€β”€ payment/         # 결제
β”‚   β”‚   β”‚   β”œβ”€β”€ recommendation/  # AI μΆ”μ²œ (Spring AI)
β”‚   β”‚   β”‚   β”œβ”€β”€ review/          # 리뷰
β”‚   β”‚   β”‚   β”œβ”€β”€ store/           # κ°€κ²Œ Β· κ°€κ²Œ μΉ΄ν…Œκ³ λ¦¬ Β· μš΄μ˜μ‹œκ°„
β”‚   β”‚   β”‚   β”œβ”€β”€ tag/             # νƒœκ·Έ
β”‚   β”‚   β”‚   └── user/            # μ‚¬μš©μž Β· μ£Όμ†Œ
β”‚   β”‚   └── resources/
β”‚   β”‚       β”œβ”€β”€ application.yml
β”‚   β”‚       β”œβ”€β”€ application-local.yml
β”‚   β”‚       β”œβ”€β”€ application-dev.yml
β”‚   β”‚       β”œβ”€β”€ application-prod.yml
β”‚   β”‚       └── logback-spring.xml
β”‚   β”œβ”€β”€ test/
β”‚   β”‚   └── java/com/project/baedalsodae/
β”‚   β”‚       β”œβ”€β”€ {domain}/
β”‚   β”‚       β”‚   β”œβ”€β”€ controller/  # @WebMvcTest + Spring REST Docs
β”‚   β”‚       β”‚   β”œβ”€β”€ service/     # Mockito λ‹¨μœ„ ν…ŒμŠ€νŠΈ
β”‚   β”‚       β”‚   └── fixture/     # ν…ŒμŠ€νŠΈ ν”½μŠ€μ²˜
β”‚   β”‚       β”œβ”€β”€ event/           # EventTest Β· EventPollerTest
β”‚   β”‚       β”œβ”€β”€ payment/         # PaymentListenerTest Β· PaymentEventPublisherTest
β”‚   β”‚       β”œβ”€β”€ cart/            # CartQueryDslSmokeTest
β”‚   β”‚       └── global/config/   # TestSecurityConfig
β”‚   └── docs/asciidoc/           # REST Docs adoc μ†ŒμŠ€
β”‚       β”œβ”€β”€ allowed-region.adoc
β”‚       β”œβ”€β”€ auth.adoc
β”‚       β”œβ”€β”€ admin-store.adoc
β”‚       β”œβ”€β”€ admin-user.adoc
β”‚       β”œβ”€β”€ menu-category.adoc
β”‚       β”œβ”€β”€ menu-item.adoc
β”‚       β”œβ”€β”€ store.adoc
β”‚       β”œβ”€β”€ store-hours.adoc
β”‚       β”œβ”€β”€ store-category.adoc
β”‚       β”œβ”€β”€ user.adoc
β”‚       └── user-address.adoc
β”œβ”€β”€ docs/
β”‚   └── API.md                   # 전체 API μ—”λ“œν¬μΈνŠΈ λͺ©λ‘
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ Dockerfile
└── build.gradle

πŸ—„ ERD

erDiagram
    p_user {
        UUID id PK
        string username UK
        string email UK
        string nickname UK
        string phone
        string password
        string name
        string role
        UUID user_main_address_id
    }
    p_user_address {
        UUID id PK
        UUID user_id FK
        string sido_code
        string sido_name
        string sigungu_code
        string sigungu_name
        string dong_code
        string dong_name
        string road_address
        string detail_address
        string description
    }
    p_store_category {
        UUID id PK
        string name
        string description
    }
    p_store {
        UUID id PK
        UUID user_id
        UUID store_category_id FK
        string name
        string business_number UK
        string phone
        string sido_code
        string sigungu_code
        string road_address
        string detail_address
        string description
        double avg_rating
        int review_count
        string store_status
    }
    p_store_hours {
        UUID id PK
        UUID store_id FK
        string day_of_week
        time open_time
        time close_time
        time break_start
        time break_end
        boolean is_open
    }
    p_menu_category {
        UUID id PK
        UUID store_id FK
        string name
        int order_no
    }
    p_menu_item {
        UUID id PK
        UUID menu_category_id FK
        string name
        decimal price
        string description
        boolean is_popular
        int order_no
        string menu_status
    }
    p_tag {
        UUID id PK
        string name UK
    }
    p_tag_mapping {
        UUID id PK
        UUID tag_id FK
        UUID menu_item_id FK
        int order_no
    }
    p_cart {
        UUID id PK
        UUID user_id UK
        UUID store_id FK
    }
    p_cart_item {
        UUID id PK
        UUID cart_id FK
        UUID menu_item_id FK
        int quantity
    }
    p_order {
        UUID id PK
        string order_no UK
        UUID user_id
        string user_nickname_snapshot
        string user_phone_snapshot
        UUID store_id
        string store_name_snapshot
        UUID address_id
        string delivery_address_snapshot
        string status
        string store_request_note
        string delivery_request_note
        decimal total_amount
        decimal delivery_fee
        decimal discount_amount
        decimal final_amount
    }
    p_order_item {
        UUID id PK
        UUID order_id FK
        UUID menu_item_id
        string name_snapshot
        decimal price_snapshot
        int quantity
    }
    p_order_status_history {
        UUID id PK
        UUID order_id
        string from_status
        string to_status
        string actor_type
        UUID actor_id
        string reason
    }
    p_payment {
        UUID id PK
        UUID order_id
        UUID user_id
        decimal amount
        string payment_method
        string status
        datetime paid_at
        string pg_transaction_id
    }
    p_review {
        UUID id PK
        UUID order_id
        UUID user_id
        int rating
        string content
        boolean is_hidden
    }
    p_allowed_region {
        UUID id PK
        string sido_code
        string sido_name
        string sigungu_code UK
        string sigungu_name
        boolean is_active
    }

    p_user ||--o{ p_user_address : "has"
    p_user ||--o| p_cart : "has"
    p_store_category ||--o{ p_store : "categorizes"
    p_store ||--o{ p_store_hours : "has"
    p_store ||--o{ p_menu_category : "has"
    p_store ||--o{ p_cart : "in"
    p_menu_category ||--o{ p_menu_item : "has"
    p_menu_item ||--o{ p_tag_mapping : "has"
    p_menu_item ||--o{ p_cart_item : "in"
    p_tag ||--o{ p_tag_mapping : "mapped"
    p_cart ||--o{ p_cart_item : "has"
    p_order ||--o{ p_order_item : "has"
    p_order ||--o{ p_order_status_history : "tracks"
    p_order ||--o| p_payment : "has"
    p_order ||--o| p_review : "has"
Loading

πŸ” 기술적 μ˜μ‚¬κ²°μ •

상세 λ‚΄μš©: docs/decisions.md

주제 μš”μ•½
μ»€μ„œ 기반 νŽ˜μ΄μ§€λ„€μ΄μ…˜ offset λ°©μ‹μ˜ 데이터 λˆ„λ½ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ cursor 방식 λ„μž…. QueryDSL둜 닀쀑 μ •λ ¬ μ „λž΅ κ΅¬ν˜„
RESTful API 섀계 μ™€μ΄μ–΄ν”„λ ˆμž„ 기반 νŒ€ 얼라인 ν›„ URI λͺ…λͺ…, HTTP λ©”μ„œλ“œ, μƒνƒœ μ½”λ“œ μ»¨λ²€μ…˜ νŒ€ 전체 ν•©μ˜
μ™€μ΄μ–΄ν”„λ ˆμž„ 기반 νŒ€ 얼라인 개발 μ „ κΈ°λŠ₯ λ²”μœ„Β·ν™”λ©΄ 흐름을 μ‹œκ°ν™”ν•˜μ—¬ μš”κ΅¬μ‚¬ν•­ μ˜€ν•΄ λ°©μ§€ 및 API 섀계 κΈ°μ€€ 곡유
μ»€μ„œ 기반 μ •λ ¬ 보μž₯ 볡합 μ •λ ¬ κΈ°μ€€μ—μ„œ 동일 κ°’ λ°œμƒ μ‹œ id νƒ€μ΄λΈŒλ ˆμ΄ν‚Ή + crossBoundary OR 쑰건으둜 νŽ˜μ΄μ§€ 경계 μ •ν™•μ„± 보μž₯
refreshToken + Redis λΈ”λž™λ¦¬μŠ€νŠΈ λ¬΄μƒνƒœμ„± μœ μ§€λ₯Ό μœ„ν•΄ accessToken/refreshToken 뢄리, λ‘œκ·Έμ•„μ›ƒλœ 토큰을 Redis TTL둜 λΈ”λž™λ¦¬μŠ€νŠΈ 관리
μ•„μ›ƒλ°•μŠ€ νŒ¨ν„΄ + 이벀트 폴링 νŠΈλžœμž­μ…˜ μ•ˆμ •μ„±μ„ μœ„ν•΄ @TransactionalEventListener λŒ€μ‹  μ•„μ›ƒλ°•μŠ€ νŒ¨ν„΄ + μŠ€μΌ€μ€„λŸ¬ 폴링 선택
비관적 락 λ„μž… 메뉴 μˆœμ„œ λ³€κ²½ μ‹œ 닀쀑 row λ™μ‹œ μˆ˜μ • β†’ 낙관적 락 μž¬μ‹œλ„ λ³΅μž‘λ„ 문제둜 비관적 락 선택
WireMock 기반 가상 PG사 κ΅¬ν˜„ μ‹€μ œ PG사 계약 없이 WireMock을 Docker Compose둜 λ„μ›Œ 결제 μŠΉμΈΒ·μ·¨μ†Œ μ „ 흐름을 μ‹€μ œμ™€ λ™μΌν•˜κ²Œ 검증
pgvector + STT 기반 메뉴 μΆ”μ²œ μŒμ„± μž…λ ₯(STT) β†’ ν…μŠ€νŠΈ μž„λ² λ”© β†’ pgvector μœ μ‚¬λ„ κ²€μƒ‰μœΌλ‘œ μžμ—°μ–΄ 기반 메뉴 μΆ”μ²œ κ΅¬ν˜„

πŸ“‘ API μ—”λ“œν¬μΈνŠΈ

κΈ°λ³Έ 경둜: /api/v1 Β· 전체 λͺ©λ‘: docs/API.md Β· REST Docs: GitHub Pages

κ·Έλ£Ή μ£Όμš” URL νŒ¨ν„΄ μ„€λͺ…
Auth /auth/** νšŒμ›κ°€μž…, 둜그인, 토큰
Users /users/me ν”„λ‘œν•„ 쑰회/μˆ˜μ •
User Address /user-addresses/** 배달 μ£Όμ†Œ 관리
Store Categories /store-categories/** κ°€κ²Œ μΉ΄ν…Œκ³ λ¦¬ 관리
Stores /stores/** κ°€κ²Œ 쑰회/관리
Store Hours /stores/{id}/hours μš΄μ˜μ‹œκ°„ 관리
Menu /menu-categories/**, /menu-items/** 메뉴 관리
Cart /carts/** μž₯λ°”κ΅¬λ‹ˆ
Orders /orders/** μ£Όλ¬Έ 생성/μƒνƒœ 관리
Reviews /reviews/** 리뷰 μž‘μ„±/쑰회
Payments /payments/** 결제 쑰회
Allowed Regions /allowed-regions/** ν—ˆμš© μ§€μ—­ 관리
Tags /tags νƒœκ·Έ 쑰회
Recommendations /recommendations/** AI μŒμ„± μΆ”μ²œ
Admin /admins/** κ΄€λ¦¬μž μ „μš©

πŸ§ͺ ν…ŒμŠ€νŠΈ

# 전체 ν…ŒμŠ€νŠΈ μ‹€ν–‰
./gradlew test

# νŠΉμ • 클래슀만 μ‹€ν–‰
./gradlew test --tests "com.project.baedalsodae.order.service.OrderServiceTest"

# ν…ŒμŠ€νŠΈ 리포트 확인
open build/reports/tests/test/index.html

ν…ŒμŠ€νŠΈ ꡬ쑰

펼쳐보기
http/                                                          ← IntelliJ HTTP Client μˆ˜λ™ ν…ŒμŠ€νŠΈ
β”œβ”€β”€ cart.http
β”œβ”€β”€ menu.http
β”œβ”€β”€ order.http
β”œβ”€β”€ store.http
β”œβ”€β”€ store-hours.http
└── (기타 도메인별 .http 파일)

src/test/
└── java/com/project/baedalsodae/
    β”œβ”€β”€ allowedRegion/
    β”‚   β”œβ”€β”€ controller/   AllowedRegionControllerTest            ← REST Docs
    β”‚   β”œβ”€β”€ service/      AllowedRegionServiceImplTest
    β”‚   └── fixture/      AllowedRegionFixture
    β”œβ”€β”€ auth/
    β”‚   β”œβ”€β”€ controller/   AuthControllerTest                     ← REST Docs
    β”‚   β”œβ”€β”€ service/      AuthServiceTest
    β”‚   └── fixture/      AuthFixture
    β”œβ”€β”€ cart/
    β”‚   β”œβ”€β”€ controller/   CartControllerTest                     ← REST Docs
    β”‚   β”œβ”€β”€ repository/   CartQueryDslSmokeTest                  ← QueryDSL 톡합
    β”‚   └── service/      CartServiceTest
    β”œβ”€β”€ event/
    β”‚   β”œβ”€β”€ entity/       EventTest
    β”‚   └── poller/       EventPollerTest
    β”œβ”€β”€ menu/
    β”‚   β”œβ”€β”€ controller/   MenuCategoryControllerTest             ← REST Docs
    β”‚   β”‚                 MenuItemControllerTest                 ← REST Docs
    β”‚   β”œβ”€β”€ service/      MenuCategoryServiceImplTest
    β”‚   β”‚                 MenuItemServiceImplTest
    β”‚   └── fixture/      MenuCategoryMockFixture Β· MenuItemMockFixture Β· ...
    β”œβ”€β”€ order/
    β”‚   β”œβ”€β”€ controller/   OrderControllerTest                    ← REST Docs
    β”‚   β”‚                 AdminOrderControllerTest               ← REST Docs
    β”‚   └── service/      OrderServiceTest
    β”œβ”€β”€ payment/
    β”‚   β”œβ”€β”€ listener/     PaymentListenerTest
    β”‚   └── publisher/    PaymentEventPublisherTest
    β”œβ”€β”€ review/
    β”‚   β”œβ”€β”€ controller/   ReviewControllerTest                   ← REST Docs
    β”‚   └── fixture/      ReviewTestConstants
    β”œβ”€β”€ store/
    β”‚   β”œβ”€β”€ controller/   StoreControllerTest                    ← REST Docs
    β”‚   β”‚                 AdminStoreControllerTest               ← REST Docs
    β”‚   β”‚                 StoreCategoryControllerTest            ← REST Docs
    β”‚   β”‚                 StoreHourControllerTest                ← REST Docs
    β”‚   β”œβ”€β”€ service/      StoreCommandServiceTest Β· StoreQueryServiceTest
    β”‚   β”‚                 StoreCategoryServiceTest Β· StoreHoursServiceImplTest
    β”‚   β”œβ”€β”€ dto/          CreateStoreCategoryRequestTest
    β”‚   └── fixture/      StoreHoursMockFixture Β· StoreHoursRequestFixture Β· ...
    β”œβ”€β”€ tag/
    β”‚   └── service/      TagServiceImplTest Β· TagMappingServiceImplTest
    β”œβ”€β”€ user/
    β”‚   β”œβ”€β”€ controller/   UserControllerTest                     ← REST Docs
    β”‚   β”‚                 AdminUserControllerTest                ← REST Docs
    β”‚   β”‚                 UserAddressControllerTest              ← REST Docs
    β”‚   β”œβ”€β”€ service/      UserServiceTest Β· UserAddressServiceTest
    β”‚   └── fixture/      UserFixture
    └── global/config/    TestSecurityConfig

ν…ŒμŠ€νŠΈ μ „λž΅

λ ˆμ΄μ–΄ μ „λž΅ 도ꡬ
Service Mockito둜 Repository λͺ©ν‚Ή, λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 검증 JUnit 5 + Mockito
Controller @WebMvcTest + MockMvc둜 HTTP 슬라이슀 ν…ŒμŠ€νŠΈ + API λ¬Έμ„œ μžλ™ 생성 JUnit 5 + MockMvc + Spring REST Docs
μˆ˜λ™ E2E IntelliJ HTTP Client둜 μ‹€μ œ μ„œλ²„ λŒ€μƒ API 흐름 검증 .http 파일

IntelliJ HTTP Client λ„μž… 이유

Postman 같은 별도 툴 없이 IDE μ•ˆμ—μ„œ λ°”λ‘œ APIλ₯Ό ν˜ΈμΆœν•˜κ³ , .http νŒŒμΌμ„ Git으둜 νŒ€ 전체가 κ³΅μœ ν•  수 μžˆμ–΄ λ„μž…ν–ˆμŠ΅λ‹ˆλ‹€.

  • IDE 톡합: 별도 툴 μ„€μΉ˜ 없이 IntelliJμ—μ„œ λ°”λ‘œ μ‹€ν–‰
  • ν˜•μƒκ΄€λ¦¬: .http νŒŒμΌμ„ Git에 μ»€λ°‹ν•˜μ—¬ νŒ€μ› λͺ¨λ‘ λ™μΌν•œ μš”μ²­ 곡유
  • 연속 호좜: JavaScript둜 이전 응닡값을 λ‹€μŒ μš”μ²­μ— ν™œμš© β€” 둜그인 β†’ μ£Όλ¬Έ 생성 같은 2~3단계 흐름을 ν•œ λ²ˆμ— μ‹€ν–‰ κ°€λŠ₯
  • ν™˜κ²½ 뢄리: http-client.env.json으둜 dev/prod별 URL·토큰 λ“± ν™˜κ²½λ³€μˆ˜ 뢄리 관리

πŸ’‘ 기술 μŠ€νƒ μ„ μ • λ°°κ²½

Spring Boot 3.5 + Java 17

  • 생산성: Spring Boot Auto-configuration으둜 λΉ λ₯Έ 개발
  • μ•ˆμ •μ„±: λŒ€κ·œλͺ¨ μ„œλΉ„μŠ€μ—μ„œ κ²€μ¦λœ ν”„λ ˆμž„μ›Œν¬
  • μƒνƒœκ³„: Spring Security, Spring Data JPA λ“± ν’λΆ€ν•œ 라이브러리
  • Java 17: Record, Sealed Class λ“± μ΅œμ‹  문법 ν™œμš©, LTS λ²„μ „μœΌλ‘œ μ•ˆμ •μ„± 확보

PostgreSQL 16

  • μ‹ λ’°μ„±: ACID μ™„μ „ 지원, λ³΅μž‘ν•œ νŠΈλžœμž­μ…˜ (μ£Όλ¬Έ 처리) μ•ˆμ „ν•˜κ²Œ 처리
  • JSON 지원: λ°˜μ •ν˜• 데이터λ₯Ό JSONB둜 μ €μž₯ κ°€λŠ₯
  • μ„±λŠ₯: κ³ κΈ‰ 인덱싱 (λΆ€λΆ„ 인덱슀, 볡합 인덱슀)으둜 λŒ€μš©λŸ‰ 리뷰 쿼리 μ΅œμ ν™”

Redis 7

  • μ„Έμ…˜ 관리: JWT Refresh Token 쀑앙 μ €μž₯ 및 λΈ”λž™λ¦¬μŠ€νŠΈ 관리
  • 캐싱: 자주 μ‘°νšŒλ˜λŠ” κ°€κ²Œ λͺ©λ‘, 메뉴 정보 μΊμ‹±μœΌλ‘œ DB λΆ€ν•˜ κ°μ†Œ
  • TTL 지원: 토큰 만료λ₯Ό μžλ™μœΌλ‘œ 처리

QueryDSL 5.0

  • νƒ€μž… μ•ˆμ „: 컴파일 νƒ€μž„μ— 쿼리 였λ₯˜ 감지
  • 동적 쿼리: μ»€μ„œ 기반 νŽ˜μ΄μ§€λ„€μ΄μ…˜, 닀쀑 μ •λ ¬ μ „λž΅μ„ λ©”μ„œλ“œ λ‹¨μœ„λ‘œ λΆ„λ¦¬ν•΄μ„œ 관리

Spring AI + pgvector

  • AI μΆ”μ²œ: OpenAI μž„λ² λ”© 기반 메뉴 μœ μ‚¬λ„ 검색
  • 벑터 μ €μž₯: pgvector둜 PostgreSQL에 μž„λ² λ”© 벑터 μ €μž₯ 및 검색

Docker + Docker Compose

  • ν™˜κ²½ 일관성: 개발/μŠ€ν…Œμ΄μ§•/ν”„λ‘œλ•μ…˜ ν™˜κ²½ λ™μΌν•˜κ²Œ μœ μ§€
  • 격리성: 각 μ„œλΉ„μŠ€(λ°±μ—”λ“œ, DB, Redis)κ°€ λ…λ¦½λœ μ»¨ν…Œμ΄λ„ˆμ—μ„œ μ‹€ν–‰
  • κ°„νŽΈν•œ μ‹€ν–‰: docker compose up ν•œ μ€„λ‘œ 전체 μŠ€νƒ 기동

JUnit 5 + Mockito + Spring REST Docs

  • JUnit 5: @ExtendWith, @DisplayName, νŒŒλΌλ―Έν„°ν™” ν…ŒμŠ€νŠΈ λ“± κ°•λ ₯ν•œ ν…ŒμŠ€νŠΈ ν‘œν˜„λ ₯
  • Mockito: μ™ΈλΆ€ μ˜μ‘΄μ„±μ„ λͺ©ν‚Ήν•˜μ—¬ 순수 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직만 λ‹¨μœ„ ν…ŒμŠ€νŠΈ
  • Spring REST Docs: ν…ŒμŠ€νŠΈ μ½”λ“œλ‘œ API λ¬Έμ„œλ₯Ό μžλ™ 생성, μ½”λ“œμ™€ λ¬Έμ„œμ˜ 일치 보μž₯

🧹 μ½”λ“œ ν¬λ§·νŒ… (Spotless)

이 ν”„λ‘œμ νŠΈλŠ” μΌκ΄€λœ Java μ½”λ“œ μŠ€νƒ€μΌμ„ μœ μ§€ν•˜κΈ° μœ„ν•΄ Spotless(Google Java Format) λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. main, dev 브랜치둜 PR을 올릴 λ•Œ GitHub Actions CIλ₯Ό 톡해 μžλ™μœΌλ‘œ μ½”λ“œ 포맷을 κ²€μ‚¬ν•˜λ©°, 포맷이 μ–΄κΈ‹λ‚œ μ½”λ“œμ˜ λ¨Έμ§€λ₯Ό μ°¨λ‹¨ν•©λ‹ˆλ‹€.

# 포맷 μžλ™ μ •λ ¬ 적용 (컀밋 μ „ ν•„μˆ˜)
./gradlew spotlessApply

# 포맷 μ€€μˆ˜ μ—¬λΆ€ 검사 (CI 검증 λͺ…λ Ήμ–΄)
./gradlew spotlessCheck

⚠️ μ£Όμ˜μ‚¬ν•­

  • .env νŒŒμΌμ€ μ ˆλŒ€ Git에 μ»€λ°‹ν•˜μ§€ λ§ˆμ„Έμš” (.gitignore에 포함됨)
  • JWT Secret KeyλŠ” μ΅œμ†Œ 32자 μ΄μƒμœΌλ‘œ μ„€μ •ν•˜μ„Έμš”
  • ν”„λ‘œλ•μ…˜ 배포 μ‹œ SPRING_PROFILES_ACTIVE=prod μ„€μ • ν•„μš”

πŸ“„ λΌμ΄μ„ μŠ€

This project is for educational purposes.

About

store, order, payment, delivery domain service

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages