Thanks to visit codestin.com
Credit goes to github.com

Skip to content

devJin11/liveclass-assignment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

16 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

과제 A - μˆ˜κ°• μ‹ μ²­ μ‹œμŠ€ν…œ

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

λ³Έ ν”„λ‘œμ νŠΈλŠ” ν”„λ‘œλ•νŠΈ μ—”μ§€λ‹ˆμ–΄ μ±„μš© 과제 쀑 BE-A. μˆ˜κ°• μ‹ μ²­ μ‹œμŠ€ν…œμ„ κ΅¬ν˜„ν•œ λ°±μ—”λ“œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μž…λ‹ˆλ‹€.

크리에이터가 κ°•μ˜λ₯Ό κ°œμ„€ν•˜κ³ , ν΄λž˜μŠ€λ©”μ΄νŠΈκ°€ κ°•μ˜μ— μˆ˜κ°• μ‹ μ²­ν•œ λ’€ 결제 확정을 톡해 μˆ˜κ°•μ„ ν™•μ •ν•˜λŠ” 흐름을 μ œκ³΅ν•©λ‹ˆλ‹€.

κ°•μ˜ μƒνƒœ 전이, μˆ˜κ°• μ‹ μ²­ μƒνƒœ 전이, 정원 초과 λ°©μ§€, λ™μ‹œμ„± μ œμ–΄μ™€ 같은 λΉ„μ¦ˆλ‹ˆμŠ€ κ·œμΉ™μ„ Spring Boot 기반 REST API둜 κ΅¬ν˜„ν•˜λŠ” 것을 λͺ©ν‘œλ‘œ ν•©λ‹ˆλ‹€.

μ£Όμš” κΈ°λŠ₯은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • κ°•μ˜ 생성, μˆ˜μ •, λͺ¨μ§‘ μ‹œμž‘, λͺ¨μ§‘ 마감
  • κ°•μ˜ λͺ©λ‘ 쑰회 및 상세 쑰회
  • κ°•μ˜ μƒνƒœ 전이 관리
    • DRAFT
    • OPEN
    • CLOSED
  • μˆ˜κ°• μ‹ μ²­ 생성, 결제 ν™•μ •, μˆ˜κ°• μ·¨μ†Œ
  • μˆ˜κ°• μ‹ μ²­ μƒνƒœ 전이 관리
    • PENDING
    • CONFIRMED
    • CANCELLED
  • 결제 λŒ€κΈ° μƒνƒœ μžλ™ μ·¨μ†Œ(μŠ€μΌ€μ€„λŸ¬)
  • 결제 ν™•μ • μ‹œμ μ— μˆ˜κ°• ν™•μ • 처리
  • κ°•μ˜ 정원 초과 λ°©μ§€
  • λ™μ‹œ μ‹ μ²­ μƒν™©μ—μ„œ 데이터 μ •ν•©μ„± 보μž₯
  • κ°•μ˜λ³„ μˆ˜κ°•μƒ λͺ©λ‘ 쑰회(크리에이터 μ „μš©)
  • λ‚΄ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ νŽ˜μ΄μ§€λ„€μ΄μ…˜
  • Redis 기반 λŒ€κΈ°μ—΄ 관리(μ‹œκ°„ λ‚¨μœΌλ©΄ κ΅¬ν˜„ μ˜ˆμ •)
  • Docker Compose 기반 μ‹€ν–‰ ν™˜κ²½ 제곡
  • ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό ν†΅ν•œ 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ κ·œμΉ™ 검증

기술 μŠ€νƒ

Backend

  • Java 21
  • Spring Boot 3.5.14
  • Spring Data JPA
  • JPA / Hibernate
  • Bean Validation
  • SpringDoc OpenAPI
  • Flyway
  • MySQL 8.4
  • Gradle

Infra / Runtime

  • Docker
  • Docker Compose

Test

  • JUnit 5
  • Spring Boot Test
  • Spring MVC Test / MockMvc
  • Spring Data JPA Test
  • AssertJ
  • Mockito
  • Testcontainers
  • MySQL 8.4 기반 Repository / Integration / Concurrency Test

μ‹€ν–‰ 방법(κ²€ν† μž λΉ λ₯Έ μ‹€ν–‰ κ°€μ΄λ“œ)

λ³Έ ν”„λ‘œμ νŠΈλŠ” Docker Composeλ₯Ό 톡해 Spring Boot μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό MySQL을 ν•¨κ»˜ μ‹€ν–‰ν•  수 μžˆλ„λ‘ κ΅¬μ„±ν–ˆμŠ΅λ‹ˆλ‹€. Docker Desktop을 μ‚¬μš©ν•˜λŠ” 경우 별도 μ„€μΉ˜ 없이 μ‹€ν–‰ κ°€λŠ₯ν•©λ‹ˆλ‹€.

1. Repository clone

git clone https://github.com/devJin11/liveclass-assignment.git
cd liveclass-assignment

2. Docker Compose 기반 μ‹€ν–‰ ν™˜κ²½

ν‰κ°€μžκ°€ λ³„λ„μ˜ 둜컬 Java, Gradle, MySQL μ„€μΉ˜ 없이 μ‹€ν–‰ν•  수 μžˆλ„λ‘ Docker Compose 기반 μ‹€ν–‰ ν™˜κ²½μ„ μ œκ³΅ν•©λ‹ˆλ‹€.

docker compose up --build

λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ‹€ν–‰ν•˜λ €λ©΄ λ‹€μŒ λͺ…λ Ήμ–΄λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

docker compose up --build -d

3. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 접속

http://localhost:8080

4. MySQL 접속 정보

Docker Compose λ‚΄λΆ€μ—μ„œ Spring Boot μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ λ‹€μŒ μ£Όμ†Œλ‘œ MySQL에 μ—°κ²°ν•©λ‹ˆλ‹€.

jdbc:mysql://mysql:3306/enrollment_db

둜컬 PCμ—μ„œ DataGrip, DBeaver, MySQL Workbench λ“±μœΌλ‘œ 직접 접속할 κ²½μš°μ—λŠ” λ‹€μŒ 정보λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

Host: localhost
Port: 13306
Database: enrollment_db
Username: enrollment_user
Password: enrollment_password
Root Password: root_password

5. Swagger UI 접속 μ£Όμ†Œ

http://localhost:8080/swagger-ui/index.html

6. ν…ŒμŠ€νŠΈ μ‹€ν–‰

ν…ŒμŠ€νŠΈλŠ” JUnit 5, Spring Boot Test, MockMvc, AssertJ, Mockito, Testcontainers, MySQL 8.4 기반으둜 κ΅¬μ„±λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ μ‹€ν–‰ μ „ Docker Desktop이 μ‹€ν–‰ 쀑이어야 ν•©λ‹ˆλ‹€.

Linux / macOS ν™˜κ²½:

./gradlew clean test

Windows PowerShell ν™˜κ²½:

.\gradlew.bat clean test

정상 톡과 μ‹œ λ‹€μŒκ³Ό 같은 κ²°κ³Όκ°€ 좜λ ₯λ©λ‹ˆλ‹€.

BUILD SUCCESSFUL

7. μ’…λ£Œ

docker compose down

DB λ°μ΄ν„°κΉŒμ§€ ν•¨κ»˜ μ‚­μ œν•˜λ €λ©΄ λ‹€μŒ λͺ…λ Ήμ–΄λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

docker compose down -v

둜컬 개발 ν™˜κ²½

제좜 및 μ‹€ν–‰ 검증은 Docker Compose κΈ°μ€€μœΌλ‘œ κ΅¬μ„±ν–ˆμŠ΅λ‹ˆλ‹€.

λ‹€λ§Œ 둜컬 개발 μ‹œμ—λŠ” λ‹€μŒ λ°©μ‹μœΌλ‘œ κ°œλ°œν•˜μ˜€μŠ΅λ‹ˆλ‹€.

  • Spring Boot μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ IntelliJμ—μ„œ 직접 μ‹€ν–‰
  • MySQL 8.4λŠ” Docker μ»¨ν…Œμ΄λ„ˆλ‘œ 단독 μ‹€ν–‰
  • Spring profile은 local μ‚¬μš©

둜컬 개발용 μ„€μ • νŒŒμΌμ€ λ‹€μŒ νŒŒμΌμ„ μ‚¬μš©ν•©λ‹ˆλ‹€.

src/main/resources/application-local.yml

ν•΄λ‹Ή νŒŒμΌμ€ 개인 둜컬 ν™˜κ²½ μ„€μ •μ΄λ―€λ‘œ Git에 ν¬ν•¨ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.


μš”κ΅¬μ‚¬ν•­ 해석 및 κ°€μ •

λ³Έ ν”„λ‘œμ νŠΈλŠ” μš”κ΅¬μ‚¬ν•­μ—μ„œ λͺ…μ‹œλœ κ°•μ˜ 생성, μˆ˜κ°• μ‹ μ²­, 결제 ν™•μ •, μ·¨μ†Œ, 정원 관리 κΈ°λŠ₯을 기반으둜 μ‹€μ œ μ„œλΉ„μŠ€μ—μ„œ λ°œμƒν•  수 μžˆλŠ” μƒνƒœ 전이, μ’Œμ„ 점유, λ™μ‹œμ„± μ œμ–΄, 결제 λŒ€κΈ° 만료, 크리에이터 κΆŒν•œ λ²”μœ„, λŒ€κΈ°μ—΄ 정책을 κ³ λ €ν•˜μ—¬ λ‹€μŒκ³Ό 같이 μš”κ΅¬μ‚¬ν•­μ„ ν•΄μ„ν–ˆμŠ΅λ‹ˆλ‹€.


1. 인증/인가

과제 μš”κ΅¬μ‚¬ν•­μ—μ„œ 인증/μΈκ°€λŠ” κ°„λž΅νžˆ 처리 κ°€λŠ₯ν•˜λ‹€κ³  μ•ˆλ‚΄λ˜μ–΄ μžˆμœΌλ―€λ‘œ, λ³Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” Spring Security 기반 둜그인/인증을 κ΅¬ν˜„ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

λŒ€μ‹  API μš”μ²­ μ‹œ λ‹€μŒ μ‹λ³„μžλ₯Ό μš”μ²­ λ°”λ”” λ˜λŠ” μš”μ²­ νŒŒλΌλ―Έν„°λ‘œ 전달받아 κΆŒν•œμ„ λ‹¨μˆœ κ²€μ¦ν•©λ‹ˆλ‹€.

  • 크리에이터 κΆŒν•œ API: creatorId
  • ν΄λž˜μŠ€λ©”μ΄νŠΈ κΆŒν•œ API: classmateId

예λ₯Ό λ“€μ–΄ κ°•μ˜ μˆ˜μ •, κ°•μ˜ λͺ¨μ§‘ μ‹œμž‘/마감, κ°•μ˜λ³„ ν™•μ • μˆ˜κ°•μƒ λͺ©λ‘ μ‘°νšŒλŠ” μš”μ²­ν•œ creatorIdκ°€ ν•΄λ‹Ή κ°•μ˜μ˜ 크리에이터인지 κ²€μ¦ν•©λ‹ˆλ‹€.

μˆ˜κ°• μ‹ μ²­ 상세 쑰회, 결제 ν™•μ •, μˆ˜κ°• μ·¨μ†ŒλŠ” μš”μ²­ν•œ classmateIdκ°€ ν•΄λ‹Ή μ‹ μ²­μ˜ μ†Œμœ μžμΈμ§€ κ²€μ¦ν•©λ‹ˆλ‹€.

곡톡 APIμ—μ„œλŠ” role νŒŒλΌλ―Έν„°λ₯Ό λ°›μ•„ κ²€μ¦ν•©λ‹ˆλ‹€.

role = CREATOR
role = CLASSMATE

2. Creator / Classmate 관리

λ³Έ 과제의 핡심은 νšŒμ› 관리가 μ•„λ‹ˆλΌ κ°•μ˜μ™€ μˆ˜κ°• μ‹ μ²­ νλ¦„μ΄λ―€λ‘œ, creator, classmate에 λŒ€ν•œ CRUD APIλŠ” λ³„λ„λ‘œ κ΅¬ν˜„ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

λ‹€λ§Œ DB 정합성을 μœ„ν•΄ class_room, enrollmentμ—μ„œ FK둜 μ°Έμ‘°ν•˜λ―€λ‘œ 엔티티와 RepositoryλŠ” μœ μ§€ν•©λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ 및 API 검증을 μœ„ν•΄ Flyway seed SQL둜 초기 데이터λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.


3. κ°•μ˜ 생성

ν¬λ¦¬μ—μ΄ν„°λŠ” κ°•μ˜λ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.

κ°•μ˜ 생성 μ‹œ λ‹€μŒ 정보λ₯Ό μž…λ ₯ν•©λ‹ˆλ‹€.

  • κ°•μ˜ 제λͺ©
  • κ°•μ˜ μ„€λͺ…
  • 가격
  • μ΅œλŒ€ μˆ˜κ°• 정원
  • μˆ˜κ°• μ‹œμž‘μΌ
  • μˆ˜κ°• μ’…λ£ŒμΌ

κ°•μ˜λŠ” 졜초 생성 μ‹œ 항상 DRAFT μƒνƒœλ‘œ μƒμ„±λ©λ‹ˆλ‹€.

졜초 κ°•μ˜ μƒνƒœ = DRAFT

DRAFT μƒνƒœλŠ” μ΄ˆμ•ˆ μƒνƒœμ΄λ―€λ‘œ μˆ˜κ°•μƒμ€ ν•΄λ‹Ή κ°•μ˜μ— μˆ˜κ°• μ‹ μ²­ν•  수 μ—†μŠ΅λ‹ˆλ‹€.


4. κ°•μ˜ μƒνƒœ

κ°•μ˜λŠ” λ‹€μŒ μƒνƒœλ₯Ό κ°€μ§‘λ‹ˆλ‹€.

μƒνƒœ μ„€λͺ… μˆ˜κ°• μ‹ μ²­ κ°€λŠ₯ μ—¬λΆ€
DRAFT μ΄ˆμ•ˆ λΆˆκ°€
OPEN λͺ¨μ§‘ 쀑 κ°€λŠ₯
CLOSED λͺ¨μ§‘ 마감 λΆˆκ°€

ν—ˆμš©λ˜λŠ” κ°•μ˜ μƒνƒœ μ „μ΄λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

DRAFT -> OPEN
OPEN -> CLOSED

ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” μƒνƒœ μ „μ΄λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

DRAFT -> CLOSED
OPEN -> DRAFT
CLOSED -> OPEN
CLOSED -> DRAFT

ν¬λ¦¬μ—μ΄ν„°λŠ” DRAFT μƒνƒœμ˜ κ°•μ˜λ₯Ό OPEN μƒνƒœλ‘œ λ³€κ²½ν•˜μ—¬ μˆ˜κ°• 신청을 받을 수 μžˆμŠ΅λ‹ˆλ‹€.

λ˜ν•œ 정원이 λͺ¨λ‘ μ°¨μ§€ μ•Šμ•˜λ”λΌλ„ ν¬λ¦¬μ—μ΄ν„°λŠ” 운영 νŒλ‹¨μ— 따라 OPEN μƒνƒœμ˜ κ°•μ˜λ₯Ό CLOSED μƒνƒœλ‘œ λ³€κ²½ν•˜μ—¬ λͺ¨μ§‘을 λ§ˆκ°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


5. κ°•μ˜ μˆ˜μ • μ •μ±…

κ°•μ˜ μˆ˜μ •μ€ DRAFT μƒνƒœμ—μ„œλ§Œ ν—ˆμš©ν•©λ‹ˆλ‹€.

OPEN λ˜λŠ” CLOSED μƒνƒœμ˜ κ°•μ˜λŠ” μˆ˜μ •ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

μ΄λ ‡κ²Œ μ„€κ³„ν•œ μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • 이미 λͺ¨μ§‘ 쀑인 κ°•μ˜μ˜ 가격, κΈ°κ°„, 정원이 λ³€κ²½λ˜λ©΄ κΈ°μ‘΄ μ‹ μ²­μžμ—κ²Œ 영ν–₯을 쀄 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λͺ¨μ§‘ 마감 이후 κ°•μ˜ 정보가 λ³€κ²½λ˜λ©΄ μ‹ μ²­ λ‹Ήμ‹œμ˜ 정보와 μ‹€μ œ κ°•μ˜ 정보 μ‚¬μ΄μ˜ 정합성이 깨질 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ³Έ κ³Όμ œμ—μ„œλŠ” λ³€κ²½ 이λ ₯ κ΄€λ¦¬κΉŒμ§€ μš”κ΅¬ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ, 데이터 μ •ν•©μ„±κ³Ό κ΅¬ν˜„ λͺ…확성을 μœ„ν•΄ DRAFT μƒνƒœμ—μ„œλ§Œ μˆ˜μ • κ°€λŠ₯ν•˜λ„λ‘ μ œν•œν–ˆμŠ΅λ‹ˆλ‹€.

싀무 μ„œλΉ„μŠ€μ—μ„œλŠ” κ°•μ˜ μ†Œκ°œκΈ€, μ•ˆλ‚΄ 문ꡬ, 썸넀일 λ“± 일뢀 비핡심 ν•„λ“œλ§Œ OPEN μƒνƒœμ—μ„œλ„ μˆ˜μ • κ°€λŠ₯ν•˜κ²Œ 섀계할 수 μžˆμ§€λ§Œ, λ³Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” 핡심 μš”κ΅¬μ‚¬ν•­ κ΅¬ν˜„μ— μ§‘μ€‘ν•˜κΈ° μœ„ν•΄ μˆ˜μ • κ°€λŠ₯ μƒνƒœλ₯Ό DRAFT둜 μ œν•œν–ˆμŠ΅λ‹ˆλ‹€.


6. κ°•μ˜ λͺ©λ‘ 쑰회 μ •μ±…

κ°•μ˜ λͺ©λ‘ μ‘°νšŒλŠ” 크리에이터와 μˆ˜κ°•μƒ λͺ¨λ‘ μ‚¬μš©ν•  수 μžˆλŠ” 곡톡 API둜 μ œκ³΅ν•©λ‹ˆλ‹€.

λ‹€λ§Œ 역할에 따라 쑰회 κ°€λŠ₯ν•œ κ°•μ˜ μƒνƒœλ₯Ό λ‹€λ₯΄κ²Œ μ œν•œν•©λ‹ˆλ‹€.

크리에이터

ν¬λ¦¬μ—μ΄ν„°λŠ” 본인이 κ°œμ„€ν•œ κ°•μ˜λ₯Ό 관리해야 ν•˜λ―€λ‘œ λ‹€μŒ μƒνƒœλ₯Ό λͺ¨λ‘ ν•„ν„°λ§ν•˜μ—¬ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

DRAFT
OPEN
CLOSED

ν¬λ¦¬μ—μ΄ν„°μ˜ κ°•μ˜ λͺ©λ‘ μ‘°νšŒλŠ” 본인이 μƒμ„±ν•œ κ°•μ˜λ₯Ό κΈ°μ€€μœΌλ‘œ ν•©λ‹ˆλ‹€.

classRoom.creatorId == μš”μ²­ creatorId

λ”°λΌμ„œ ν¬λ¦¬μ—μ΄ν„°λŠ” μžμ‹ μ΄ μƒμ„±ν•œ μ΄ˆμ•ˆ κ°•μ˜, λͺ¨μ§‘ 쀑 κ°•μ˜, λͺ¨μ§‘ 마감 κ°•μ˜λ₯Ό λͺ¨λ‘ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μˆ˜κ°•μƒ

μˆ˜κ°•μƒμ€ 곡개 κ°€λŠ₯ν•œ κ°•μ˜λ§Œ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μˆ˜κ°•μƒμ΄ 필터링할 수 μžˆλŠ” μƒνƒœλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

OPEN
CLOSED

DRAFT μƒνƒœλŠ” 아직 곡개 μ „ μ΄ˆμ•ˆμ΄λ―€λ‘œ μˆ˜κ°•μƒ λͺ©λ‘ 쑰회 λŒ€μƒμ—μ„œ μ œμ™Έν•©λ‹ˆλ‹€.

μˆ˜κ°•μƒ κ΄€μ μ—μ„œ 각 μƒνƒœμ˜ μ˜λ―ΈλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

μƒνƒœ μ„€λͺ…
OPEN ν˜„μž¬ μˆ˜κ°• μ‹ μ²­ κ°€λŠ₯ν•œ κ°•μ˜
CLOSED λͺ¨μ§‘이 λ§ˆκ°λ˜μ–΄ 신청은 λΆˆκ°€λŠ₯ν•˜μ§€λ§Œ 쑰회 κ°€λŠ₯ν•œ κ°•μ˜

μˆ˜κ°•μƒμ΄ DRAFT μƒνƒœλ‘œ 필터링을 μš”μ²­ν•˜λ©΄ 잘λͺ»λœ μš”μ²­μœΌλ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.


7. κ°•μ˜ 상세 쑰회 μ •μ±…

κ°•μ˜ 상세 μ‘°νšŒλ„ 크리에이터와 μˆ˜κ°•μƒ λͺ¨λ‘ μ‚¬μš©ν•  수 μžˆλŠ” 곡톡 API둜 μ œκ³΅ν•©λ‹ˆλ‹€.

λ‹€λ§Œ 상세 쑰회 μ—­μ‹œ 역할에 따라 μ ‘κ·Ό κ°€λŠ₯ν•œ μƒνƒœλ₯Ό λ‹€λ₯΄κ²Œ μ œν•œν•©λ‹ˆλ‹€.

크리에이터

ν¬λ¦¬μ—μ΄ν„°λŠ” 본인이 κ°œμ„€ν•œ κ°•μ˜λΌλ©΄ λ‹€μŒ μƒνƒœλ₯Ό λͺ¨λ‘ 상세 μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

DRAFT
OPEN
CLOSED

단, λ‹€λ₯Έ 크리에이터가 μƒμ„±ν•œ κ°•μ˜μ˜ DRAFT 상세 μ •λ³΄λŠ” μ‘°νšŒν•  수 μ—†μŠ΅λ‹ˆλ‹€.

μˆ˜κ°•μƒ

μˆ˜κ°•μƒμ€ λ‹€μŒ μƒνƒœμ˜ κ°•μ˜λ§Œ 상세 μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

OPEN
CLOSED

DRAFT μƒνƒœμ˜ κ°•μ˜λŠ” 곡개 μ „ μ΄ˆμ•ˆμ΄λ―€λ‘œ μˆ˜κ°•μƒμ—κ²Œ λ…ΈμΆœν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

상세 쑰회 μ‘λ‹΅μ—λŠ” ν˜„μž¬ μ‹ μ²­ 인원을 ν¬ν•¨ν•©λ‹ˆλ‹€.

ν˜„μž¬ μ‹ μ²­ 인원 = PENDING μƒνƒœ μ‹ μ²­ 수 + CONFIRMED μƒνƒœ μ‹ μ²­ 수

8. μˆ˜κ°• μ‹ μ²­κ³Ό μˆ˜κ°• ν™•μ •μ˜ 뢄리

λ³Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” μˆ˜κ°• μ‹ μ²­κ³Ό μˆ˜κ°• 확정을 μ„œλ‘œ λ‹€λ₯Έ λΉ„μ¦ˆλ‹ˆμŠ€ νλ¦„μœΌλ‘œ ν•΄μ„ν–ˆμŠ΅λ‹ˆλ‹€.

μˆ˜κ°• μ‹ μ²­ = μ’Œμ„μ„ ν™•λ³΄ν•˜κ³  결제λ₯Ό λŒ€κΈ°ν•˜λŠ” ν–‰μœ„
μˆ˜κ°• ν™•μ • = 결제 μ™„λ£Œ ν›„ μˆ˜κ°• μƒνƒœλ₯Ό ν™•μ •ν•˜λŠ” ν–‰μœ„

μˆ˜κ°• 신청이 μ„±κ³΅ν•˜λ©΄ Enrollmentκ°€ PENDING μƒνƒœλ‘œ μƒμ„±λ©λ‹ˆλ‹€.

PENDING μƒνƒœλŠ” λ‹¨μˆœ μž„μ‹œ 데이터가 μ•„λ‹ˆλΌ, μ’Œμ„μ„ μ μœ ν•œ 결제 λŒ€κΈ° μƒνƒœλ‘œ κ°„μ£Όν•©λ‹ˆλ‹€.

결제 ν™•μ • APIκ°€ 호좜되면 PENDING μƒνƒœμ˜ 신청이 CONFIRMED μƒνƒœλ‘œ λ³€κ²½λ©λ‹ˆλ‹€.

μ™ΈλΆ€ 결제 μ‹œμŠ€ν…œ 연동은 과제 μš”κ΅¬μ‚¬ν•­μ— ν¬ν•¨λ˜μ–΄ μžˆμ§€ μ•ŠμœΌλ―€λ‘œ, λ³Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” 결제 ν™•μ • APIλ₯Ό 톡해 λ‹¨μˆœ μƒνƒœ λ³€κ²½μœΌλ‘œ λŒ€μ²΄ν•©λ‹ˆλ‹€.


9. μˆ˜κ°• μ‹ μ²­ μƒνƒœ

μˆ˜κ°• 신청은 λ‹€μŒ μƒνƒœλ₯Ό κ°€μ§‘λ‹ˆλ‹€.

μƒνƒœ μ„€λͺ… μ’Œμ„ 점유 μ—¬λΆ€
PENDING μ‹ μ²­ μ™„λ£Œ, 결제 λŒ€κΈ° 점유
CONFIRMED 결제 μ™„λ£Œ, μˆ˜κ°• ν™•μ • 점유
CANCELLED μ·¨μ†Œλ¨ 미점유

κΈ°λ³Έ μƒνƒœ μ „μ΄λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

PENDING -> CONFIRMED
PENDING -> CANCELLED
CONFIRMED -> CANCELLED
CANCELLED -> PENDING

CANCELLED -> PENDING μ „μ΄λŠ” 일반적인 볡ꡬ가 μ•„λ‹ˆλΌ, μ‚¬μš©μžμ˜ λͺ…μ‹œμ μΈ μž¬μ‹ μ²­ μš”μ²­μ— μ˜ν•΄μ„œλ§Œ ν—ˆμš©λ©λ‹ˆλ‹€.

각 μƒνƒœμ˜ μ˜λ―ΈλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • PENDING: μˆ˜κ°• 신청은 μ™„λ£Œλ˜μ—ˆμ§€λ§Œ 아직 결제 ν™•μ • 전인 μƒνƒœμž…λ‹ˆλ‹€. 이 μƒνƒœμ—μ„œλ„ μ’Œμ„μ€ μ μœ ν•©λ‹ˆλ‹€.
  • CONFIRMED: κ²°μ œκ°€ μ™„λ£Œλ˜μ–΄ μ΅œμ’… μˆ˜κ°•μ΄ ν™•μ •λœ μƒνƒœμž…λ‹ˆλ‹€.
  • CANCELLED: 신청이 μ·¨μ†Œλ˜μ–΄ μ’Œμ„μ„ μ μœ ν•˜μ§€ μ•ŠλŠ” μƒνƒœμž…λ‹ˆλ‹€.

10. 정원 관리 κΈ°μ€€

κ°•μ˜μ˜ ν˜„μž¬ μ‹ μ²­ 인원은 μ’Œμ„μ„ μ μœ ν•˜λŠ” μ‹ μ²­ μƒνƒœλ₯Ό κΈ°μ€€μœΌλ‘œ κ³„μ‚°ν•©λ‹ˆλ‹€.

enrollment_countλŠ” μ’Œμ„μ„ μ μœ ν•˜λŠ” μ‹ μ²­ 수λ₯Ό μ˜λ―Έν•œλ‹€.
즉, PENDING + CONFIRMED μƒνƒœμ˜ Enrollment μˆ˜μ™€ 동일해야 ν•œλ‹€.
ν˜„μž¬ μ‹ μ²­ 인원(enrollment_count) = PENDING μƒνƒœ μ‹ μ²­ 수 + CONFIRMED μƒνƒœ ν™•μ • 수

CANCELLED μƒνƒœλŠ” μ’Œμ„μ„ μ μœ ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ 정원 κ³„μ‚°μ—μ„œ μ œμ™Έν•©λ‹ˆλ‹€.

정원이 초과된 경우 일반 μˆ˜κ°• 신청은 μ‹€νŒ¨ν•©λ‹ˆλ‹€.


11. μˆ˜κ°• μ‹ μ²­ λ™μ‹œμ„± μ œμ–΄(쑰건뢀 μ›μžμ  UPDATE μ‚¬μš©)

μˆ˜κ°• 신청은 λ™μ‹œμ— μ—¬λŸ¬ μ‚¬μš©μžκ°€ μš”μ²­ν•  수 μžˆμœΌλ―€λ‘œ, μ’Œμ„μ— λŒ€ν•΄ 경쟁 쑰건이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ 정원이 10λͺ…이고 ν˜„μž¬ μ‹ μ²­ 인원이 9λͺ…인 μƒν™©μ—μ„œ 두 μ‚¬μš©μžκ°€ λ™μ‹œμ— μ‹ μ²­ν•˜λ©΄, λ‹¨μˆœ 쑰회 ν›„ μ €μž₯ λ°©μ‹μœΌλ‘œλŠ” 11λͺ…이 μ‹ μ²­λ˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μˆ˜κ°• μ‹ μ²­ μ‹œ λ‹€μŒ λ°©μ‹μœΌλ‘œ λ™μ‹œμ„±μ„ μ œμ–΄ν•©λ‹ˆλ‹€.

enrollment_count = PENDING μƒνƒœ μ‹ μ²­ 수 + CONFIRMED μƒνƒœ μ‹ μ²­ 수
데이터 μ •ν•©μ„±κ³Ό λ™μ‹œμ„± μ œμ–΄λ₯Ό μœ„ν•΄ μˆ˜κ°• μ‹ μ²­ μ‹œ class_room rowλ₯Ό μ›μžμ μœΌλ‘œ 쑰건뢀 UPDATEν•©λ‹ˆλ‹€.

UPDATE class_room
SET enrollment_count = enrollment_count + 1
WHERE class_room_id = ?
  AND status = 'OPEN'
  AND enrollment_count < capacity;

μˆ˜κ°• μ‹ μ²­ 처리 흐름은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

1. μˆ˜κ°• μ‹ μ²­ μš”μ²­
2. ν΄λž˜μŠ€λ©”μ΄νŠΈ 쑴재 μ—¬λΆ€ 확인
3. class_room.enrollment_count 쑰건뢀 증가 μ‹œλ„
4. 증가 μ‹€νŒ¨ μ‹œ κ°•μ˜ μ—†μŒ, λͺ¨μ§‘ 쀑 μ•„λ‹˜, 정원 초과 쀑 ν•˜λ‚˜λ‘œ μ˜ˆμ™Έ 처리
5. 동일 κ°•μ˜/동일 ν΄λž˜μŠ€λ©”μ΄νŠΈ Enrollmentλ₯Ό FOR UPDATE둜 쑰회
6. κΈ°μ‘΄ 신청이 PENDING λ˜λŠ” CONFIRMED이면 쀑볡 μ‹ μ²­ μ˜ˆμ™Έ
7. κΈ°μ‘΄ 신청이 CANCELLED이면 κΈ°μ‘΄ rowλ₯Ό PENDING μƒνƒœλ‘œ μž¬ν™œμ„±ν™”
8. κΈ°μ‘΄ 신청이 μ—†μœΌλ©΄ μ‹ κ·œ Enrollment 생성
9. νŠΈλžœμž­μ…˜ 컀밋

λ™μ‹œ μš”μ²­ μƒν™©μ—μ„œ λͺ¨λ“  νŠΈλžœμž­μ…˜μ΄ λ¨Όμ € class_room row에 λŒ€ν•΄ 쑰건뢀 UPDATEλ₯Ό μˆ˜ν–‰ν•˜λ„λ‘ 락 νšλ“ μˆœμ„œλ₯Ό ν†΅μΌν–ˆμŠ΅λ‹ˆλ‹€.

이λ₯Ό 톡해 enrollment 쑰회 잠금과 class_room κ°±μ‹  잠금이 κ΅μ°¨ν•˜λ©΄μ„œ λ°œμƒν•  수 μžˆλŠ” MySQL InnoDB λ°λ“œλ½ κ°€λŠ₯성을 쀄이고, λ™μ‹œμ— μ—¬λŸ¬ μš”μ²­μ΄ 듀어와도 μ΅œμ’… μ’Œμ„ 점유 μˆ˜κ°€ 정원을 μ΄ˆκ³Όν•˜μ§€ μ•Šλ„λ‘ 보μž₯ν•©λ‹ˆλ‹€.

쀑볡 μ‹ μ²­ λ“±μœΌλ‘œ λΉ„μ¦ˆλ‹ˆμŠ€ μ˜ˆμ™Έκ°€ λ°œμƒν•˜λŠ” 경우 전체 νŠΈλžœμž­μ…˜μ΄ rollbackλ˜λ―€λ‘œ, μ•žμ„œ μˆ˜ν–‰λœ enrollment_count 증가도 ν•¨κ»˜ rollbackλ©λ‹ˆλ‹€.


12. 결제 λŒ€κΈ° 만료 μ •μ±…

PENDING μƒνƒœλŠ” μ’Œμ„μ„ μ μœ ν•˜λŠ” μƒνƒœμž…λ‹ˆλ‹€.

λ”°λΌμ„œ μ‚¬μš©μžκ°€ μˆ˜κ°• μ‹ μ²­λ§Œ ν•˜κ³  결제λ₯Ό μ™„λ£Œν•˜μ§€ μ•ŠμœΌλ©΄ μ’Œμ„μ΄ 계속 μ μœ λ˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ λ³Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” 결제 λŒ€κΈ° 만료 정책을 μ μš©ν•©λ‹ˆλ‹€.

μˆ˜κ°• μ‹ μ²­ ν›„ 10λΆ„ 이내 결제 확정이 λ˜μ§€ μ•ŠμœΌλ©΄ μžλ™ μ·¨μ†Œν•©λ‹ˆλ‹€.

μˆ˜κ°• 신청이 생성될 λ•Œ 결제 만료 μ‹œκ°μ„ ν•¨κ»˜ μ €μž₯ν•©λ‹ˆλ‹€.

paymentExpiredAt = enrollmentCreatedAt + 10λΆ„

1λΆ„ λ‹¨μœ„ μŠ€μΌ€μ€„λŸ¬κ°€ 만료된 PENDING 신청을 μ‘°νšŒν•˜μ—¬ CANCELLED μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.

μ‹€ν–‰ μ£ΌκΈ°: 1λΆ„
λŒ€μƒ: status = PENDING and paymentExpiredAt <= now
처리: PENDING -> CANCELLED

μžλ™ μ·¨μ†Œλœ 신청은 μ’Œμ„μ„ 더 이상 μ μœ ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ enrollmentCountκ°€ κ°μ†Œν•©λ‹ˆλ‹€.

운영 ν™˜κ²½μ—μ„œ μ—¬λŸ¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μΈμŠ€ν„΄μŠ€κ°€ λ™μ‹œμ— μ‹€ν–‰λ˜λŠ” κ²½μš°μ—λŠ” μŠ€μΌ€μ€„λŸ¬ 쀑볡 싀행을 막기 μœ„ν•΄ λΆ„μ‚° 락이 ν•„μš”ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ³Έ κ³Όμ œμ—μ„œλŠ” 단일 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μΈμŠ€ν„΄μŠ€ 싀행을 κΈ°μ€€μœΌλ‘œ κ΅¬ν˜„ν•©λ‹ˆλ‹€.


13. 결제 ν™•μ •

μˆ˜κ°•μƒμ€ PENDING μƒνƒœμ˜ μˆ˜κ°• 신청을 결제 ν™•μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

결제 ν™•μ • μ‹œ μƒνƒœλŠ” λ‹€μŒκ³Ό 같이 λ³€κ²½λ©λ‹ˆλ‹€.

PENDING -> CONFIRMED

결제 ν™•μ • μ‹œ 검증 κ·œμΉ™μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μ‹ μ²­ 내역이 μ‘΄μž¬ν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • μš”μ²­ν•œ μˆ˜κ°•μƒμ΄ ν•΄λ‹Ή μ‹ μ²­μ˜ μ†Œμœ μžμ—¬μ•Ό ν•©λ‹ˆλ‹€.
  • μ‹ μ²­ μƒνƒœκ°€ PENDING이어야 ν•©λ‹ˆλ‹€.
  • 결제 λŒ€κΈ° 만료 μ‹œκ°„μ΄ μ§€λ‚˜μ§€ μ•Šμ•„μ•Ό ν•©λ‹ˆλ‹€.
  • 이미 CONFIRMED λ˜λŠ” CANCELLED μƒνƒœμΈ 신청은 결제 ν™•μ •ν•  수 μ—†μŠ΅λ‹ˆλ‹€.
  • 결제 확정은 paymentExpiredAt μ΄μ „κΉŒμ§€λ§Œ κ°€λŠ₯ν•˜λ©°, paymentExpiredAt μ‹œκ°μ΄ λ„λž˜ν•˜λ©΄ 만료된 κ²ƒμœΌλ‘œ κ°„μ£Όν•©λ‹ˆλ‹€.

결제 ν™•μ • μ‹œμ μ—λŠ” 정원 검사λ₯Ό λ‹€μ‹œ μˆ˜ν–‰ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

κ·Έ μ΄μœ λŠ” μˆ˜κ°• μ‹ μ²­ μ‹œμ μ— 이미 PENDING μƒνƒœλ‘œ μ’Œμ„μ„ μ μœ ν–ˆκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.


14. μˆ˜κ°• μ·¨μ†Œ

μˆ˜κ°•μƒμ€ 본인의 μˆ˜κ°• 신청을 μ·¨μ†Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ·¨μ†Œ 정책은 μ‹ μ²­ μƒνƒœμ— 따라 λ‹€λ₯΄κ²Œ μ μš©ν•©λ‹ˆλ‹€.

PENDING μƒνƒœ μ·¨μ†Œ

PENDING μƒνƒœλŠ” 결제 μ „ μƒνƒœμ΄λ―€λ‘œ μ–Έμ œλ“  μ·¨μ†Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

PENDING -> CANCELLED

μ‚¬μš©μžκ°€ 직접 μ·¨μ†Œν•˜μ§€ μ•Šλ”λΌλ„ 결제 λŒ€κΈ° μ‹œκ°„μ΄ 10뢄을 μ΄ˆκ³Όν•˜λ©΄ μŠ€μΌ€μ€„λŸ¬μ— μ˜ν•΄ μžλ™ μ·¨μ†Œλ©λ‹ˆλ‹€.

CONFIRMED μƒνƒœ μ·¨μ†Œ

CONFIRMED μƒνƒœλŠ” 결제 μ™„λ£Œ μƒνƒœμ΄λ―€λ‘œ 결제 ν™•μ • ν›„ 7일 μ΄λ‚΄μ—λ§Œ μ·¨μ†Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

CONFIRMED -> CANCELLED

μ·¨μ†Œ κ°€λŠ₯ 기간은 λ‹€μŒ κΈ°μ€€μœΌλ‘œ νŒλ‹¨ν•©λ‹ˆλ‹€.

ν˜„μž¬ μ‹œκ° <= confirmedAt + 7일

결제 ν™•μ • ν›„ 7일이 μ§€λ‚œ 신청은 μ·¨μ†Œν•  수 μ—†μŠ΅λ‹ˆλ‹€.

CANCELLED μƒνƒœ μ·¨μ†Œ

이미 CANCELLED μƒνƒœμΈ 신청은 λ‹€μ‹œ μ·¨μ†Œν•  수 μ—†μŠ΅λ‹ˆλ‹€.


15. μž¬μ‹ μ²­ μ •μ±…

λ™μΌν•œ μˆ˜κ°•μƒμ€ λ™μΌν•œ κ°•μ˜μ— λŒ€ν•΄ λ™μ‹œμ— μ—¬λŸ¬ 개의 ν™œμ„± 신청을 κ°€μ§ˆ 수 μ—†μŠ΅λ‹ˆλ‹€.

ν™œμ„± 신청은 λ‹€μŒ μƒνƒœλ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.

PENDING
CONFIRMED

λ”°λΌμ„œ 동일 μˆ˜κ°•μƒμ΄ 같은 κ°•μ˜μ— λŒ€ν•΄ PENDING λ˜λŠ” CONFIRMED μƒνƒœμ˜ 신청을 이미 κ°€μ§€κ³  μžˆλ‹€λ©΄ 쀑볡 신청은 λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.

λ‹€λ§Œ κΈ°μ‘΄ 신청이 CANCELLED μƒνƒœλΌλ©΄ μ’Œμ„μ„ μ μœ ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ, 정원이 남아 μžˆλŠ” 경우 λ‹€μ‹œ μ‹ μ²­ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

κ΅¬ν˜„ λ‹¨μˆœν™”λ₯Ό μœ„ν•΄ 동일 κ°•μ˜μ™€ 동일 μˆ˜κ°•μƒ 쑰합에 λŒ€ν•΄μ„œλŠ” ν•˜λ‚˜μ˜ Enrollment rowλ₯Ό μœ μ§€ν•˜κ³ , κΈ°μ‘΄ μƒνƒœκ°€ CANCELLED인 경우 ν•΄λ‹Ή rowλ₯Ό λ‹€μ‹œ PENDING μƒνƒœλ‘œ λ³€κ²½ν•˜λŠ” λ°©μ‹μœΌλ‘œ μž¬μ‹ μ²­μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

CANCELLED -> PENDING

이 μ „μ΄λŠ” 일반적인 μƒνƒœ 볡ꡬ가 μ•„λ‹ˆλΌ, μ‚¬μš©μžμ˜ λͺ…μ‹œμ μΈ μž¬μ‹ μ²­ μš”μ²­μ— μ˜ν•΄μ„œλ§Œ ν—ˆμš©λ©λ‹ˆλ‹€.


17. ν¬λ¦¬μ—μ΄ν„°μ˜ κ°•μ˜λ³„ μˆ˜κ°•μƒ λͺ©λ‘ 쑰회

ν¬λ¦¬μ—μ΄ν„°λŠ” 본인이 κ°œμ„€ν•œ κ°•μ˜μ˜ μˆ˜κ°•μƒ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

단, λ‹€λ₯Έ 크리에이터가 κ°œμ„€ν•œ κ°•μ˜μ˜ μˆ˜κ°•μƒ λͺ©λ‘μ€ μ‘°νšŒν•  수 μ—†μŠ΅λ‹ˆλ‹€.

검증 κ·œμΉ™μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

classRoom.creatorId == μš”μ²­ creatorId

μΌμΉ˜ν•˜μ§€ μ•ŠλŠ” 경우 FORBIDDEN μ˜ˆμ™Έλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

μˆ˜κ°•μƒ λͺ©λ‘ 쑰회 λŒ€μƒμ€ 기본적으둜 PENDING, CONFIRMED, CANCELLED μƒνƒœλ₯Ό λͺ¨λ‘ 포함할 수 μžˆμŠ΅λ‹ˆλ‹€.

λ‹€λ§Œ μ‹€μ œ μˆ˜κ°• ν™•μ •μžλ§Œ 보고 싢은 경우λ₯Ό μœ„ν•΄ status ν•„ν„°λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

μ˜ˆμ‹œ:

GET /api/class-rooms/{classRoomId}/enrollments?creatorId=1&status=CONFIRMED&page=0&size=20

18. λ‚΄ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ 쑰회

μˆ˜κ°•μƒμ€ 본인의 μˆ˜κ°• μ‹ μ²­ λͺ©λ‘μ„ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

GET /api/enrollments/me?classmateId=1&page=0&size=20

쑰회 κ²°κ³Όμ—λŠ” λ‹€μŒ μƒνƒœμ˜ 신청이 포함될 수 μžˆμŠ΅λ‹ˆλ‹€.

  • PENDING
  • CONFIRMED
  • CANCELLED

μ‘λ‹΅μ—λŠ” μ‹ μ²­ν•œ κ°•μ˜ 정보와 μ‹ μ²­ μƒνƒœλ₯Ό ν•¨κ»˜ μ œκ³΅ν•©λ‹ˆλ‹€.


19. νŽ˜μ΄μ§€λ„€μ΄μ…˜ μ •μ±…

λͺ¨λ“  λͺ©λ‘ 쑰회 APIλŠ” νŽ˜μ΄μ§€ 번호 기반 νŽ˜μ΄μ§€λ„€μ΄μ…˜μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.

적용 λŒ€μƒμ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • κ°•μ˜ λͺ©λ‘ 쑰회
  • λ‚΄ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ 쑰회
  • κ°•μ˜λ³„ μˆ˜κ°•μƒ λͺ©λ‘ 쑰회

μš”μ²­ νŒŒλΌλ―Έν„°λŠ” λ‹€μŒ ν˜•μ‹μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.

page=0&size=20

기본값은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

page = 0
size = 20

μ΅œλŒ€ sizeλŠ” κ³Όλ„ν•œ 쑰회λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μ œν•œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

max size = 50

섀계 κ²°μ •κ³Ό 이유

1. Class λŒ€μ‹  ClassRoom 도메인λͺ… μ‚¬μš©

Javaμ—μ„œ ClassλŠ” 이미 μ‘΄μž¬ν•˜λŠ” νƒ€μž… 이름이며 도메인 클래슀λͺ…μœΌλ‘œ μ‚¬μš©ν•˜κΈ° λΆ€μ μ ˆν•©λ‹ˆλ‹€.

λ”°λΌμ„œ λ³Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” κ°•μ˜λ₯Ό μ˜λ―Έν•˜λŠ” 도메인λͺ…μœΌλ‘œ ClassRoom을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.


2. DRAFT κ°•μ˜λŠ” μˆ˜κ°•μƒμ—κ²Œ λ…ΈμΆœν•˜μ§€ μ•ŠμŒ

DRAFT μƒνƒœλŠ” 크리에이터가 아직 κ³΅κ°œν•˜μ§€ μ•Šμ€ μ΄ˆμ•ˆμž…λ‹ˆλ‹€.

λ”°λΌμ„œ μˆ˜κ°•μƒμ€ DRAFT μƒνƒœμ˜ κ°•μ˜λ₯Ό λͺ©λ‘ μ‘°νšŒν•˜κ±°λ‚˜ 상세 μ‘°νšŒν•  수 μ—†μŠ΅λ‹ˆλ‹€.

ν¬λ¦¬μ—μ΄ν„°λŠ” 본인이 κ°œμ„€ν•œ κ°•μ˜λ₯Ό 관리해야 ν•˜λ―€λ‘œ DRAFT, OPEN, CLOSED μƒνƒœλ₯Ό λͺ¨λ‘ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.


3. PENDING을 μ’Œμ„ 점유 μƒνƒœλ‘œ 해석

λ³Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” PENDING μƒνƒœλ₯Ό λ‹¨μˆœ μž„μ‹œ 신청이 μ•„λ‹ˆλΌ μ’Œμ„μ„ μ μœ ν•œ 결제 λŒ€κΈ° μƒνƒœλ‘œ ν•΄μ„ν–ˆμŠ΅λ‹ˆλ‹€.

μ΄λ ‡κ²Œ μ„€κ³„ν•œ μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μ‚¬μš©μžκ°€ μˆ˜κ°• 신청에 μ„±κ³΅ν–ˆλ‹€λ©΄ μ’Œμ„μ€ ν™•λ³΄λ˜μ—ˆλ‹€κ³  λ³΄λŠ” 것이 μžμ—°μŠ€λŸ½μŠ΅λ‹ˆλ‹€.
  • PENDING을 정원에 ν¬ν•¨ν•˜μ§€ μ•ŠμœΌλ©΄ 정원보닀 λ§Žμ€ μ‚¬μš©μžκ°€ 결제 λŒ€κΈ° μƒνƒœκ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 결제 ν™•μ • μ‹œμ μ— 정원 초과둜 μ‹€νŒ¨ν•˜λŠ” 흐름은 μ‚¬μš©μž κ²½ν—˜μƒ λΆ€μžμ—°μŠ€λŸ½μŠ΅λ‹ˆλ‹€.
  • λ™μ‹œμ„± μ œμ–΄ 지점을 μˆ˜κ°• μ‹ μ²­ μ‹œμ μœΌλ‘œ μ§‘μ€‘μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

4. 정원 초과 λ°©μ§€λ₯Ό μœ„ν•œ 쑰건뢀 μ›μžμ  UPDATE μ‚¬μš©

μˆ˜κ°• 신청은 λ™μ‹œμ— μ—¬λŸ¬ μ‚¬μš©μžκ°€ μš”μ²­ν•  수 μžˆλŠ” κΈ°λŠ₯μž…λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ 정원이 10λͺ…이고 ν˜„μž¬ μ’Œμ„ 점유 μˆ˜κ°€ 9λͺ…인 μƒν™©μ—μ„œ 두 μ‚¬μš©μžκ°€ λ™μ‹œμ— μ‹ μ²­ν•˜λ©΄, λ‹¨μˆœνžˆ ν˜„μž¬ μ‹ μ²­ 수λ₯Ό μ‘°νšŒν•œ λ’€ Enrollmentλ₯Ό μ €μž₯ν•˜λŠ” λ°©μ‹μœΌλ‘œλŠ” 두 μš”μ²­μ΄ λͺ¨λ‘ μ„±κ³΅ν•˜μ—¬ 정원을 μ΄ˆκ³Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ³Έ ν”„λ‘œμ νŠΈμ—μ„œλŠ” 이λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ class_room ν…Œμ΄λΈ”μ— enrollment_count μ»¬λŸΌμ„ 두고, ν•΄λ‹Ή 값을 ν˜„μž¬ μ’Œμ„ 점유 수둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.

enrollment_count = PENDING μƒνƒœ μ‹ μ²­ 수 + CONFIRMED μƒνƒœ μ‹ μ²­ 수

PENDING은 결제 λŒ€κΈ° μƒνƒœμ΄μ§€λ§Œ 이미 μ’Œμ„μ„ ν™•λ³΄ν•œ μƒνƒœλ‘œ ν•΄μ„ν•˜λ―€λ‘œ 정원 계산에 ν¬ν•¨ν•©λ‹ˆλ‹€. CANCELLED μƒνƒœλŠ” μ’Œμ„μ„ μ μœ ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ 정원 κ³„μ‚°μ—μ„œ μ œμ™Έν•©λ‹ˆλ‹€.

μˆ˜κ°• μ‹ μ²­ μ‹œμ—λŠ” λ‹€μŒκ³Ό 같은 쑰건뢀 UPDATEλ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.

UPDATE class_room
SET enrollment_count = enrollment_count + 1
WHERE class_room_id = ?
  AND status = 'OPEN'
  AND enrollment_count < capacity;

이 UPDATEλŠ” DB row λ‹¨μœ„λ‘œ μ›μžμ μœΌλ‘œ μ‹€ν–‰λ©λ‹ˆλ‹€.

λ”°λΌμ„œ λ™μ‹œμ— μ—¬λŸ¬ μ‚¬μš©μžκ°€ λ§ˆμ§€λ§‰ μ’Œμ„μ„ μ‹ μ²­ν•˜λ”λΌλ„ enrollment_count < capacity 쑰건을 λ§Œμ‘±ν•œ μš”μ²­λ§Œ μ„±κ³΅ν•©λ‹ˆλ‹€.

처리 κ²°κ³ΌλŠ” affected row 수둜 νŒλ‹¨ν•©λ‹ˆλ‹€.


5. PENDING μžλ™ μ·¨μ†Œ μŠ€μΌ€μ€„λŸ¬ λ„μž…

PENDING μƒνƒœκ°€ μ’Œμ„μ„ μ μœ ν•˜κΈ° λ•Œλ¬Έμ— 결제λ₯Ό μ™„λ£Œν•˜μ§€ μ•Šμ€ 신청이 계속 남아 있으면 λ‹€λ₯Έ μ‚¬μš©μžκ°€ μ‹ μ²­ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

이λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ 결제 λŒ€κΈ° μ‹œκ°„μ„ 10λΆ„μœΌλ‘œ μ œν•œν•˜κ³ , 1λΆ„ λ‹¨μœ„ μŠ€μΌ€μ€„λŸ¬κ°€ 만료된 PENDING 신청을 μžλ™μœΌλ‘œ CANCELLED μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.


6. 쑰회 APIλŠ” DTO Projection μ‚¬μš©

λͺ©λ‘ μ‘°νšŒμ™€ 상세 μ‘°νšŒλŠ” μ—”ν‹°ν‹°λ₯Ό 직접 λ°˜ν™˜ν•˜μ§€ μ•Šκ³  DTO Projection으둜 μ‘°νšŒν•©λ‹ˆλ‹€.

μ΄λ ‡κ²Œ μ„€κ³„ν•œ μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • 응닡에 ν•„μš”ν•œ ν•„λ“œλ§Œ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λΆˆν•„μš”ν•œ μ—”ν‹°ν‹° λ‘œλ”©μ„ 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.
  • Lazy Loading으둜 μΈν•œ N+1 문제λ₯Ό λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • API 응닡 ꡬ쑰와 μ—”ν‹°ν‹° ꡬ쑰λ₯Ό 뢄리할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ¬Όλ‘ , λ³Έ κ³Όμ œμ—μ„œλŠ” λ‹¨μˆœν•œ 응닡 ν•„λ“œ ꡬ쑰둜, Lazy Loading으둜 μΈν•œ N+1 문제 상황은 λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

κΈ°λŠ₯ μš”κ΅¬μ‚¬ν•­ λͺ…μ„Έ

κ°•μ˜ 관리

κΈ°λŠ₯ μ„€λͺ…
κ°•μ˜ 생성 크리에이터가 κ°•μ˜λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. 생성 μ‹œ μƒνƒœλŠ” DRAFTμž…λ‹ˆλ‹€.
κ°•μ˜ μˆ˜μ • DRAFT μƒνƒœμ—μ„œλ§Œ κ°•μ˜ 정보λ₯Ό μˆ˜μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
κ°•μ˜ λͺ¨μ§‘ μ‹œμž‘ 크리에이터가 DRAFT μƒνƒœμ˜ κ°•μ˜λ₯Ό OPEN μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.
κ°•μ˜ λͺ¨μ§‘ 마감 크리에이터가 OPEN μƒνƒœμ˜ κ°•μ˜λ₯Ό CLOSED μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.
κ°•μ˜ λͺ©λ‘ 쑰회 역할별 μƒνƒœ 필터와 νŽ˜μ΄μ§€λ„€μ΄μ…˜μ„ μ§€μ›ν•©λ‹ˆλ‹€.
κ°•μ˜ 상세 쑰회 ν˜„μž¬ μ‹ μ²­ 인원을 ν¬ν•¨ν•œ κ°•μ˜ 상세 정보λ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€.

μˆ˜κ°• μ‹ μ²­ 관리

κΈ°λŠ₯ μ„€λͺ…
μˆ˜κ°• μ‹ μ²­ OPEN μƒνƒœμ˜ κ°•μ˜μ— μ‹ μ²­ν•©λ‹ˆλ‹€. 성곡 μ‹œ PENDING μƒνƒœκ°€ λ©λ‹ˆλ‹€.
결제 ν™•μ • PENDING μƒνƒœμ˜ 신청을 CONFIRMED μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.
μˆ˜κ°• μ·¨μ†Œ PENDING λ˜λŠ” CONFIRMED μƒνƒœμ˜ 신청을 CANCELLED μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.
μžλ™ μ·¨μ†Œ 결제 λŒ€κΈ° μ‹œκ°„μ΄ 10뢄을 μ΄ˆκ³Όν•œ PENDING 신청을 μŠ€μΌ€μ€„λŸ¬κ°€ μžλ™ μ·¨μ†Œν•©λ‹ˆλ‹€.
λ‚΄ μ‹ μ²­ λͺ©λ‘ 쑰회 μˆ˜κ°•μƒ 본인의 μ‹ μ²­ λͺ©λ‘μ„ νŽ˜μ΄μ§€λ„€μ΄μ…˜μœΌλ‘œ μ‘°νšŒν•©λ‹ˆλ‹€.

API λͺ©λ‘ 및 μ˜ˆμ‹œ

API 전체 λͺ©λ‘

κ°•μ˜ API

κΈ°λŠ₯ Method URL μ„€λͺ…
κ°•μ˜ 등둝 POST /api/class-rooms 크리에이터가 κ°•μ˜λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. μƒμ„±λœ κ°•μ˜λŠ” DRAFT μƒνƒœμž…λ‹ˆλ‹€.
κ°•μ˜ μˆ˜μ • PUT /api/class-rooms/{classRoomId} DRAFT μƒνƒœμ˜ κ°•μ˜λ§Œ μˆ˜μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
κ°•μ˜ λͺ¨μ§‘ μ‹œμž‘ PATCH /api/class-rooms/{classRoomId}/open DRAFT μƒνƒœμ˜ κ°•μ˜λ₯Ό OPEN μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.
κ°•μ˜ λͺ¨μ§‘ 마감 PATCH /api/class-rooms/{classRoomId}/close OPEN μƒνƒœμ˜ κ°•μ˜λ₯Ό CLOSED μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.
κ°•μ˜ λͺ©λ‘ 쑰회 GET /api/class-rooms μ—­ν• λ³„λ‘œ 쑰회 κ°€λŠ₯ν•œ κ°•μ˜ λͺ©λ‘μ„ νŽ˜μ΄μ§€λ„€μ΄μ…˜μœΌλ‘œ μ‘°νšŒν•©λ‹ˆλ‹€.
κ°•μ˜ 상세 쑰회 GET /api/class-rooms/{classRoomId} κ°•μ˜ 상세 정보와 ν˜„μž¬ μ‹ μ²­ 인원을 μ‘°νšŒν•©λ‹ˆλ‹€.
κ°•μ˜λ³„ μˆ˜κ°•μƒ λͺ©λ‘ 쑰회 GET /api/class-rooms/{classRoomId}/enrollments 크리에이터가 본인 κ°•μ˜μ˜ μˆ˜κ°•μƒ λͺ©λ‘μ„ μ‘°νšŒν•©λ‹ˆλ‹€.

μˆ˜κ°• μ‹ μ²­ API

κΈ°λŠ₯ Method URL μ„€λͺ…
μˆ˜κ°• μ‹ μ²­ POST /api/class-rooms/{classRoomId}/enrollments μˆ˜κ°•μƒμ΄ OPEN μƒνƒœμ˜ κ°•μ˜μ— μ‹ μ²­ν•©λ‹ˆλ‹€. 성곡 μ‹œ PENDING μƒνƒœκ°€ λ©λ‹ˆλ‹€.
결제 ν™•μ • PATCH /api/enrollments/{enrollmentId}/confirm PENDING μƒνƒœμ˜ 신청을 CONFIRMED μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.
μˆ˜κ°• μ·¨μ†Œ PATCH /api/enrollments/{enrollmentId}/cancel PENDING λ˜λŠ” CONFIRMED μƒνƒœμ˜ 신청을 CANCELLED μƒνƒœλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€.
λ‚΄ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ 쑰회 GET /api/enrollments/me μˆ˜κ°•μƒ 본인의 μ‹ μ²­ λͺ©λ‘μ„ νŽ˜μ΄μ§€λ„€μ΄μ…˜μœΌλ‘œ μ‘°νšŒν•©λ‹ˆλ‹€.
μˆ˜κ°• μ‹ μ²­ 상세 쑰회 GET /api/enrollments/{enrollmentId} μˆ˜κ°•μƒ 본인의 μ‹ μ²­ 상세 정보λ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€.

1. κ°•μ˜ 등둝

Request

POST /api/class-rooms
Content-Type: application/json
{
  "creatorId": 1,
  "title": "Spring Boot μž…λ¬Έ",
  "description": "Spring Boot 기반 REST API 개발 κ°•μ˜",
  "price": 50000,
  "capacity": 30,
  "startAt": "2026-05-01T00:00:00",
  "endAt": "2026-06-01T00:00:00"
}

Response

201 Created
Location: /api/class-rooms/1
body μ—†μŒ.

2. κ°•μ˜ μˆ˜μ •

Request

PUT /api/class-rooms/{classRoomId}
Content-Type: application/json
{
  "creatorId": 1,
  "title": "Spring Boot μ‹€μ „ μž…λ¬Έ",
  "description": "Spring Boot 기반 REST API μ‹€μ „ κ°•μ˜",
  "price": 60000,
  "capacity": 25,
  "startAt": "2026-05-01T00:00:00",
  "endAt": "2026-06-01T00:00:00"
}

Response

204 No Content

3. κ°•μ˜ λͺ¨μ§‘ μ‹œμž‘

Request

PATCH /api/class-rooms/{classRoomId}/open
Content-Type: application/json
{
  "creatorId": 1
}

Response

204 No Content

4. κ°•μ˜ λͺ¨μ§‘ 마감

Request

PATCH /api/class-rooms/{classRoomId}/close
Content-Type: application/json
{
  "creatorId": 1
}

Response

204 No Content

5. 크리에이터 κ°•μ˜ λͺ©λ‘ 쑰회

ν¬λ¦¬μ—μ΄ν„°λŠ” 본인이 μƒμ„±ν•œ κ°•μ˜λ₯Ό DRAFT, OPEN, CLOSED μƒνƒœλ‘œ ν•„ν„°λ§ν•˜μ—¬ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Request

GET /api/class-rooms?role=CREATOR&creatorId=1&status=DRAFT&page=0&size=20

Response

{
  "content": [
    {
      "classRoomId": 1,
      "creatorId": 1,
      "creatorName": "creator1",
      "title": "Spring Boot μž…λ¬Έ",
      "price": 50000,
      "capacity": 30,
      "enrollmentCount": 0,
      "status": "DRAFT",
      "startAt": "2026-05-01T00:00:00",
      "endAt": "2026-06-01T00:00:00"
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 1,
  "totalPages": 1
}

6. μˆ˜κ°•μƒ κ°•μ˜ λͺ©λ‘ 쑰회

μˆ˜κ°•μƒμ€ OPEN, CLOSED μƒνƒœμ˜ κ°•μ˜λ§Œ ν•„ν„°λ§ν•˜μ—¬ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Request

GET /api/class-rooms?role=CLASSMATE&status=OPEN&page=0&size=20

Response

{
  "content": [
    {
      "classRoomId": 1,
      "creatorId": 1,
      "creatorName": "creator1",
      "title": "Spring Boot μž…λ¬Έ",
      "price": 50000,
      "capacity": 30,
      "enrollmentCount": 0,
      "status": "DRAFT",
      "startAt": "2026-05-01T00:00:00",
      "endAt": "2026-06-01T00:00:00"
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 1,
  "totalPages": 1
}

7. κ°•μ˜ 상세 쑰회

Request

GET /api/class-rooms/1?role=CREATOR&creatorId=1

λ˜λŠ” μˆ˜κ°•μƒ 쑰회:

GET /api/class-rooms/1?role=CLASSMATE

Response

{
  "classRoomId": 1,
  "creatorId": 1,
  "creatorName": "creator_1",
  "title": "Spring Boot μž…λ¬Έ",
  "description": "Spring Boot 기반 REST API 개발 κ°•μ˜",
  "price": 51000,
  "capacity": 30,
  "enrollmentCount": 0,
  "status": "CLOSED",
  "startAt": "2026-05-01T00:00:00",
  "endAt": "2026-06-01T00:00:00",
  "createdAt": "2026-04-28T04:20:13",
  "updatedAt": "2026-04-28T04:25:21"
}

8. μˆ˜κ°• μ‹ μ²­

Request

POST /api/class-rooms/1/enrollments
Content-Type: application/json
{
  "classmateId": 10
}

Response

201 Created
Location: /api/enrollments/1
{
  "enrollmentId": 2,
  "classRoomId": 2,
  "classmateId": 9,
  "status": "PENDING",
  "paymentExpiredAt": "2026-04-28T04:56:32",
  "confirmedAt": null,
  "cancelledAt": null,
  "createdAt": "2026-04-28T04:46:32"
}

9. 결제 ν™•μ •

Request

PATCH /api/enrollments/1/confirm
Content-Type: application/json
{
  "classmateId": 10
}

Response

{
  "enrollmentId": 2,
  "classRoomId": 2,
  "classmateId": 9,
  "status": "CONFIRMED",
  "paymentExpiredAt": "2026-04-28T04:56:32",
  "confirmedAt": "2026-04-28T04:47:28",
  "cancelledAt": null,
  "createdAt": "2026-04-28T04:46:32"
}

10. μˆ˜κ°• μ·¨μ†Œ

Request

PATCH /api/enrollments/1/cancel
Content-Type: application/json
{
  "classmateId": 10
}

Response

{
  "enrollmentId": 2,
  "classRoomId": 2,
  "classmateId": 9,
  "status": "CANCELLED",
  "paymentExpiredAt": "2026-04-28T04:56:32",
  "confirmedAt": "2026-04-28T04:47:28",
  "cancelledAt": "2026-04-28T04:48:10",
  "createdAt": "2026-04-28T04:46:32"
}

11. λ‚΄ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ 쑰회

Request

GET /api/enrollments/me?classmateId=10&page=0&size=20

Response

{
  "content": [
    {
      "enrollmentId": 1,
      "classRoomId": 1,
      "classRoomTitle": "Spring Boot μž…λ¬Έ",
      "price": 50000,
      "status": "CONFIRMED",
      "createdAt": "2026-04-26T10:00:00",
      "confirmedAt": "2026-04-26T10:05:00",
      "cancelledAt": null
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 1,
  "totalPages": 1
}

12. κ°•μ˜λ³„ μˆ˜κ°•μƒ λͺ©λ‘ 쑰회

ν¬λ¦¬μ—μ΄ν„°λŠ” 본인이 κ°œμ„€ν•œ κ°•μ˜μ˜ μˆ˜κ°•μƒ λͺ©λ‘λ§Œ μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Request

GET /api/class-rooms/2/enrollments?creatorId=1&status=CANCELLED&page=0&size=20

Response

{
  "content": [
    {
      "enrollmentId": 2,
      "classmateId": 9,
      "classmateName": "classmate_9",
      "status": "CANCELLED",
      "paymentExpiredAt": "2026-04-28T04:56:32",
      "confirmedAt": "2026-04-28T04:47:28",
      "cancelledAt": "2026-04-28T04:48:10",
      "createdAt": "2026-04-28T04:46:32"
    },
    {
      "enrollmentId": 1,
      "classmateId": 10,
      "classmateName": "classmate_10",
      "status": "CANCELLED",
      "paymentExpiredAt": "2026-04-28T04:48:06",
      "confirmedAt": null,
      "cancelledAt": "2026-04-28T04:49:00",
      "createdAt": "2026-04-28T04:38:06"
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 2,
  "totalPages": 1
}


데이터 λͺ¨λΈ μ„€λͺ…

1. DB μŠ€ν‚€λ§ˆ, DDL

DB μŠ€ν‚€λ§ˆμ™€ DDL은 λ³Έ ν”„λ‘œμ νŠΈμ˜ resources/db/migration/ *.sql νŒŒμΌμ„ μ œκ³΅ν•˜μ—¬ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

2. ERD

ERD cloud URL

λ‹€μŒ URL을 톡해 ERDλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

https://www.erdcloud.com/d/39WesCmjRoB5w2Gpf

λ³Έ ν”„λ‘œμ νŠΈλŠ” μˆ˜κ°• μ‹ μ²­ μ‹œμŠ€ν…œμ˜ 핡심 도메인을 λ‹€μŒ 4개 μ—”ν‹°ν‹°λ‘œ κ΅¬μ„±ν•©λ‹ˆλ‹€.

  • Creator: κ°•μ˜λ₯Ό κ°œμ„€ν•˜λŠ” 크리에이터
  • Classmate: κ°•μ˜μ— μˆ˜κ°• μ‹ μ²­ν•˜λŠ” ν΄λž˜μŠ€λ©”μ΄νŠΈ
  • ClassRoom: 크리에이터가 κ°œμ„€ν•œ κ°•μ˜
  • Enrollment: ν΄λž˜μŠ€λ©”μ΄νŠΈμ˜ μˆ˜κ°• μ‹ μ²­ λ‚΄μ—­

Creator와 ClassmateλŠ” λ³Έ κ³Όμ œμ—μ„œ νšŒμ›κ°€μž…/둜그인 κΈ°λŠ₯의 λŒ€μƒμ΄ μ•„λ‹ˆλ©°, class_room, enrollment의 FK 참쑰와 ν…ŒμŠ€νŠΈ 데이터 ꡬ성을 μœ„ν•΄ μ‚¬μš©ν•©λ‹ˆλ‹€.


Creator

크리에이터 정보λ₯Ό μ €μž₯ν•˜λŠ” μ—”ν‹°ν‹°μž…λ‹ˆλ‹€.

λ³Έ κ³Όμ œμ—μ„œλŠ” 크리에이터 CRUD APIλ₯Ό λ³„λ„λ‘œ μ œκ³΅ν•˜μ§€ μ•Šκ³ , Flyway seed 데이터λ₯Ό 톡해 초기 데이터λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

ν•„λ“œ νƒ€μž… μ„€λͺ…
id Long 크리에이터 ID
name String 크리에이터 이름
email String 크리에이터 이메일
status CreatorStatus 크리에이터 μƒνƒœ
createdAt LocalDateTime 생성 μ‹œκ°„
updatedAt LocalDateTime μˆ˜μ • μ‹œκ°„

크리에이터 μƒνƒœλŠ” λ‹€μŒ enum으둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.

public enum CreatorStatus {
  ACTIVE,
  DELETED
}

Classmate

ν΄λž˜μŠ€λ©”μ΄νŠΈ 정보λ₯Ό μ €μž₯ν•˜λŠ” μ—”ν‹°ν‹°μž…λ‹ˆλ‹€.

λ³Έ κ³Όμ œμ—μ„œλŠ” ν΄λž˜μŠ€λ©”μ΄νŠΈ CRUD APIλ₯Ό λ³„λ„λ‘œ μ œκ³΅ν•˜μ§€ μ•Šκ³ , Flyway seed 데이터λ₯Ό 톡해 초기 데이터λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

ν•„λ“œ νƒ€μž… μ„€λͺ…
id Long ν΄λž˜μŠ€λ©”μ΄νŠΈ ID
name String ν΄λž˜μŠ€λ©”μ΄νŠΈ 이름
email String ν΄λž˜μŠ€λ©”μ΄νŠΈ 이메일
status ClassmateStatus ν΄λž˜μŠ€λ©”μ΄νŠΈ μƒνƒœ
createdAt LocalDateTime 생성 μ‹œκ°„
updatedAt LocalDateTime μˆ˜μ • μ‹œκ°„

ν΄λž˜μŠ€λ©”μ΄νŠΈ μƒνƒœλŠ” λ‹€μŒ enum으둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.

public enum ClassmateStatus {
  ACTIVE,
  DELETED
}

ClassRoom

κ°•μ˜ 정보λ₯Ό μ €μž₯ν•˜λŠ” μ—”ν‹°ν‹°μž…λ‹ˆλ‹€.

크리에이터가 κ°•μ˜λ₯Ό μƒμ„±ν•˜λ©΄ 졜초 μƒνƒœλŠ” DRAFTκ°€ λ©λ‹ˆλ‹€. 이후 크리에이터가 λͺ¨μ§‘ μ‹œμž‘ APIλ₯Ό ν˜ΈμΆœν•˜λ©΄ OPEN μƒνƒœκ°€ 되며, OPEN μƒνƒœμ˜ κ°•μ˜μ—λ§Œ μˆ˜κ°• μ‹ μ²­ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν•„λ“œ νƒ€μž… μ„€λͺ…
id Long κ°•μ˜ ID
creator Creator κ°•μ˜λ₯Ό μƒμ„±ν•œ 크리에이터
title String κ°•μ˜ 제λͺ©
description String κ°•μ˜ μ„€λͺ…
price Long κ°•μ˜ 가격
capacity int μ΅œλŒ€ μˆ˜κ°• 정원
enrollmentCount int ν˜„μž¬ μ’Œμ„ 점유 수
status ClassRoomStatus κ°•μ˜ μƒνƒœ
startAt LocalDateTime μˆ˜κ°• μ‹œμž‘ μΌμ‹œ
endAt LocalDateTime μˆ˜κ°• μ’…λ£Œ μΌμ‹œ
createdAt LocalDateTime 생성 μ‹œκ°„
updatedAt LocalDateTime μˆ˜μ • μ‹œκ°„

κ°•μ˜ μƒνƒœλŠ” λ‹€μŒ enum으둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.

public enum ClassRoomStatus {
    DRAFT,
    OPEN,
    CLOSED
}

Enrollment

μˆ˜κ°• μ‹ μ²­ 정보λ₯Ό μ €μž₯ν•˜λŠ” μ—”ν‹°ν‹°μž…λ‹ˆλ‹€.

ν΄λž˜μŠ€λ©”μ΄νŠΈκ°€ κ°•μ˜μ— μˆ˜κ°• μ‹ μ²­ν•˜λ©΄ PENDING μƒνƒœμ˜ Enrollmentκ°€ μƒμ„±λ©λ‹ˆλ‹€. PENDING μƒνƒœλŠ” 결제 λŒ€κΈ° μƒνƒœμ΄μ§€λ§Œ μ’Œμ„μ„ μ μœ ν•©λ‹ˆλ‹€. 결제 ν™•μ • μ‹œ CONFIRMED μƒνƒœκ°€ 되고, μˆ˜κ°• μ·¨μ†Œ λ˜λŠ” 결제 λŒ€κΈ° 만료 μ‹œ CANCELLED μƒνƒœκ°€ λ©λ‹ˆλ‹€.

ν•„λ“œ νƒ€μž… μ„€λͺ…
id Long μˆ˜κ°• μ‹ μ²­ ID
classRoom ClassRoom μ‹ μ²­ν•œ κ°•μ˜
classmate Classmate μ‹ μ²­ν•œ ν΄λž˜μŠ€λ©”μ΄νŠΈ
status EnrollmentStatus μ‹ μ²­ μƒνƒœ
paymentExpiredAt LocalDateTime 결제 λŒ€κΈ° 만료 μ‹œκ°
confirmedAt LocalDateTime 결제 ν™•μ • μ‹œκ°
cancelledAt LocalDateTime μ·¨μ†Œ μ‹œκ°
cancelReason CancelReason μ·¨μ†Œ μ‚¬μœ 
createdAt LocalDateTime 생성 μ‹œκ°„
updatedAt LocalDateTime μˆ˜μ • μ‹œκ°„

μ‹ μ²­ μƒνƒœλŠ” λ‹€μŒ enum으둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.

public enum EnrollmentStatus {
    PENDING,
    CONFIRMED,
    CANCELLED
}

μ·¨μ†Œ μ‚¬μœ λŠ” λ‹€μŒ enum으둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€.

public enum CancelReason {
  USER_CANCELLED,
  PAYMENT_EXPIRED
}

μ£Όμš” μ œμ•½ 쑰건

동일 κ°•μ˜ 쀑볡 μ‹ μ²­ λ°©μ§€

λ™μΌν•œ ν΄λž˜μŠ€λ©”μ΄νŠΈλŠ” λ™μΌν•œ κ°•μ˜μ— λŒ€ν•΄ μ—¬λŸ¬ 개의 ν™œμ„± 신청을 κ°€μ§ˆ 수 μ—†μŠ΅λ‹ˆλ‹€.

ν™œμ„± 신청은 λ‹€μŒ μƒνƒœλ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.

PENDING
CONFIRMED

DBμ—μ„œλŠ” λ‹€μŒ unique μ œμ•½μœΌλ‘œ 동일 κ°•μ˜μ™€ 동일 ν΄λž˜μŠ€λ©”μ΄νŠΈ 쑰합을 ν•˜λ‚˜μ˜ row둜 μ œν•œν•©λ‹ˆλ‹€.

UNIQUE (class_room_id, classmate_id)

이 μ œμ•½μ— 따라 동일 ν΄λž˜μŠ€λ©”μ΄νŠΈκ°€ 같은 κ°•μ˜μ— λ‹€μ‹œ μ‹ μ²­ν•˜λŠ” κ²½μš°μ—λŠ” μƒˆ rowλ₯Ό μƒμ„±ν•˜μ§€ μ•Šκ³ , κΈ°μ‘΄ CANCELLED μƒνƒœμ˜ rowλ₯Ό PENDING μƒνƒœλ‘œ μž¬ν™œμ„±ν™”ν•©λ‹ˆλ‹€.


정원 초과 λ°©μ§€

ClassRoom.capacityλŠ” μ΅œλŒ€ μˆ˜κ°• 정원을 μ˜λ―Έν•©λ‹ˆλ‹€.

ClassRoom.enrollmentCountλŠ” ν˜„μž¬ μ’Œμ„ 점유 수λ₯Ό μ˜λ―Έν•˜λ©°, λ‹€μŒ 쑰건을 λ§Œμ‘±ν•΄μ•Ό ν•©λ‹ˆλ‹€.

0 <= enrollmentCount <= capacity

μˆ˜κ°• μ‹ μ²­ μ‹œ enrollmentCount < capacity 쑰건을 ν¬ν•¨ν•œ UPDATEλ₯Ό μ‚¬μš©ν•˜μ—¬ 정원을 μ΄ˆκ³Όν•˜μ§€ μ•Šλ„λ‘ 보μž₯ν•©λ‹ˆλ‹€.


결제 λŒ€κΈ° 만료

PENDING μƒνƒœλŠ” μ’Œμ„μ„ μ μœ ν•˜λ―€λ‘œ, κ²°μ œκ°€ μž₯μ‹œκ°„ μ™„λ£Œλ˜μ§€ μ•ŠμœΌλ©΄ λ‹€λ₯Έ μ‚¬μš©μžκ°€ μˆ˜κ°• μ‹ μ²­ν•  수 μ—†λŠ” λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μˆ˜κ°• μ‹ μ²­ 생성 μ‹œ 결제 만료 μ‹œκ°μ„ μ €μž₯ν•©λ‹ˆλ‹€.

paymentExpiredAt = μ‹ μ²­ μ‹œκ° + 10λΆ„

1λΆ„ λ‹¨μœ„ μŠ€μΌ€μ€„λŸ¬κ°€ λ‹€μŒ 쑰건의 신청을 μžλ™ μ·¨μ†Œν•©λ‹ˆλ‹€.

status = PENDING
paymentExpiredAt <= now

μžλ™ μ·¨μ†Œλœ 신청은 CANCELLED μƒνƒœκ°€ 되며, cancelReason은 PAYMENT_EXPIRED둜 μ €μž₯λ©λ‹ˆλ‹€.


μˆ˜κ°• μ·¨μ†Œ κ°€λŠ₯ κΈ°κ°„

PENDING μƒνƒœμ˜ 신청은 결제 μ „ μƒνƒœμ΄λ―€λ‘œ μ–Έμ œλ“  직접 μ·¨μ†Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

CONFIRMED μƒνƒœμ˜ 신청은 결제 ν™•μ • ν›„ 7일 μ΄λ‚΄μ—λ§Œ μ·¨μ†Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν˜„μž¬ μ‹œκ° <= confirmedAt + 7일

μ·¨μ†Œ κ°€λŠ₯ 기간이 μ§€λ‚œ CONFIRMED 신청은 μ·¨μ†Œν•  수 μ—†μŠ΅λ‹ˆλ‹€.


ν…Œμ΄λΈ” 관계 μš”μ•½

Creator 1 : N ClassRoom
ClassRoom 1 : N Enrollment
Classmate 1 : N Enrollment

관계 μ„€λͺ…은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • ν•˜λ‚˜μ˜ CreatorλŠ” μ—¬λŸ¬ 개의 ClassRoom을 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • ν•˜λ‚˜μ˜ ClassRoomμ—λŠ” μ—¬λŸ¬ 개의 Enrollmentκ°€ 생성될 수 μžˆμŠ΅λ‹ˆλ‹€.
  • ν•˜λ‚˜μ˜ ClassmateλŠ” μ—¬λŸ¬ κ°•μ˜μ— λŒ€ν•΄ Enrollmentλ₯Ό κ°€μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 단, λ™μΌν•œ ClassmateλŠ” λ™μΌν•œ ClassRoom에 λŒ€ν•΄ ν•˜λ‚˜μ˜ Enrollment row만 κ°€μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

μ£Όμš” 인덱슀

쑰회 μ„±λŠ₯κ³Ό μŠ€μΌ€μ€„λŸ¬ 처리λ₯Ό μœ„ν•΄ λ‹€μŒ 인덱슀λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

인덱슀 λͺ©μ 
uk_creator_email 크리에이터 이메일 쀑볡 λ°©μ§€
uk_classmate_email ν΄λž˜μŠ€λ©”μ΄νŠΈ 이메일 쀑볡 λ°©μ§€
idx_class_room_creator_status_created_at ν¬λ¦¬μ—μ΄ν„°μ˜ κ°•μ˜ λͺ©λ‘ 쑰회
idx_class_room_status_created_at μˆ˜κ°•μƒμ˜ 곡개 κ°•μ˜ λͺ©λ‘ 쑰회
uk_enrollment_class_room_classmate 동일 κ°•μ˜ 쀑볡 μ‹ μ²­ λ°©μ§€
idx_enrollment_classmate_created_at λ‚΄ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ 쑰회
idx_enrollment_class_room_status_created_at κ°•μ˜λ³„ μˆ˜κ°•μƒ λͺ©λ‘ 쑰회
idx_enrollment_status_payment_expired_at 결제 λŒ€κΈ° 만료 μŠ€μΌ€μ€„λŸ¬ 쑰회


ν…ŒμŠ€νŠΈ

λ³Έ ν”„λ‘œμ νŠΈλŠ” 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ κ·œμΉ™κ³Ό 데이터 정합성을 κ²€μ¦ν•˜κΈ° μœ„ν•΄ 계측별 ν…ŒμŠ€νŠΈλ₯Ό κ΅¬μ„±ν–ˆμŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈλŠ” λ‹¨μˆœ 성곡 μΌ€μ΄μŠ€λΏ μ•„λ‹ˆλΌ μƒνƒœ 전이 μ‹€νŒ¨, κΆŒν•œ 검증, Validation μ‹€νŒ¨, Repository Query 검증, MySQL 기반 λ™μ‹œμ„± μ œμ–΄κΉŒμ§€ ν¬ν•¨ν•©λ‹ˆλ‹€.


ν…ŒμŠ€νŠΈ μ‹€ν–‰ 방법

전체 ν…ŒμŠ€νŠΈλŠ” λ‹€μŒ λͺ…λ Ήμ–΄λ‘œ μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

./gradlew clean test

Windows ν™˜κ²½μ—μ„œλŠ” λ‹€μŒ λͺ…λ Ήμ–΄λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

gradlew.bat clean test

νŠΉμ • ν…ŒμŠ€νŠΈλ§Œ μ‹€ν–‰ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

./gradlew test --tests "*ControllerTest"
./gradlew test --tests "*RepositoryTest"
./gradlew test --tests "*IntegrationTest"
./gradlew test --tests "*ConcurrencyTest"

Windows ν™˜κ²½μ—μ„œλŠ” λ‹€μŒκ³Ό 같이 μ‹€ν–‰ν•©λ‹ˆλ‹€.

gradlew.bat test --tests "*ControllerTest"
gradlew.bat test --tests "*RepositoryTest"
gradlew.bat test --tests "*IntegrationTest"
gradlew.bat test --tests "*ConcurrencyTest"

ν…ŒμŠ€νŠΈ ν™˜κ²½

ν…ŒμŠ€νŠΈλŠ” μ‹€μ œ 운영 DB와 μœ μ‚¬ν•œ ν™˜κ²½μ—μ„œ κ²€μ¦ν•˜κΈ° μœ„ν•΄ H2κ°€ μ•„λ‹Œ MySQL 8.4 Testcontainersλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

Repository ν…ŒμŠ€νŠΈμ™€ Service 톡합 ν…ŒμŠ€νŠΈλŠ” 각각 λ…λ¦½λœ MySQL Testcontainerλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ ꡬ뢄 ν…ŒμŠ€νŠΈ ν™˜κ²½
Controller Test @WebMvcTest, MockMvc, Mock Service
Entity Test Spring Context 없이 JUnit 5, AssertJ, Mockito
Repository Test @DataJpaTest, MySQL Testcontainers
Service Integration Test @SpringBootTest, MySQL Testcontainers
Concurrency Test @SpringBootTest, MySQL Testcontainers, ExecutorService, CountDownLatch

Repository ν…ŒμŠ€νŠΈμ—μ„œλŠ” @AutoConfigureTestDatabase(replace = NONE) 섀정을 μ‚¬μš©ν•˜μ—¬ Spring Bootκ°€ ν…ŒμŠ€νŠΈ DBλ₯Ό H2둜 λŒ€μ²΄ν•˜μ§€ μ•Šλ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€.

λ˜ν•œ JPA Auditing ν•„λ“œμΈ createdAt, updatedAt이 Repository slice testμ—μ„œλ„ 정상 λ™μž‘ν•˜λ„λ‘ JpaAuditingConfigλ₯Ό ν…ŒμŠ€νŠΈ μ»¨ν…μŠ€νŠΈμ— λͺ…μ‹œμ μœΌλ‘œ importν–ˆμŠ΅λ‹ˆλ‹€.

TestcontainersλŠ” ν…ŒμŠ€νŠΈ 클래슀 묢음 μ‹€ν–‰ μ‹œ μ»¨ν…Œμ΄λ„ˆ 수λͺ…주기와 Spring TestContext μΊμ‹œκ°€ μΆ©λŒν•˜μ§€ μ•Šλ„λ‘ singleton container λ°©μ‹μœΌλ‘œ κ΅¬μ„±ν–ˆμŠ΅λ‹ˆλ‹€.

ν…ŒμŠ€νŠΈ κ°„ 데이터 간섭을 λ°©μ§€ν•˜κΈ° μœ„ν•΄ 각 ν…ŒμŠ€νŠΈ μ‹€ν–‰ μ „ λ‹€μŒ ν…Œμ΄λΈ”μ„ μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.

enrollment
class_room

creator, classmate ν…Œμ΄λΈ”μ€ Flyway seed 데이터λ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ ν…ŒμŠ€νŠΈ μ΄ˆκΈ°ν™” λŒ€μƒμ—μ„œ μ œμ™Έν•©λ‹ˆλ‹€.


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

ν…ŒμŠ€νŠΈ μ „λž΅μ€ λ‹€μŒ κΈ°μ€€μœΌλ‘œ λ‚˜λˆ„μ—ˆμŠ΅λ‹ˆλ‹€.

계측 λͺ©μ 
Entity Test 도메인 μƒνƒœ 전이와 λΉ„μ¦ˆλ‹ˆμŠ€ κ·œμΉ™μ„ λΉ λ₯΄κ²Œ 검증
Controller Test HTTP μš”μ²­/응닡, Validation, μ˜ˆμ™Έ 응닡 ꡬ쑰 검증
Repository Test JPQL, Native Query, DTO Projection, 쑰건뢀 UPDATE 검증
Service Integration Test μ‹€μ œ DB 기반 전체 λΉ„μ¦ˆλ‹ˆμŠ€ 흐름 검증
Concurrency Test 정원 초과 방지와 쀑볡 μ‹ μ²­ λ°©μ§€μ˜ λ™μ‹œμ„± 검증

Controller Test

Controller ν…ŒμŠ€νŠΈλŠ” @WebMvcTest와 MockMvcλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

Service 계측은 Mock 객체둜 λŒ€μ²΄ν•˜κ³ , Controller κ³„μΈ΅μ˜ μ±…μž„λ§Œ κ²€μ¦ν•©λ‹ˆλ‹€.

μ£Όμš” 검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • 정상 μš”μ²­ μ‹œ HTTP status code 검증
  • 생성 API의 Location 응닡 헀더 검증
  • Bean Validation μ‹€νŒ¨ μ‹œ 400 응닡 검증
  • JSON νƒ€μž… 였λ₯˜ λ°œμƒ μ‹œ 400 νƒ€μž… 였λ₯˜ 응닡 검증
  • Service 계측 μ˜ˆμ™Έ λ°œμƒ μ‹œ μ „μ—­ μ˜ˆμ™Έ 응닡 ꡬ쑰 검증
  • νŽ˜μ΄μ§€λ„€μ΄μ…˜ μ‘λ‹΅μ—μ„œ ν•„μš”ν•œ ν•„λ“œλ§Œ λ…ΈμΆœλ˜λŠ”μ§€ 검증

κ°•μ˜ λͺ©λ‘ 쑰회 APIλŠ” Spring Data Pageλ₯Ό κ·ΈλŒ€λ‘œ μ‘λ‹΅ν•˜μ§€ μ•Šκ³  PageResponse둜 λ³€ν™˜ν•©λ‹ˆλ‹€.

λ”°λΌμ„œ ν…ŒμŠ€νŠΈμ—μ„œλŠ” λ‹€μŒ ν•„λ“œλ§Œ μ‘λ‹΅λ˜λŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€.

{
  "content": [],
  "page": 0,
  "size": 10,
  "totalElements": 1,
  "totalPages": 1
}

λ˜ν•œ PageImpl κΈ°λ³Έ 직렬화 ν•„λ“œκ°€ μ™ΈλΆ€ API 응닡에 λ…ΈμΆœλ˜μ§€ μ•ŠλŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€.

pageable
sort
first
last
number
numberOfElements
empty

Entity Test

Entity ν…ŒμŠ€νŠΈλŠ” Spring Context 없이 JUnit 5, AssertJ, Mockito만 μ‚¬μš©ν•©λ‹ˆλ‹€.

이λ₯Ό 톡해 도메인 객체의 μƒνƒœ 전이 κ·œμΉ™μ„ λΉ λ₯΄κ²Œ κ²€μ¦ν•©λ‹ˆλ‹€.

ClassRoom Entity Test

검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • κ°•μ˜ 생성 μ‹œ 졜초 μƒνƒœλŠ” DRAFT
  • κ°•μ˜ 생성 μ‹œ μ‹ μ²­ 인원은 0λͺ…
  • DRAFT μƒνƒœμ˜ κ°•μ˜λŠ” OPEN μƒνƒœλ‘œ λ³€κ²½ κ°€λŠ₯
  • DRAFTκ°€ μ•„λ‹Œ κ°•μ˜λŠ” λͺ¨μ§‘ μ‹œμž‘ λΆˆκ°€
  • OPEN μƒνƒœμ˜ κ°•μ˜λŠ” CLOSED μƒνƒœλ‘œ λ³€κ²½ κ°€λŠ₯
  • OPEN이 μ•„λ‹Œ κ°•μ˜λŠ” λͺ¨μ§‘ 마감 λΆˆκ°€
  • DRAFT μƒνƒœμ˜ κ°•μ˜λ§Œ μˆ˜μ • κ°€λŠ₯
  • κ°•μ˜ μ‹œμž‘ μ‹œκ°μ΄ μ’…λ£Œ μ‹œκ°λ³΄λ‹€ 이전이 μ•„λ‹ˆλ©΄ μˆ˜μ • λΆˆκ°€
  • μš”μ²­ν•œ creatorIdκ°€ κ°•μ˜ μ†Œμœ μžμΈμ§€ 검증

Enrollment Entity Test

검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μˆ˜κ°• μ‹ μ²­ 생성 μ‹œ PENDING μƒνƒœ
  • μˆ˜κ°• μ‹ μ²­ 생성 μ‹œ 결제 만료 μ‹œκ°μ€ μ‹ μ²­ μ‹œκ° κΈ°μ€€ 10λΆ„ λ’€
  • 결제 만료 μ „ PENDING 신청은 CONFIRMED μƒνƒœλ‘œ ν™•μ • κ°€λŠ₯
  • PENDING이 μ•„λ‹Œ 신청은 결제 ν™•μ • λΆˆκ°€
  • 결제 κ°€λŠ₯ μ‹œκ°„μ΄ 만료된 신청은 결제 ν™•μ • λΆˆκ°€
  • PENDING 신청은 μ‚¬μš©μž μ·¨μ†Œ κ°€λŠ₯
  • CONFIRMED 신청은 ν™•μ • ν›„ 7일 이내 μ·¨μ†Œ κ°€λŠ₯
  • ν™•μ • ν›„ 7일이 μ§€λ‚˜λ©΄ μ·¨μ†Œ λΆˆκ°€
  • 이미 μ·¨μ†Œλœ 신청은 λ‹€μ‹œ μ·¨μ†Œ λΆˆκ°€
  • 결제 만료 μ‹œκ°„μ΄ μ§€λ‚œ PENDING 신청은 μžλ™ μ·¨μ†Œ κ°€λŠ₯
  • 결제 만료 μ‹œκ°„μ΄ μ§€λ‚˜μ§€ μ•Šμ€ 신청은 μžλ™ μ·¨μ†Œ λΆˆκ°€
  • CANCELLED μƒνƒœμ˜ 신청은 μž¬μ‹ μ²­ μ‹œ PENDING μƒνƒœλ‘œ 볡ꡬ κ°€λŠ₯
  • CANCELLED μƒνƒœκ°€ μ•„λ‹ˆλ©΄ μž¬μ‹ μ²­ λΆˆκ°€
  • μš”μ²­ν•œ classmateIdκ°€ μ‹ μ²­ μ†Œμœ μžμΈμ§€ 검증

Repository Test

Repository ν…ŒμŠ€νŠΈλŠ” @DataJpaTest와 MySQL Testcontainersλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

H2κ°€ μ•„λ‹Œ MySQLμ—μ„œ ν…ŒμŠ€νŠΈν•˜λŠ” μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • MySQL SQL 문법과 μ‹€μ œ μ‹€ν–‰ ν™˜κ²½μ— κ°€κΉŒμš΄ μ‘°κ±΄μ—μ„œ 검증
  • SELECT ... FOR UPDATE 기반 잠금 쿼리 검증
  • Native Query 기반 쑰건뢀 UPDATE 검증
  • Flyway migrationκ³Ό seed data 적용 검증
  • μ‹€μ œ DB μ œμ•½μ‘°κ±΄κ³Ό 인덱슀 λ™μž‘ 검증

ClassRoomRepository Test

검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • ν¬λ¦¬μ—μ΄ν„°μ˜ κ°•μ˜ λͺ©λ‘ DTO Projection 쑰회
  • μˆ˜κ°•μƒμ˜ 곡개 κ°•μ˜ λͺ©λ‘ DTO Projection 쑰회
  • κ°•μ˜ 상세 DTO Projection 쑰회
  • κ°•μ˜λ³„ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ DTO Projection 쑰회
  • OPEN μƒνƒœμ΄κ³  정원이 남아 있으면 enrollment_count 증가 성곡
  • 정원이 가득 μ°¨λ©΄ enrollment_count 증가 μ‹€νŒ¨

EnrollmentRepository Test

검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • κ°•μ˜ ID와 ν΄λž˜μŠ€λ©”μ΄νŠΈ ID둜 μˆ˜κ°• μ‹ μ²­ 쑰회
  • 동일 κ°•μ˜/동일 ν΄λž˜μŠ€λ©”μ΄νŠΈ μ‹ μ²­ 쑰회 μ‹œ FOR UPDATE 잠금 μ‚¬μš©
  • λ‚΄ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ DTO Projection 쑰회
  • 결제 만료된 PENDING μ‹ μ²­ λͺ©λ‘ 쑰회
  • μˆ˜κ°• μ‹ μ²­ 상세 DTO Projection 쑰회

Service Integration Test

Service 톡합 ν…ŒμŠ€νŠΈλŠ” @SpringBootTest와 MySQL Testcontainersλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

Controllerλ₯Ό κ±°μΉ˜μ§€ μ•Šκ³  Service 계측을 직접 ν˜ΈμΆœν•˜μ—¬ μ‹€μ œ DB와 νŠΈλžœμž­μ…˜ 기반 λΉ„μ¦ˆλ‹ˆμŠ€ 흐름을 κ²€μ¦ν•©λ‹ˆλ‹€.

ClassRoomService Integration Test

검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • κ°•μ˜ 생성 μ‹œ DRAFT μƒνƒœλ‘œ μ €μž₯
  • μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” ν¬λ¦¬μ—μ΄ν„°λ‘œ κ°•μ˜ 생성 μ‹œ 404 μ˜ˆμ™Έ
  • κ°•μ˜ μ†Œμœ μžλŠ” DRAFT μƒνƒœμ˜ κ°•μ˜ μˆ˜μ • κ°€λŠ₯
  • κ°•μ˜ μ†Œμœ μžκ°€ μ•„λ‹ˆλ©΄ μˆ˜μ • λΆˆκ°€
  • κ°•μ˜ μ†Œμœ μžλŠ” DRAFT κ°•μ˜λ₯Ό OPEN μƒνƒœλ‘œ λ³€κ²½ κ°€λŠ₯
  • OPEN κ°•μ˜λŠ” CLOSED μƒνƒœλ‘œ λ³€κ²½ κ°€λŠ₯
  • ν¬λ¦¬μ—μ΄ν„°λŠ” 본인 κ°•μ˜μ˜ DRAFT 상세 쑰회 κ°€λŠ₯
  • μˆ˜κ°•μƒμ€ DRAFT κ°•μ˜ 상세 쑰회 λΆˆκ°€
  • μˆ˜κ°•μƒ κ°•μ˜ λͺ©λ‘ μ‘°νšŒμ—μ„œ DRAFT μƒνƒœ ν•„ν„°λŠ” ν—ˆμš©λ˜μ§€ μ•ŠμŒ
  • 크리에이터 κ°•μ˜ λͺ©λ‘ μ‘°νšŒμ—μ„œ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” creatorIdλŠ” 404 μ˜ˆμ™Έ

EnrollmentService Integration Test

검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • OPEN κ°•μ˜μ— μˆ˜κ°• μ‹ μ²­ν•˜λ©΄ PENDING μ‹ μ²­ 생성
  • μˆ˜κ°• μ‹ μ²­ 성곡 μ‹œ enrollment_count 증가
  • μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” ν΄λž˜μŠ€λ©”μ΄νŠΈλ‘œ μ‹ μ²­ μ‹œ 404 μ˜ˆμ™Έ
  • μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” κ°•μ˜μ— μ‹ μ²­ μ‹œ 404 μ˜ˆμ™Έ
  • OPEN μƒνƒœκ°€ μ•„λ‹Œ κ°•μ˜μ—λŠ” μ‹ μ²­ λΆˆκ°€
  • 정원이 가득 μ°¬ κ°•μ˜μ—λŠ” μ‹ μ²­ λΆˆκ°€
  • 이미 μ’Œμ„μ„ 점유 쀑인 신청이 있으면 쀑볡 μ‹ μ²­ λΆˆκ°€
  • CANCELLED 신청이 있으면 같은 rowλ₯Ό μž¬μ‚¬μš©ν•˜μ—¬ μž¬μ‹ μ²­
  • 본인 신청이고 결제 κ°€λŠ₯ μ‹œκ°„μ΄ μ§€λ‚˜μ§€ μ•Šμ•˜μœΌλ©΄ 결제 ν™•μ • κ°€λŠ₯
  • λ‹€λ₯Έ ν΄λž˜μŠ€λ©”μ΄νŠΈμ˜ 신청은 결제 ν™•μ • λΆˆκ°€
  • μ‹ μ²­ μ·¨μ†Œ μ‹œ CANCELLED μƒνƒœλ‘œ λ³€κ²½λ˜κ³  enrollment_count κ°μ†Œ
  • λ‚΄ μˆ˜κ°• μ‹ μ²­ λͺ©λ‘ 쑰회 μ‹œ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” ν΄λž˜μŠ€λ©”μ΄νŠΈλŠ” 404 μ˜ˆμ™Έ
  • μˆ˜κ°• μ‹ μ²­ 상세 쑰회 μ‹œ μ†Œμœ μžκ°€ μ•„λ‹ˆλ©΄ 403 μ˜ˆμ™Έ
  • 결제 만료된 PENDING 신청은 μžλ™ μ·¨μ†Œλ˜κ³  enrollment_count κ°μ†Œ

Concurrency Test

λ™μ‹œμ„± ν…ŒμŠ€νŠΈλŠ” μ‹€μ œ MySQL Testcontainers ν™˜κ²½μ—μ„œ ExecutorService와 CountDownLatchλ₯Ό μ‚¬μš©ν•˜μ—¬ μ—¬λŸ¬ μš”μ²­μ„ λ™μ‹œμ— λ°œμƒμ‹œν‚΅λ‹ˆλ‹€.

μˆ˜κ°• 신청은 λ‹€μŒ 쿼리λ₯Ό 톡해 정원 초과λ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€.

UPDATE class_room
SET enrollment_count = enrollment_count + 1
WHERE class_room_id = ?
  AND status = 'OPEN'
  AND enrollment_count < capacity;

λ™μ‹œμ„± ν…ŒμŠ€νŠΈμ—μ„œλŠ” λ‹€μŒ μ‹œλ‚˜λ¦¬μ˜€λ₯Ό κ²€μ¦ν•©λ‹ˆλ‹€.

정원 초과 λ°©μ§€

정원 5λͺ…인 κ°•μ˜μ— 10λͺ…μ˜ ν΄λž˜μŠ€λ©”μ΄νŠΈκ°€ λ™μ‹œμ— μ‹ μ²­ν•˜λ”λΌλ„ μ„±κ³΅μžλŠ” 5λͺ…λ§Œ λ°œμƒν•΄μ•Ό ν•©λ‹ˆλ‹€.

검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • 성곡 μš”μ²­ μˆ˜λŠ” 5건
  • μ‹€νŒ¨ μš”μ²­ μˆ˜λŠ” 5건
  • class_room.enrollment_countλŠ” 5
  • enrollment row μˆ˜λŠ” 5

동일 ν΄λž˜μŠ€λ©”μ΄νŠΈ 쀑볡 μ‹ μ²­ λ°©μ§€

동일 ν΄λž˜μŠ€λ©”μ΄νŠΈκ°€ 같은 κ°•μ˜μ— λ™μ‹œμ— 10번 μ‹ μ²­ν•˜λ”λΌλ„ ν•˜λ‚˜μ˜ ν™œμ„± μ‹ μ²­λ§Œ μƒμ„±λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

검증 ν•­λͺ©μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • 성곡 μš”μ²­ μˆ˜λŠ” 1건
  • μ‹€νŒ¨ μš”μ²­ μˆ˜λŠ” 9건
  • class_room.enrollment_countλŠ” 1
  • enrollment row μˆ˜λŠ” 1
  • μ‹€νŒ¨ μš”μ²­μ€ 쀑볡 μ‹ μ²­ μ˜ˆμ™Έλ‘œ 처리

ν…ŒμŠ€νŠΈλ‘œ κ²€μ¦ν•œ μ£Όμš” λΉ„μ¦ˆλ‹ˆμŠ€ κ·œμΉ™

λ³Έ ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” λ‹€μŒ 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ κ·œμΉ™μ„ κ²€μ¦ν•©λ‹ˆλ‹€.

κ°•μ˜ μƒνƒœ κ·œμΉ™

DRAFT -> OPEN
OPEN -> CLOSED
  • DRAFT μƒνƒœμ—μ„œλ§Œ κ°•μ˜ μˆ˜μ • κ°€λŠ₯
  • μˆ˜κ°•μƒμ€ DRAFT κ°•μ˜ 쑰회 λΆˆκ°€
  • OPEN μƒνƒœ κ°•μ˜λ§Œ μˆ˜κ°• μ‹ μ²­ κ°€λŠ₯

μˆ˜κ°• μ‹ μ²­ μƒνƒœ κ·œμΉ™

PENDING -> CONFIRMED
PENDING -> CANCELLED
CONFIRMED -> CANCELLED
CANCELLED -> PENDING
  • PENDING, CONFIRMEDλŠ” μ’Œμ„ 점유 μƒνƒœ
  • CANCELLEDλŠ” μ’Œμ„ 미점유 μƒνƒœ
  • CANCELLED 신청은 μž¬μ‹ μ²­ κ°€λŠ₯
  • 이미 μ’Œμ„μ„ 점유 쀑인 신청은 쀑볡 μ‹ μ²­ λΆˆκ°€

정원 μ •ν•©μ„± κ·œμΉ™

enrollment_count = PENDING μƒνƒœ μ‹ μ²­ 수 + CONFIRMED μƒνƒœ μ‹ μ²­ 수
  • μ‹ μ²­ 성곡 μ‹œ enrollment_count + 1
  • μ·¨μ†Œ λ˜λŠ” 결제 만료 μžλ™ μ·¨μ†Œ μ‹œ enrollment_count - 1
  • 정원 초과 μ‹œ μ‹ μ²­ μ‹€νŒ¨
  • λ™μ‹œ μ‹ μ²­ μƒν™©μ—μ„œλ„ 정원 초과 λΆˆκ°€

결제 λŒ€κΈ° 만료 κ·œμΉ™

paymentExpiredAt = μ‹ μ²­ μ‹œκ° + 10λΆ„
  • 결제 만료 μ „μ—λŠ” 결제 ν™•μ • κ°€λŠ₯
  • 결제 만료 μ‹œκ°μ΄ λ„λž˜ν•˜λ©΄ 결제 ν™•μ • λΆˆκ°€
  • 만료된 PENDING 신청은 μžλ™ μ·¨μ†Œ λŒ€μƒ
  • μžλ™ μ·¨μ†Œ μ‹œ μ’Œμ„ 점유 수 κ°μ†Œ

κΆŒν•œ 검증 κ·œμΉ™

  • ν¬λ¦¬μ—μ΄ν„°λŠ” 본인 κ°•μ˜λ§Œ μˆ˜μ •, λͺ¨μ§‘ μ‹œμž‘, λͺ¨μ§‘ 마감, μˆ˜κ°•μƒ λͺ©λ‘ 쑰회 κ°€λŠ₯
  • ν΄λž˜μŠ€λ©”μ΄νŠΈλŠ” 본인 μˆ˜κ°• μ‹ μ²­λ§Œ 상세 쑰회, 결제 ν™•μ •, μ·¨μ†Œ κ°€λŠ₯
  • κΆŒν•œμ΄ μ—†λŠ” μš”μ²­μ€ 403 μ˜ˆμ™Έλ‘œ 처리

ν…ŒμŠ€νŠΈ μ‹€ν–‰ κ²°κ³Ό

μ΅œμ’…μ μœΌλ‘œ λ‹€μŒ λͺ…λ Ήμ–΄ κΈ°μ€€ 전체 ν…ŒμŠ€νŠΈ 톡과λ₯Ό ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€.

./gradlew clean test

Windows ν™˜κ²½μ—μ„œλŠ” λ‹€μŒ λͺ…λ Ήμ–΄ κΈ°μ€€ 전체 ν…ŒμŠ€νŠΈ 톡과λ₯Ό ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€.

gradlew.bat clean test

ν…ŒμŠ€νŠΈ 성곡 μ‹œ Gradle 좜λ ₯은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

BUILD SUCCESSFUL

ν…ŒμŠ€νŠΈ κ΄€λ ¨ μ œμ•½μ‚¬ν•­

  • ν…ŒμŠ€νŠΈλŠ” Docker Desktop이 μ‹€ν–‰ 쀑인 ν™˜κ²½μ„ μ „μ œλ‘œ ν•©λ‹ˆλ‹€.
  • Repository / Integration / Concurrency ν…ŒμŠ€νŠΈλŠ” MySQL Testcontainersλ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ 졜초 μ‹€ν–‰ μ‹œ MySQL Docker image pull μ‹œκ°„μ΄ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • creator, classmate ν…ŒμŠ€νŠΈ λ°μ΄ν„°λŠ” Flyway seed data에 μ˜μ‘΄ν•©λ‹ˆλ‹€.
  • enrollment, class_room λ°μ΄ν„°λŠ” ν…ŒμŠ€νŠΈ κ°„ 격리λ₯Ό μœ„ν•΄ 각 ν…ŒμŠ€νŠΈ 전에 μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
  • μ™ΈλΆ€ 결제 μ‹œμŠ€ν…œ 연동은 μ—†μœΌλ―€λ‘œ 결제 확정은 μƒνƒœ λ³€κ²½ 둜직으둜만 κ²€μ¦ν•©λ‹ˆλ‹€.
  • Redis λŒ€κΈ°μ—΄ κΈ°λŠ₯은 별도 선택 κ΅¬ν˜„ μ˜μ—­μ΄λ©°, ν˜„μž¬ 핡심 ν…ŒμŠ€νŠΈ λ²”μœ„λŠ” DB 기반 κ°•μ˜/μˆ˜κ°• μ‹ μ²­ μ •ν•©μ„± 검증에 μ§‘μ€‘ν•©λ‹ˆλ‹€.

λ―Έκ΅¬ν˜„ / μ œμ•½μ‚¬ν•­

  • μ‹€μ œ νšŒμ›κ°€μž… 및 둜그인 κΈ°λŠ₯은 κ΅¬ν˜„ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.
  • Spring Security 기반 인증/μΈκ°€λŠ” μ μš©ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.
  • μš”μ²­μ˜ creatorId, classmateId 값을 톡해 μ‚¬μš©μžλ₯Ό μ‹λ³„ν•©λ‹ˆλ‹€.
  • μ‹€μ œ μ™ΈλΆ€ 결제 μ‹œμŠ€ν…œ 연동은 κ΅¬ν˜„ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.
  • 결제 확정은 μ™ΈλΆ€ 결제 승인 κ²°κ³Όλ₯Ό κ²€μ¦ν•˜μ§€ μ•Šκ³  μ‹ μ²­ μƒνƒœλ₯Ό CONFIRMED둜 λ³€κ²½ν•˜λŠ” λ°©μ‹μœΌλ‘œ λŒ€μ²΄ν–ˆμŠ΅λ‹ˆλ‹€.
  • 운영 ν™˜κ²½μ—μ„œ μ—¬λŸ¬ μΈμŠ€ν„΄μŠ€κ°€ μŠ€μΌ€μ€„λŸ¬λ₯Ό λ™μ‹œμ— μ‹€ν–‰ν•˜λŠ” κ²½μš°μ—λŠ” λΆ„μ‚° 락이 ν•„μš”ν•˜μ§€λ§Œ, λ³Έ ν”„λ‘œμ νŠΈλŠ” 단일 μΈμŠ€ν„΄μŠ€ 싀행을 κΈ°μ€€μœΌλ‘œ κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€.
  • Redis λŒ€κΈ°μ—΄μ€ μ‹œκ°„μƒ μ œμ•½μ΄ μžˆμ–΄ κ΅¬ν˜„ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.
  • 동일 μˆ˜κ°•μƒμ΄ 같은 κ°•μ˜μ— 졜초 신청을 λ™μ‹œμ— μ—¬λŸ¬ 번 μš”μ²­ν•˜λŠ” 극단적인 경우 DB의 unique μ œμ•½μ΄ μ΅œμ’… 쀑볡 λ°©μ–΄μ„ μœΌλ‘œ λ™μž‘ν•©λ‹ˆλ‹€. 이 경우 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œλŠ” 쀑볡 μ‹ μ²­ μ˜ˆμ™Έλ‘œ λ³€ν™˜ν•˜μ—¬ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

AI ν™œμš© λ²”μœ„

λ³Έ 과제 μˆ˜ν–‰ κ³Όμ •μ—μ„œ AI 도ꡬλ₯Ό λ‹€μŒ λ²”μœ„λ‘œ ν™œμš©ν–ˆμŠ΅λ‹ˆλ‹€.

  • μš”κ΅¬μ‚¬ν•­ 뢄석 및 κΈ°λŠ₯ λͺ…μ„Έ 정리
  • README ꡬ쑰 μ΄ˆμ•ˆ μž‘μ„± 및 μ΅œμ’… κ²€ν† 
  • ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ λ„μΆœ
  • ν…ŒμŠ€νŠΈ μ½”λ“œ 섀계
  • μ½”λ“œ 검증

AIκ°€ μƒμ„±ν•œ λ‚΄μš©μ„ κ·ΈλŒ€λ‘œ μ œμΆœν•˜μ§€ μ•Šκ³ , ν”„λ‘œμ νŠΈ μš”κ΅¬μ‚¬ν•­κ³Ό κ΅¬ν˜„ μ½”λ“œμ— 맞게 직접 μˆ˜μ •ν•˜κ³  κ²€μ¦ν–ˆμŠ΅λ‹ˆλ‹€.

μ΅œμ’… 섀계, κ΅¬ν˜„, ν…ŒμŠ€νŠΈ, μ˜ˆμ™Έ 처리 방식은 직접 νŒλ‹¨ν•˜μ—¬ λ°˜μ˜ν–ˆμŠ΅λ‹ˆλ‹€.

About

liveclass-assignment

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors