Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ out/

### VS Code ###
.vscode/
.DS_Store
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
3. 이미 μ˜ˆμ•½ λ˜λŠ” λŒ€κΈ°κ°€ μžˆλŠ” μŠ¬λ‘―μ—λŠ” 직접 μ˜ˆμ•½ν•  수 μ—†λ‹€.
4. μ˜ˆμ•½ λ˜λŠ” μŠ¬λ‘―μ—μ„œ μ‚¬μš© 쀑인 μ‹œκ°„/ν…Œλ§ˆ μ‚­μ œλŠ” μ œν•œλœλ‹€.
5. 본인 μ†Œμœ κ°€ μ•„λ‹Œ μ˜ˆμ•½ λ˜λŠ” λŒ€κΈ° μ·¨μ†ŒλŠ” κ±°λΆ€λœλ‹€.
6. μ—†λŠ” μ˜ˆμ•½ λ˜λŠ” λŒ€κΈ° μ·¨μ†Œ μš”μ²­μ€ κΈ°μ‘΄ 정책에 따라 성곡 μ²˜λ¦¬ν•œλ‹€.
6. μ—†λŠ” μ˜ˆμ•½ μ·¨μ†Œ μš”μ²­μ€ 성곡 μ²˜λ¦¬ν•œλ‹€. μ—†λŠ” λŒ€κΈ° μ·¨μ†Œ μš”μ²­μ€ `404 Not Found`와 `WAITING_404`둜 μ‹€νŒ¨ μ²˜λ¦¬ν•œλ‹€.

## 2. API λͺ…μ„Έ

Expand Down Expand Up @@ -415,7 +415,55 @@ DELETE /api/manager/slots/{slotId}

이λ₯Ό 막기 μœ„ν•΄ μ˜ˆμ•½ μ·¨μ†Œμ˜ 승격 λŒ€μƒ λŒ€κΈ°μ—΄ μ‘°νšŒμ™€ λŒ€κΈ° μ·¨μ†Œμ˜ 단건 λŒ€κΈ° μ‘°νšŒμ— `SELECT ... FOR UPDATE`λ₯Ό μ‚¬μš©ν•œλ‹€.

## 4. ν…ŒμŠ€νŠΈ μ „λž΅
## 4. JPA μ—°κ²° λ―Έμ…˜ 이해 κ°€μ΄λ“œ

### ν˜„μž¬ ꡬ쑰 μš”μ•½

이 ν”„λ‘œμ νŠΈλŠ” κΈ°μ‘΄ λ°©νƒˆμΆœ μ˜ˆμ•½/λŒ€κΈ° 도메인을 JPA 기반 μ˜μ†μ„±μœΌλ‘œ μ—°κ²°ν•œ μƒνƒœλ‹€. 읽을 λ•ŒλŠ” λ‹€μŒ μˆœμ„œλ‘œ 보면 이해가 쉽닀.

1. **도메인 μ—”ν‹°ν‹°**: `member`, `theme`, `reservationtime`, `slot`, `reservation`, `waiting` νŒ¨ν‚€μ§€μ˜ `domain` ν΄λž˜μŠ€κ°€ JPA `@Entity`λ‹€.
2. **μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 포트**: 각 κΈ°λŠ₯의 `application.port.out` μ €μž₯μ†Œ μΈν„°νŽ˜μ΄μŠ€κ°€ μ„œλΉ„μŠ€κ°€ μ˜μ‘΄ν•˜λŠ” 좔상화닀.
3. **JPA μ–΄λŒ‘ν„°**: `adapter.out.persistence.Jpa*Repository`κ°€ 포트λ₯Ό κ΅¬ν˜„ν•˜κ³ , λ‚΄λΆ€μ—μ„œ `SpringData*Repository`λ₯Ό ν˜ΈμΆœν•œλ‹€.
4. **μœ μŠ€μΌ€μ΄μŠ€ μ„œλΉ„μŠ€**: `ReservationService`, `WaitingService`, `SlotService` 등이 νŠΈλžœμž­μ…˜κ³Ό 도메인 κ·œμΉ™ 흐름을 μ‘°μœ¨ν•œλ‹€.
5. **검증 ν…ŒμŠ€νŠΈ**: `Jpa*RepositoryTest`, API 톡합 ν…ŒμŠ€νŠΈ, λ™μ‹œμ„± 톡합 ν…ŒμŠ€νŠΈκ°€ JPA λ§€ν•‘κ³Ό μš”κ΅¬μ‚¬ν•­μ„ ν•¨κ»˜ κ²€μ¦ν•œλ‹€.

### μ£Όμš” JPA λ§€ν•‘ κ²°μ •

- `Reservation`, `Waiting`, `Slot`은 λͺ¨λ‘ `Member`, `Slot`, `Theme`, `ReservationTime`을 μ‹λ³„μž ν•„λ“œκ°€ μ•„λ‹ˆλΌ 객체 μ—°κ΄€μœΌλ‘œ κ°€μ§„λ‹€.
- λ‹€λŒ€μΌ 연관은 기본적으둜 `FetchType.LAZY`λ₯Ό μ‚¬μš©ν•΄ μ„œλΉ„μŠ€ νλ¦„μ—μ„œ ν•„μš”ν•œ λ°μ΄ν„°λ§Œ μ‘°νšŒν•œλ‹€.
- ν•œ μŠ¬λ‘―μ—λŠ” ν•˜λ‚˜μ˜ μ˜ˆμ•½λ§Œ μ‘΄μž¬ν•΄μ•Ό ν•˜λ―€λ‘œ `reservation.slot_id`에 `uk_reservation_slot` μœ λ‹ˆν¬ μ œμ•½μ„ λ‘”λ‹€.
- 같은 μ‚¬μš©μžλŠ” 같은 μŠ¬λ‘―μ— ν•œ 번만 λŒ€κΈ°ν•  수 μžˆμœΌλ―€λ‘œ `waiting(member_id, slot_id)`에 `uk_waiting_member_slot` μœ λ‹ˆν¬ μ œμ•½μ„ λ‘”λ‹€.
- μŠ¬λ‘―μ€ `date`, `time_id`, `theme_id` μ‘°ν•©μœΌλ‘œ μœ μΌν•΄μ•Ό ν•˜λ―€λ‘œ `uk_slot_date_time_theme` μ œμ•½μ„ λ‘”λ‹€.
- λŒ€κΈ° μˆœλ²ˆμ€ 별도 컬럼으둜 μ €μž₯ν•˜μ§€ μ•Šκ³ , 같은 슬둯의 `waiting.id` μ˜€λ¦„μ°¨μˆœμœΌλ‘œ 쑰회 μ‹œ κ³„μ‚°ν•œλ‹€.

### νŠΈλžœμž­μ…˜/락 μ •μ±…

- μ˜ˆμ•½ μ·¨μ†Œμ™€ 첫 번째 λŒ€κΈ° μŠΉκ²©μ€ ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜μ—μ„œ μ²˜λ¦¬ν•œλ‹€.
- 승격 λŒ€μƒ λŒ€κΈ°μ—΄ μ‘°νšŒμ™€ λŒ€κΈ° μ·¨μ†Œ 단건 μ‘°νšŒλŠ” λͺ…λ Ή νλ¦„μ—μ„œλ§Œ `PESSIMISTIC_WRITE` 락을 μ‚¬μš©ν•œλ‹€.
- 쑰회용 λ©”μ„œλ“œμ™€ 락 쑰회 λ©”μ„œλ“œλ₯Ό 뢄리해, 일반 ν™”λ©΄ μ‘°νšŒκ°€ λΆˆν•„μš”ν•˜κ²Œ row lock을 μž‘μ§€ μ•Šλ„λ‘ ν–ˆλ‹€.
- λŒ€κΈ° μ·¨μ†Œ μš”μ²­μ—μ„œ λŒ€μƒ λŒ€κΈ°κ°€ μ—†μœΌλ©΄ 성곡 μ²˜λ¦¬ν•˜μ§€ μ•Šκ³  `WAITING_404`둜 μ‹€νŒ¨μ‹œν‚¨λ‹€. μ΄λŠ” 승격된 λŒ€κΈ°λ₯Ό λ’€λŠ¦κ²Œ μ·¨μ†Œ μ„±κ³΅μœΌλ‘œ μ˜€ν•΄ν•˜λŠ” 상황을 막기 μœ„ν•œ 정책이닀.

### μ½”λ“œ 리뷰 체크리슀트

JPA μ—°κ²° 이후 변경을 κ²€ν† ν•  λ•ŒλŠ” λ‹€μŒμ„ μš°μ„  ν™•μΈν•œλ‹€.

- μ—”ν‹°ν‹° 연관관계와 DB μ œμ•½μ΄ 도메인 κ·œμΉ™κ³Ό 같은 λ°©ν–₯인지 ν™•μΈν•œλ‹€.
- μ„œλΉ„μŠ€κ°€ Spring Data κ΅¬ν˜„μ²΄μ— 직접 μ˜μ‘΄ν•˜μ§€ μ•Šκ³  `application.port.out` ν¬νŠΈμ—λ§Œ μ˜μ‘΄ν•˜λŠ”μ§€ ν™•μΈν•œλ‹€.
- `@Transactional`이 데이터 λ³€κ²½ μœ μŠ€μΌ€μ΄μŠ€μ™€ `FOR UPDATE` 쑰회λ₯Ό ν¬ν•¨ν•˜λŠ” λ©”μ„œλ“œμ— κ±Έλ € μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
- JPQL projection이 응닡 DTO 쑰립을 λ‹¨μˆœν™”ν•˜λŠ”μ§€, λ°˜λŒ€λ‘œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 계측을 μ˜μ†μ„± 세뢀사항에 κ³Όν•˜κ²Œ λ¬Άμ§€λŠ” μ•ŠλŠ”μ§€ ν™•μΈν•œλ‹€.
- λŒ€κΈ° 순번 계산, μ˜ˆμ•½ μ·¨μ†Œ/승격, μ—†λŠ” λŒ€κΈ° μ·¨μ†Œ μ‹€νŒ¨ 정책이 ν…ŒμŠ€νŠΈ 이름과 λ¬Έμ„œμ— ν•¨κ»˜ λ“œλŸ¬λ‚˜λŠ”μ§€ ν™•μΈν•œλ‹€.

### 남은 κ°œμ„  후보

- 운영 DBκ°€ H2κ°€ 아닐 경우 `SELECT ... FOR UPDATE`와 `ORDER BY` μ‘°ν•©μ˜ μ‹€μ œ 락 λ²”μœ„λ₯Ό λ‹€μ‹œ 확인해야 ν•œλ‹€.
- `findMyReservations()`λŠ” μ˜ˆμ•½ λͺ©λ‘κ³Ό λŒ€κΈ° λͺ©λ‘μ„ ν•©μΉœ λ’€ μ •λ ¬ 정책이 λͺ…ν™•ν•˜μ§€ μ•Šλ‹€. ν™”λ©΄ μš”κ΅¬κ°€ 생기면 λ‚ μ§œ/μ‹œκ°„/id κΈ°μ€€ 정렬을 λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.
- κ΄€λ¦¬μž μ˜ˆμ•½ μ·¨μ†Œμ—μ„œ μ—†λŠ” μ˜ˆμ•½μ„ 성곡 μ²˜λ¦¬ν•˜λŠ” 정책은 ν˜„μž¬ μœ μ§€λ˜μ§€λ§Œ, λŒ€κΈ° μ·¨μ†Œ μ •μ±…κ³Ό λ‹€λ₯΄λ―€λ‘œ API λ¬Έμ„œμ™€ ν…ŒμŠ€νŠΈμ—μ„œ μ˜λ„λ₯Ό 계속 뢄리해야 ν•œλ‹€.
- `theme.name`, `reservation_time.start_at`처럼 μ„œλΉ„μŠ€μ—μ„œ 쀑볡을 κ²€μ¦ν•˜λŠ” 값은 λ™μ‹œ μš”μ²­κΉŒμ§€ λ§‰μœΌλ €λ©΄ DB unique constraint와 μ˜ˆμ™Έ 맀핑을 μΆ”κ°€λ‘œ κ²€ν† ν•΄μ•Ό ν•œλ‹€.
- μš”μ²­ DTO의 ν•„μˆ˜κ°’/λ¬Έμžμ—΄ 길이 검증은 API 계약과 ν•¨κ»˜ 관리해야 ν•œλ‹€. λˆ„λ½ μ‹œ 도메인 생성 λ˜λŠ” DB μ œμ•½ 였λ₯˜λ‘œ 늦게 λ“œλŸ¬λ‚  수 μžˆλ‹€.
- `ddl-auto: create-drop`κ³Ό `data.sql` μ΄ˆκΈ°ν™”λŠ” 둜컬/λ―Έμ…˜ κ²€μ¦μš© 섀정이닀. 운영 ν™˜κ²½μ„ κ°€μ •ν•œλ‹€λ©΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 도ꡬ와 ν”„λ‘œνŒŒμΌ 뢄리가 ν•„μš”ν•˜λ‹€.
- ADR μΌλΆ€μ˜ μ˜ˆμ „ `Jdbc*Repository` λͺ…칭은 ν˜„μž¬ JPA μ–΄λŒ‘ν„° λͺ…μΉ­κ³Ό λ§žμ§€ μ•Šμ„ 수 μžˆλ‹€. μƒˆ ADR을 μž‘μ„±ν•  λ•ŒλŠ” `Jpa*Repository`/`SpringData*Repository` κΈ°μ€€μœΌλ‘œ κΈ°λ‘ν•œλ‹€.

## 5. ν…ŒμŠ€νŠΈ μ „λž΅

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

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
Loading