Skip to content

imbbeck/lessonReservation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

57 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Lesson Reservation System

μˆ˜μ—… μ˜ˆμ•½/λŒ€κΈ°/체크인 μ‹œμŠ€ν…œ


μ‹€ν–‰ 방법

1. ν™˜κ²½ μ„€μ •

# Docker ν™˜κ²½ μ‹œμž‘
chmod +x docker-commands.sh
./docker-commands.sh up

# λ˜λŠ” μˆ˜λ™μœΌλ‘œ
docker-compose up -d

2. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰

./gradlew bootRun

3. API μš”μ²­ ν…ŒμŠ€νŠΈ

4. 계정 정보

ν…ŒμŠ€νŠΈ νšŒμ› 계정

λͺ¨λ“  계정 λΉ„λ°€λ²ˆν˜Έ: 12341234

이메일 이름 μƒνƒœ
member1@example.com νšŒμ›1 ACTIVE
member2@example.com νšŒμ›2 ACTIVE
member3@example.com νšŒμ›3 ACTIVE
member4@example.com νšŒμ›4 ACTIVE
member5@example.com νšŒμ›5 ACTIVE
member6@example.com νšŒμ›6 INACTIVE

κ΄€λ¦¬μž 계정 - application.yml에 μ„€μ •.

  • 이메일: admin@lessonrsvn.com
  • λΉ„λ°€λ²ˆν˜Έ: admin123!@#
  • κΆŒν•œ: ADMIN (κ΄€λ¦¬μž μ „μš© API μ ‘κ·Ό κ°€λŠ₯)
  • μ „μš© μ—”λ“œν¬μΈνŠΈ: /api/admin/**

ν”„λ‘œμ νŠΈ κ°œμš”

μˆ˜μ—… μ˜ˆμ•½, λŒ€κΈ°μ—΄ 관리, 체크인 κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” λ°±μ—”λ“œ μ‹œμŠ€ν…œμž…λ‹ˆλ‹€.

핡심 κΈ°λŠ₯

  • μˆ˜μ—… 관리: μˆ˜μ—… 생성, 쑰회, μˆ˜μ •, μ‚­μ œ, 정원 증가
  • μ˜ˆμ•½ μ‹œμŠ€ν…œ: μˆ˜μ—… μ˜ˆμ•½ 생성 및 μ·¨μ†Œ, 쀑볡 μ˜ˆμ•½ λ°©μ§€
  • λŒ€κΈ°μ—΄ 관리: 정원 초과 μ‹œ μžλ™ λŒ€κΈ°μ—΄ 등둝 및 승격 처리
  • 체크인: μ˜ˆμ•½μž μΆœμ„ 처리, 쀑볡 체크인 λ°©μ§€
  • λ™μ‹œμ„± μ œμ–΄: Redis 뢄산락을 ν™œμš©ν•œ μ›μžμ  처리
  • μΊμ‹œ μ΅œμ ν™”: Redis 기반 μ„±λŠ₯ μ΅œμ ν™” 및 Self-healing
  • 이벀트 λ“œλ¦¬λΈ: μˆ˜μ—… μ·¨μ†Œ/정원 증가/νšŒμ› μƒνƒœ λ³€κ²½ μ‹œ μ—°κ΄€ 데이터 μžλ™ 처리
  • λ©±λ“±μ„± 보μž₯: API 레벨 λ©±λ“±μ„± 및 쀑볡 μš”μ²­ 처리

μ•„ν‚€ν…μ²˜

기술 μŠ€νƒ

  • Language: Java 21
  • Framework: Spring Boot 3.2.10, Spring Security, Spring Data JPA
  • Authentication: JWT (JSON Web Token)
  • Database: MySQL 8.0 (Flyway λ§ˆμ΄κ·Έλ ˆμ΄μ…˜)
  • Cache & Lock: Redis 7.0, Redisson (뢄산락)
  • Build Tool: Gradle
  • Testing: JUnit 5, AssertJ, Mockito, Testcontainers
  • Documentation: OpenAPI 3.0 (Swagger)
  • Containerization: Docker, Docker Compose

DDD + CQRS ꡬ쑰

com.lessonRsvn
β”œβ”€β”€ applicationInfra/          # 곡톡 인프라
β”‚   β”œβ”€β”€ config/               # μ„€μ • (Security, Redis, Async)
β”‚   β”œβ”€β”€ exception/            # μ „μ—­ μ˜ˆμ™Έ 처리
β”‚   β”œβ”€β”€ idempotency/          # λ©±λ“±μ„± ν•„ν„°
β”‚   └── redis/                # Redis μ„œλΉ„μŠ€ (μΊμ‹œ, 뢄산락)
β”œβ”€β”€ lesson/                   # μˆ˜μ—… 도메인
β”‚   β”œβ”€β”€ application/          # μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€
β”‚   β”œβ”€β”€ domain/               # 도메인 λͺ¨λΈ, 이벀트, λ ˆν¬μ§€ν† λ¦¬
β”‚   β”œβ”€β”€ dto/                  # 데이터 전솑 객체
β”‚   └── interfaces/           # REST 컨트둀러
β”œβ”€β”€ reservation/              # μ˜ˆμ•½ 도메인 (CQRS 적용)
β”‚   β”œβ”€β”€ application/          # μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€
β”‚   β”‚   β”œβ”€β”€ command/          # λͺ…λ Ή μ„œλΉ„μŠ€ (μƒνƒœ λ³€κ²½)
β”‚   β”‚   └── query/            # 쑰회 μ„œλΉ„μŠ€ (읽기 μ „μš©)
β”‚   β”œβ”€β”€ checkin/              # 체크인 μ„œλΈŒλ„λ©”μΈ
β”‚   β”œβ”€β”€ domain/               # 도메인 λͺ¨λΈ, μ„œλΉ„μŠ€, 이벀트
β”‚   β”œβ”€β”€ dto/                  # 데이터 전솑 객체
β”‚   └── interfaces/           # REST 컨트둀러 (Command/Query 뢄리)
└── member/                   # νšŒμ› 도메인
    β”œβ”€β”€ application/          # 인증/νšŒμ› μ„œλΉ„μŠ€
    β”œβ”€β”€ domain/               # 도메인 λͺ¨λΈ, 이벀트
    └── systemNotice/         # μ‹œμŠ€ν…œμ•Œλ¦Ό μ„œλΈŒλ„λ©”μΈ (TODO)

CQRS (Command Query Responsibility Segregation) 적용

μ˜ˆμ•½ 도메인에 CQRS νŒ¨ν„΄ 적용으둜 ν™•μž₯μ„±κ³Ό μ„±λŠ₯ μ΅œμ ν™”:

Command μ„œλΉ„μŠ€ (μƒνƒœ λ³€κ²½)

  • μ—­ν• : μ˜ˆμ•½ 생성, μ·¨μ†Œ, λŒ€κΈ°μ—΄ 승격 λ“± μƒνƒœ λ³€κ²½ μž‘μ—…
  • νŠΈλžœμž­μ…˜: μ“°κΈ° νŠΈλžœμž­μ…˜ (@Transactional)
  • λ™μ‹œμ„± μ œμ–΄: Redis λΆ„μ‚°λ½μœΌλ‘œ 데이터 일관성 보μž₯
  • 이벀트 λ°œν–‰: 도메인 이벀트λ₯Ό ν†΅ν•œ λΆ€κ°€ μž‘μ—… 처리

Query μ„œλΉ„μŠ€ (쑰회 μ „μš©)

  • μ—­ν• : μ˜ˆμ•½ λͺ©λ‘, λŒ€κΈ°μ—΄, 톡계 λ“± λ‹€μ–‘ν•œ 쑰회 μž‘μ—…
  • νŠΈλžœμž­μ…˜: 읽기 μ „μš© (@Transactional(readOnly = true))
  • μ„±λŠ₯ μ΅œμ ν™”: Redis μΊμ‹œ μš°μ„  쑰회, N+1 문제 ν•΄κ²°
  • ν™•μž₯μ„±: 쑰회 μ „μš© μ΅œμ ν™” 및 독립적 μŠ€μΌ€μΌλ§ κ°€λŠ₯

개발 ν™˜κ²½

λ°μ΄ν„°λ² μ΄μŠ€ 접속 정보

  • MySQL: localhost:3307/lesson_rsvn (lesson_rsvn/lesson_rsvn)
  • Redis: localhost:6380 (password: redispw)

Docker λͺ…λ Ήμ–΄

./docker-commands.sh up      # ν™˜κ²½ μ‹œμž‘
./docker-commands.sh down    # ν™˜κ²½ μ’…λ£Œ  
./docker-commands.sh restart # ν™˜κ²½ μž¬μ‹œμž‘
./docker-commands.sh logs    # 둜그 확인
./docker-commands.sh status  # μƒνƒœ 확인
./docker-commands.sh clean   # 데이터 μ‚­μ œ

μ£Όμš” λΉ„μ¦ˆλ‹ˆμŠ€ 둜직

1. μ˜ˆμ•½ 생성 ν”„λ‘œμ„ΈμŠ€

  • 쀑볡 μ˜ˆμ•½ λ°©μ§€: 동일 νšŒμ›μ˜ 동일 μˆ˜μ—… 쀑볡 μ˜ˆμ•½ 차단
  • 정원 관리: Redis μΊμ‹œ 기반 μ‹€μ‹œκ°„ 정원 확인
  • μƒνƒœ κ²°μ •: 정원 μ—¬μœ  μ‹œ μ¦‰μ‹œ ν™•μ •, 초과 μ‹œ λŒ€κΈ°μ—΄ 등둝
  • μž¬μ˜ˆμ•½ 지원: μ·¨μ†Œλœ μ˜ˆμ•½μ˜ μƒνƒœ 변경을 ν†΅ν•œ 효율적 μž¬μ˜ˆμ•½

2. μ˜ˆμ•½ μ·¨μ†Œ 및 μžλ™ 승격

  • λ™μ‹œμ„± μ œμ–΄: Redis λΆ„μ‚°λ½μœΌλ‘œ μ•ˆμ „ν•œ μ·¨μ†Œ/승격 처리
  • μžλ™ 승격: ν™•μ • μ˜ˆμ•½ μ·¨μ†Œ μ‹œ λŒ€κΈ°μ—΄ 첫 번째 νšŒμ› μžλ™ μŠΉκΈ‰
  • μˆœμ„œ 보μž₯: Redis Sorted Set 기반 λŒ€κΈ° μˆœμ„œ 관리
  • μƒνƒœ 동기화: μΊμ‹œ-DB 일관성 μœ μ§€

3. 체크인 처리

  • 자격 검증: ν™•μ • μ˜ˆμ•½ νšŒμ›λ§Œ 체크인 κ°€λŠ₯
  • μ‹œκ°„ μ œν•œ: μˆ˜μ—… μ‹œμž‘ 10λΆ„ μ „λΆ€ν„° μ’…λ£ŒκΉŒμ§€λ§Œ ν—ˆμš©
  • 쀑볡 λ°©μ§€: 동일 μ˜ˆμ•½μ— λŒ€ν•œ 쀑볡 체크인 차단

4. 이벀트 λ“œλ¦¬λΈ 처리

  • μˆ˜μ—… μ·¨μ†Œ: ν•΄λ‹Ή μˆ˜μ—…μ˜ λͺ¨λ“  μ˜ˆμ•½ μžλ™ μ·¨μ†Œ
  • 정원 증가: λŒ€κΈ°μ—΄μ—μ„œ μ¦κ°€λŸ‰λ§ŒνΌ μžλ™ μŠΉκΈ‰
  • νšŒμ› λΉ„ν™œμ„±ν™”: ν•΄λ‹Ή νšŒμ›μ˜ λͺ¨λ“  μ˜ˆμ•½ μžλ™ μ·¨μ†Œ

λ™μ‹œμ„± μ œμ–΄ 및 μ„±λŠ₯ μ΅œμ ν™”

Redis 뢄산락

  • Redisson ν™œμš©: μ•ˆμ „ν•œ λΆ„μ‚° ν™˜κ²½ λ™μ‹œμ„± μ œμ–΄
  • 락 λ²”μœ„: μˆ˜μ—…λ³„ μ˜ˆμ•½ 생성/μ·¨μ†Œ/μŠΉκΈ‰ 처리
  • μ„€μ • κ°€λŠ₯: λŒ€κΈ° μ‹œκ°„ 및 μž„λŒ€ μ‹œκ°„ ν™˜κ²½λ³„ μ„€μ •

μΊμ‹œ μ „λž΅

  • λŒ€μƒ: μˆ˜μ—…λ³„ ν™•μ • μ˜ˆμ•½ 수, λŒ€κΈ°μ—΄ 정보
  • Self-healing: μΊμ‹œ 미슀 μ‹œ DB 기반 μžλ™ 볡ꡬ
  • TTL 관리: μˆ˜μ—… μ‹œμž‘ μ‹œκ°„μ— μžλ™ 만료
  • μ •κΈ° 동기화: μŠ€μΌ€μ€„λŸ¬ 기반 μΊμ‹œ-DB 일관성 μœ μ§€

λ°μ΄ν„°λ² μ΄μŠ€ μ΅œμ ν™”

  • 인덱슀 μ „λž΅: 쿼리 νŒ¨ν„΄ 기반 볡합 인덱슀 섀계
  • N+1 ν•΄κ²°: @EntityGraph ν™œμš©ν•œ 페치 쑰인
  • 읽기 μ΅œμ ν™”: CQRS νŒ¨ν„΄μ˜ Query μ„œλΉ„μŠ€ 뢄리

ν…ŒμŠ€νŠΈ

λ‹¨μœ„ ν…ŒμŠ€νŠΈ

./gradlew test -Dspring.profiles.active=test

톡합 ν…ŒμŠ€νŠΈ (Testcontainers)

./gradlew test --tests "*Integration*" -Dspring.profiles.active=testcontainers

전체 ν…ŒμŠ€νŠΈ μ‹€ν–‰

./gradlew test

ν…ŒμŠ€νŠΈ 컀버리지 확인

./gradlew jacocoTestReport
# build/reports/jacoco/test/html/index.htmlμ—μ„œ 확인

μ£Όμš” ν…ŒμŠ€νŠΈ 클래슀

  • λ‹¨μœ„ ν…ŒμŠ€νŠΈ: 도메인 μ—”ν‹°ν‹°, μ„œλΉ„μŠ€λ³„ λ‹¨μœ„ ν…ŒμŠ€νŠΈ
  • 톡합 ν…ŒμŠ€νŠΈ: Redis, MySQL ν¬ν•¨ν•œ μ‹€μ œ ν™˜κ²½ ν…ŒμŠ€νŠΈ
  • λ™μ‹œμ„± ν…ŒμŠ€νŠΈ: 뢄산락 및 λŒ€κΈ°μ—΄ 처리 검증
  • μ‹œμŠ€ν…œ ν…ŒμŠ€νŠΈ: End-to-End 전체 ν”Œλ‘œμš° 검증

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

인증 API

POST /api/auth/login           # 둜그인
POST /api/auth/refresh         # 토큰 κ°±μ‹ 
POST /api/auth/refresh-from-cookie # μΏ ν‚€ 기반 토큰 κ°±μ‹ 
POST /api/auth/logout          # λ‘œκ·Έμ•„μ›ƒ

νšŒμ› 관리 API

=== 일반 νšŒμ›μš© ===
POST /api/members/signup       # νšŒμ› κ°€μž…
GET  /api/members/me           # λ‚΄ 정보 쑰회
PUT  /api/members/me           # λ‚΄ 정보 μˆ˜μ •
PATCH /api/members/me/password # λΉ„λ°€λ²ˆν˜Έ λ³€κ²½
DELETE /api/members/me         # νšŒμ› νƒˆν‡΄

=== κ΄€λ¦¬μžμš© ===
GET  /api/admin/members              # νšŒμ› λͺ©λ‘ 쑰회
GET  /api/admin/members/{id}         # νšŒμ› 상세 쑰회
PATCH /api/admin/members/{id}/status # νšŒμ› μƒνƒœ λ³€κ²½

μˆ˜μ—… 관리 API

=== 곡톡 ===
GET  /api/lessons              # μˆ˜μ—… λͺ©λ‘ 쑰회
GET  /api/lessons/{id}         # μˆ˜μ—… 상세 쑰회
GET  /api/lessons/reservable   # μ˜ˆμ•½ κ°€λŠ₯ν•œ μˆ˜μ—… 쑰회
GET  /api/lessons/date         # νŠΉμ • λ‚ μ§œ μˆ˜μ—… 쑰회

=== κ΄€λ¦¬μžμš© ===
POST /api/admin/lessons                # μˆ˜μ—… 생성
PUT  /api/admin/lessons/{id}           # μˆ˜μ—… 정보 μˆ˜μ •
DELETE /api/admin/lessons/{id}         # μˆ˜μ—… μ‚­μ œ(μ·¨μ†Œ)
PATCH /api/admin/lessons/{id}/capacity # μˆ˜μ—… 정원 증가

μ˜ˆμ•½ 관리 API (CQRS 적용)

Command API (μƒνƒœ λ³€κ²½)

POST /api/reservations                 # μ˜ˆμ•½ 생성
DELETE /api/reservations/{id}          # μ˜ˆμ•½ μ·¨μ†Œ

Query API (쑰회)

=== 개인 μ˜ˆμ•½ 쑰회 ===
GET /api/reservations/my                 # λ‚΄ μ˜ˆμ•½ λͺ©λ‘
GET /api/reservations/my/status          # μƒνƒœλ³„ λ‚΄ μ˜ˆμ•½
GET /api/reservations/my/active          # μ·¨μ†Œ κ°€λŠ₯ν•œ λ‚΄ μ˜ˆμ•½
GET /api/reservations/my/date-range      # 기간별 λ‚΄ μ˜ˆμ•½
GET /api/reservations/my/today           # 였늘 ν™•μ •λœ λ‚΄ μ˜ˆμ•½
GET /api/reservations/my/checkin-eligible # 체크인 κ°€λŠ₯ν•œ μ˜ˆμ•½
GET /api/reservations/my/waiting-position/{lessonId} # λ‚΄ λŒ€κΈ° 순번

=== μˆ˜μ—…λ³„ μ˜ˆμ•½ 쑰회 (κ΄€λ¦¬μž) ===
GET /api/admin/reservations/{id}               # μ˜ˆμ•½ 상세
GET /api/admin/reservations/lessons/{id}       # μˆ˜μ—…λ³„ μ˜ˆμ•½ λͺ©λ‘
GET /api/admin/reservations/lessons/{id}/waiting # μˆ˜μ—…λ³„ λŒ€κΈ°μ—΄
GET /api/admin/reservations/lessons/{id}/confirmed # μˆ˜μ—…λ³„ ν™•μ • μ˜ˆμ•½
GET /api/admin/reservations/date               # νŠΉμ • λ‚ μ§œ λͺ¨λ“  μ˜ˆμ•½

체크인 API

POST /api/checkins                       # 체크인 처리
GET  /api/checkins/my                    # λ‚΄ 체크인 ν˜„ν™©
GET  /api/admin/checkins/lessons/{id}          # μˆ˜μ—…λ³„ 체크인 ν˜„ν™© (κ΄€λ¦¬μž)
GET  /api/admin/checkins/reservations/{id}     # μ˜ˆμ•½λ³„ 체크인 쑰회 (κ΄€λ¦¬μž)

κ³ κΈ‰ κΈ°λŠ₯

λ©±λ“±μ„± 보μž₯

  • HTTP 헀더: X-Idempotency-Key 기반 쀑볡 μš”μ²­ 처리
  • μΊμ‹œ μ €μž₯: Redis 30초 TTL둜 응닡 캐싱
  • μžλ™ λ°˜ν™˜: 동일 ν‚€ μš”μ²­ μ‹œ 이전 응닡 λ°˜ν™˜

μ—λŸ¬ 처리

  • κ΅¬μ‘°ν™”λœ 응닡: μΌκ΄€λœ μ—λŸ¬ 포맷 제곡
  • λΉ„μ¦ˆλ‹ˆμŠ€ μ˜ˆμ™Έ: 도메인별 λͺ…ν™•ν•œ μ—λŸ¬ λ©”μ‹œμ§€
  • Fallback 처리: Redis μž₯μ•  μ‹œ DB 기반 μ„œλΉ„μŠ€ μœ μ§€

λ³΄μ•ˆ

  • JWT 인증: μ•‘μ„ΈμŠ€/λ¦¬ν”„λ ˆμ‹œ 토큰 기반
  • μ—­ν•  기반 μ ‘κ·Ό μ œμ–΄: 일반 νšŒμ›/κ΄€λ¦¬μž κΆŒν•œ 뢄리
  • ν™˜κ²½λ³„ λ³΄μ•ˆ μ„€μ •: 개발/운영 ν™˜κ²½ 차별화

λ¬Έμ„œ

μƒμ„Έν•œ 섀계 및 κ΅¬ν˜„ λ‚΄μš©μ€ λ‹€μŒ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”:


ν™•μž₯ κ³„νš

κ΄€λ¦¬μž 도메인

  • κ΄€λ¦¬μž 별 κΆŒν•œμ œμ–΄

배치 처리

  • μˆ˜μ—… μƒνƒœ μžλ™ λ³€κ²½ (μ‹œμž‘/μ’…λ£Œ μ‹œκ°„ κΈ°μ€€)
  • 체크인 λ¦¬λ§ˆμΈλ” μ•Œλ¦Ό
  • μΊμ‹œ μ •ν•©μ„± μ •κΈ° 점검

μ•Œλ¦Ό μ‹œμŠ€ν…œ

  • μ‹€μ‹œκ°„ μ•Œλ¦Ό (Server-Sent Events)
  • μ˜ˆμ•½ ν™•μ •/μ·¨μ†Œ/승격 μ•Œλ¦Ό
  • μˆ˜μ—… λ¦¬λ§ˆμΈλ” μ•Œλ¦Ό

λͺ¨λ‹ˆν„°λ§

  • λΉ„μ¦ˆλ‹ˆμŠ€ λ©”νŠΈλ¦­ μˆ˜μ§‘
  • μ„±λŠ₯ μ§€ν‘œ λͺ¨λ‹ˆν„°λ§
  • μ•Œλ¦Ό 및 λŒ€μ‹œλ³΄λ“œ ꡬ좕

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published