Skip to content

jxmen/course-registration-server-example

Repository files navigation

수강 신청 서버 예제

기술 스택

  • Java21
  • Spring Boot 4.0.6, Spring Framework 7.0.7
  • Spring Data JPA, MySQL

실행 방법

docker-compose up -d

API 목록 및 예시

문서 빌드 방법

./gradlew asciidoctor
  • asciidoctor 태스크는 test에 의존하므로 테스트 실행 → REST Docs 스니펫 생성(build/generated-snippets) → 문서 조립 순으로 수행됩니다.
  • 빌드 결과물: build/docs/asciidoc/index.html

폴더 구조

src/main/java/com/liveklass/jxmen_assignment/
├── domain/                    # JPA 엔티티 + 비즈니스 규칙
│   ├── exceptions/            # 도메인 예외 클래스
│   ├── Class.java             # 강의 (DRAFT → OPEN → CLOSED)
│   ├── ClassMate.java         # 수강생
│   ├── ClassStatus.java
│   ├── Creator.java           # 강사
│   ├── Enrollment.java        # 수강 신청 (WAITING/PENDING → CONFIRMED / CANCELLED)
│   └── EnrollmentStatus.java
├── usecase/                   # 쓰기 유스케이스 (@Transactional)
│   ├── dto/                   # 유스케이스 입력 Command 객체
│   ├── EnrollmentUseCase.java
│   ├── CancelEnrollmentUseCase.java
│   └── ...
├── query/                     # 읽기 전용 조회 (@Transactional readOnly=true)
│   ├── ListClassesQuery.java
│   ├── MyEnrollmentsQuery.java
│   └── ...
└── infrastructure/
    ├── web/                   # @RestController, DTO, ApiExceptionHandler
    │   └── dto/
    ├── persistence/           # Spring Data JPA Repository 인터페이스
    └── config/

기본적으로는 클린 아키텍처를 지향하였으나, 단순한 CRUD 어플리케이션을 고려하여 어느정도 타협하였습니다.

clean-architecture.png

레이어별 설명

  • domain
    • 비즈니스 로직을 담은 메서드 및 JPA 엔티티 설정 등이 포함되어 있습니다.
    • JPA 의존성이 있기에 POJO는 아닙니다. POJO 형태로 만들 경우 entity/domain 클래스를 분리하고 mapeer를 사용해야 합니다.
  • usecase
    • 트랜잭션 관리, 도메인으로 비즈니스 로직 처리 위임
    • 인터페이스가 아닌 구현체 클래스를 그대로 사용했습니다. 만약 구현 변경이 필요하다면, 인터페이스 추출 후 구현체 클래스를 사용하는 방식으로 리팩토링 하는 것이 좋다고 판단했습니다.
  • query: 조회용 서비스
  • infrastructure:
    • 외부 구현체 - web, database 등

데이터 모델 설명

엔티티 설명
Creator 강사. 강의를 생성한다
Class 강의. 생성 시 DRAFT, 수동으로 OPENCLOSED 전이
ClassMate 수강생. enroll() 호출로 수강 신청
Enrollment 수강 신청. 정원 미달 시 PENDING, 초과 시 WAITING 생성. 취소 시 대기열 선두가 자동으로 PENDING 승격

요구사항 해석 및 가정

  • 인증/사용자 식별: 별도 인증 미들웨어 없이 요청 헤더(X-Creator-Id, X-ClassMate-Id)로 식별합니다. 실제 서비스라면 JWT 등으로 대체해야 하나, 과제 범위에서는 단순화하였습니다.
  • 대기열(WAITING) 동작:
    • 정원이 초과된 상태에서 신청하면 거부하지 않고 WAITING 상태로 생성합니다.
  • 취소 가능 기간: 명세의 "예: 7일"을 그대로 채택하여, 결제 확정(confirmedAt) 후 7일이 지나면 취소 불가로 처리
  • 중복 신청 방지: 동일 수강생이 동일 강의에 활성 신청(WAITING/PENDING/CONFIRMED)을 동시에 두 건 이상 가질 수 없도록 제약합니다. 취소 후 재신청은 허용합니다.
  • 강의별 수강생 목록 조회: 크리에이터 전용으로 제한하며, 강의 소유자가 아니면 403을 반환합니다.

설계 결정과 이유

동시성 테스트 코드 - EnrollmentUseCaseConcurrencyTest.java

  • 수강 신청 동시성 처리의 경우 원자적 업데이트를 사용했습니다. 구현이 간단하고 성능이 뛰어나기 때문입니다.
  • 대기열 기능의 경우, 수강 신청 상태 중 별도의 WAITING이라는 상태로 설정하게 하였습니다. 그리고 누군가 결제 후 취소(CONFIRMED -> CANCELLED)를 한다면, 가장 먼저 WAITING 상태의 수강생이 PENDING 상태가 되도록 구현하였습니다.
    • 취소 시 상태 변경도 capacity를 넘지 않기 위해 비관적 락을 사용했으나, 충돌이 많을 것으로 예상되는 부분은 아니라 낙관적 락도 적절한 것 같습니다.

테스트 실행 방법

./gradlew test

테스트 커버리지 측정 (JaCoCo)

./gradlew test jacocoTestReport
  • HTML 리포트: build/reports/jacoco/test/html/index.html
  • XML 리포트: build/reports/jacoco/test/jacocoTestReport.xml
  • test 태스크가 끝나면 jacocoTestReport가 자동 실행되도록 설정되어 있습니다(finalizedBy).

현재 커버리지 (line 기준):

패키지 Line 비율
domain 106/112 94.6%
domain/exceptions 16/18 88.9%
usecase 43/64 67.2%
usecase/dto 1/2 50.0%
query 29/32 90.6%
infrastructure/web 48/55 87.3%
infrastructure/web/dto 64/64 100.0%
전체 309/351 88.0%

미구현 / 제약사항

  • 완전한 클린 아키텍처가 아님
  • API 예외 시 별도 상태 응답 코드 추가하지 못함 (예: {code: INVALID_INPUT})
  • 사용자 등록 API 없음: Creator/ClassMate는 DB에 미리 존재한다고 가정하며, 가입 플로우는 구현되어 있지 않습니다
    • data.sql을 통해 더미 데이터 생성
  • 대기열 부가 기능 미구현: 대기 순번 조회, 사용자 본인의 WAITING 이탈(취소)등은 구현되어 있지 않습니다.
  • 시나리오/E2E 테스트 부재: 단위·동시성 테스트는 작성되어 있으나, 여러 API를 엮은 다단계 시나리오 테스트는 작성하지 못했습니다.

AI 활용 범위

모델 정보: Claude Code Sonnet 4.6 (medium)

반복적으로 작성되는 아래 항목의 초기 템플릿 생성을 자동화했습니다.

  • Controller 테스트
  • REST Docs snippet
  • HTTP example
  • DTO skeleton

비즈니스 로직 및 도메인 규칙은 직접 구현했습니다.

자동화 목적

  • 테스트 누락 방지
  • API 문서 형식 일관화
  • 반복 작업 감소

생성 후 직접 검증한 항목

  • API 설계 적절성
  • 예외 처리
  • 상태 전이 로직
  • 도메인 규칙
  • 테스트 통과 여부

스킬 파일: SKILL.md

About

수강 신청 서버 예제

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors