Skip to content

Conversation

@ghtjr410
Copy link

수강신청(요구사항 변경) 과제 제출

안녕하세요!
수강신청(요구사항 변경) 과제 제출합니다.

스트랭글러 패턴 적용 과정

커밋 전략

모든 변경사항을 하나의 커밋에 담으면 단위가 너무 커집니다.

  • PR 단위: 요구사항을 만족하는 완결된 기능
  • Commit 단위: 빌드와 테스트가 통과하는, 작업 단계의 의미있는 원자적 단위 (기능은 미완결일 수 있음)

실무에서 PR 리뷰를 받는다고 가정하고, 커밋 히스토리만 봐도 변경 흐름을 파악할 수 있도록 작업을 나눴습니다.

SessionStatus → ProgressStatus + RecruitmentStatus 분리

기존 코드를 한 번에 바꾸지 않고, 빌드가 깨지지 않는 단위로 커밋을 나눴습니다.

1. Session에 새 필드 추가 (null) → 기존 생성자 유지
2. schema.sql 컬럼 추가 + Repository save 수정
3. Repository 조회에서 새 컬럼 읽기
4. validateStatus 로직을 새 필드 기반으로 변경
5. 기존 SessionStatus 제거

각 커밋마다 테스트가 통과하는 상태를 유지했습니다.

CoverImage → CoverImages (1:N)

DB가 이미 session_id를 FK로 가지는 1:N 구조여서, 도메인만 변경하면 됐습니다.
스키마 변경 없이 바로 교체가 가능한 경우는 점진적 전환 없이 진행했습니다.

고민한 지점

1. 도메인 로직 → 영속화 분리

// 도메인이 상태 변경하고 객체 반환
Enrollment approved = session.approve(studentId);

// Repository는 객체를 받아서 저장 (상태값 노출 X)
sessionRepository.updateEnrollment(approved);

Service에서 상태값을 직접 넘기면 도메인과 불일치할 수 있어서, 변경된 객체를 반환받아 저장하는 방식을 선택했습니다.

2. JDBC 한계 인식

JPA의 Dirty Checking이 없어서 수동 저장이 필요하지만, "도메인 로직 → 영속화" 순서를 명확히 하는 것으로 타협했습니다.

배운 점

  • 스트랭글러 패턴: 빌드가 깨지지 않는 단위로 점진적 전환
  • 커밋 단위: 빌드가 통과하는 원자적 단위로 커밋
  • 전술적 판단: DB 구조가 이미 1:N이면 점진적 전환 없이 바로 교체 가능
  • YAGNI: 요구사항에 없는 repo.updateEnrollmentStatus(상태값) 대신 repo.updateEnrollment(객체)로 최소 구현

도메인의 모든 비즈니스 로직(Session 생성, 상태 변경 등)을 Service에 구현하고 싶은 욕심이 있었습니다.

하지만 이번 단계의 핵심은 스트랭글러 패턴을 적용한 점진적 리팩터링이기 때문에,
변경된 기능 요구사항에 해당하는 수강 승인/취소만 Service에 구현하는 것으로 범위를 제한했습니다.

리뷰 잘 부탁드립니다!

README.md
- 4단계 문서 링크 추가

02-lms-domain-model.md
- SessionRepository/SessionService 관련 완료 항목 제거로 문서 정리

03-lms-db-mapping.md
- 구현 기능 목록 상세 추가

04-lms-requirements-update.md
- 신규 파일 생성 및 4단계 요구사항/학습 목표/프로그래밍 요구사항 문서화
SessionService.java
- 불필요한 세미콜론 제거로 코드 정리

SessionServiceTest.java
- 더 이상 사용되지 않는 테스트 클래스 전체 삭제
SessionBuilder.java
- 테스트용 Session 객체 생성을 위한 빌더 클래스 신규 추가
- 코스 ID, 커버 이미지, 기간, 상태, 정책 등을 유연하게 설정 가능
- Free/Paid 정책 설정 메서드 제공
- 최종 Session 객체 생성하는 build 메서드 구현

SessionRepositoryTest.java
- Session 생성 시 직접 new로 생성하던 방식을 SessionBuilder 사용 방식으로 변경
- 커버 이미지 포함 테스트에서 빌더로 커버 이미지 설정하도록 수정
- 무료/유료 강의 저장 및 조회 테스트에서 정책 설정을 빌더 메서드로 대체
- 상태(RECRUITING)와 유료 정책을 조합하여 빌더로 Session 구성하도록 변경
- courseId 조회 테스트에서도 SessionBuilder 사용하여 중복 코드 제거
ProgressStatus.java
- 세션 진행 상태 표현을 위한 PREPARING, IN_PROGRESS, CLOSED enum 추가
- 각 상태에 대한 한글 설명 필드 포함

RecruitmentStatus.java
- 모집 상태 NOT_RECRUITING, RECRUITING enum 추가
- 모집 가능 여부를 판단하는 canEnroll() 메서드 구현

RecruitmentStatusTest.java
- RECRUITING일 때 canEnroll()이 true임을 검증
- NOT_RECRUITING일 때 canEnroll()이 false임을 검증
Session.java
- ProgressStatus, RecruitmentStatus 필드를 새로 추가
- 기존 생성자들에 두 상태값이 전달되지 않을 경우 null로 초기화하도록 수정
- 전체 필드를 포함하는 메인 생성자에 progressStatus, recruitmentStatus 파라미터 추가

JdbcSessionRepository.java
- ResultSet → Session 매핑 시 progressStatus, recruitmentStatus를 일단 null로 설정하도록 수정
- 새로 추가된 생성자 시그니처에 맞춰 객체 생성 코드 업데이트
Session.java
- progressStatus, recruitmentStatus 조회용 getter 메서드 추가

JdbcSessionRepository.java
- insert SQL에 progress_status, recruitment_status 컬럼 추가
- 두 상태값을 조건부(name 또는 null)로 저장하도록 PreparedStatement 설정 수정
- 새로운 컬럼 추가에 따라 파라미터 인덱스 재정렬

schema.sql
- session 테이블에 progress_status, recruitment_status 컬럼 추가
JdbcSessionRepository.java
- progress_status, recruitment_status 컬럼값을 문자열로 읽어와 enum으로 변환
- null 가능성을 고려하여 조건부 매핑 처리
- Session 생성 시 progressStatus와 recruitmentStatus를 실제 값으로 전달하도록 수정
ProgressStatus.java
- canEnroll() 로직 추가: CLOSED일 경우만 신청 불가 처리

Session.java
- SessionStatus 기반 progressStatus, recruitmentStatus 자동 매핑 생성자 추가
- progressStatus / recruitmentStatus 명시 지정 생성자 추가
- 상태 매핑 메서드(toProgressStatus, toRecruitmentStatus) 구현
- validateStatus 로직을 progressStatus·recruitmentStatus 기반으로 수정

ProgressStatusTest.java
- 상태별 canEnroll() 결과를 파라미터화 테스트로 검증

RecruitmentStatusTest.java
- RECRUITING / NOT_RECRUITING 상태별 canEnroll() 파라미터화 테스트로 변경

SessionBuilder.java
- progressStatus, recruitmentStatus 설정 기능 추가
- 변경된 생성자 시그니처에 맞게 build() 수정

SessionTest.java
- 기본 생성 시 상태 PREPARING / NOT_RECRUITING 검증
- 진행/모집 상태 조합에 따른 수강신청 가능 테스트 추가
- 종료된 강의, 비모집중 강의의 예외 발생 검증 추가
Session.java
- SessionStatus 필드 및 관련 생성자/메서드 삭제
- progressStatus / recruitmentStatus 를 단일 진리로 사용하도록 생성자 정리
- 기본 생성 시 PREPARING / NOT_RECRUITING 적용
- 조회/저장 시 SessionStatus 의존 제거

JdbcSessionRepository.java
- insert SQL에서 status 컬럼 제거
- progress_status, recruitment_status를 NOT NULL 컬럼으로 저장하도록 매핑 수정
- 조회 시 progress_status, recruitment_status를 직접 enum 변환하여 Session 생성

schema.sql
- status 컬럼 삭제
- progress_status, recruitment_status 를 NOT NULL 로 변경

SessionBuilder.java
- status 관련 필드/메서드 제거
- progressStatus, recruitmentStatus 기반 빌더로 변경
- 변경된 Session 생성자에 맞게 build() 수정

SessionRepositoryTest.java
- status 기반 검증 삭제
- progressStatus / recruitmentStatus 기반으로 조회 결과 검증하도록 수정
SessionStatus.java
- SessionStatus enum 전체 삭제(PREPARING/RECRUITING/CLOSED 상태 및 canEnroll 로직 제거)

SessionStatusTest.java
- SessionStatus 관련 단위 테스트 파일 전체 삭제
CoverImages.java
- CoverImage 목록을 관리하는 도메인 컬렉션 신규 추가
- 최소 1개 이상 필수 값 검증 로직 구현
- add(), size(), getValues() 메서드 제공
- 내부 리스트는 불변성을 위해 defensive copy 처리

CoverImagesTest.java
- 1개 이상 이미지 목록 생성 성공 테스트
- 경계값(1개, 2개) 테스트 케이스 제공
- null 또는 빈 리스트 입력 시 예외 발생 검증
Session.java
- coverImage → coverImages 필드로 변경
- 생성자 모두 CoverImages 기반으로 재작성
- getter도 getCoverImages()로 수정

JdbcSessionRepository.java
- Session 저장 시 다중 CoverImage 저장하도록 로직 변경
- 조회 시 여러 이미지를 CoverImages로 반환하도록 변경
- findCoverImageBySessionId → findCoverImagesBySessionId 리턴 타입 변경

SessionBuilder.java (test)
- coverImage → coverImages 필드 변경
- withCoverImage는 단일 이미지를 CoverImages로 감싸도록 수정
- withCoverImages 메서드 추가
- 변경된 Session 생성자에 맞게 build() 수정

SessionRepositoryTest.java
- 단일 이미지 검증을 CoverImages 기반 검증으로 수정
- getCoverImages().size(), getValues() 등 컬렉션 기반 검증으로 변경
EnrollmentStatus.java
- PENDING(대기), APPROVED(승인), REJECTED(취소) 상태 정의
- 각 상태에 대한 한글 설명 필드 포함
- description을 초기화하는 생성자 추가
Enrollment.java
- EnrollmentStatus 필드 추가
- 기본 생성 시 status를 PENDING으로 설정
- 새로운 전체 필드 포함 생성자 추가
- getStatus() 메서드 추가

JdbcSessionRepository.java
- Enrollment 조회 시 status는 일단 null로 매핑(추후 컬럼 추가 시 확장 가능)

EnrollmentTest.java
- 생성 시 기본 상태가 PENDING인지 검증하는 테스트 추가
Enrollment.java
- approve() 추가: 상태를 APPROVED로 변경
- reject() 추가: 상태를 REJECTED로 변경
- isApproved() 추가: 승인 여부 반환
- 상태 변경을 테스트할 수 있도록 기존 구조 유지하면서 기능 확장

EnrollmentTest.java
- 기본 상태가 PENDING인지 검증하는 테스트 수정
- approve() 호출 시 APPROVED로 변경되는지 검증 테스트 추가
- reject() 호출 시 REJECTED로 변경되는지 검증 테스트 추가
Enrollments.java
- equals 기반 contains() 검사 → studentId 동일 여부를 비교하는 스트림 검사로 변경
- 동일 학생이 중복 신청할 경우 예외 발생하도록 로직 개선

EnrollmentsTest.java
- 동일 studentId를 가진 서로 다른 Enrollment 객체(e1, e2)를 사용해 중복 검증 테스트 강화
- 중복 신청 시 예외 메시지 검증 유지
Enrollments.java
- approve(studentId): 해당 학생의 Enrollment를 찾아 승인 처리
- reject(studentId): 해당 학생의 Enrollment를 찾아 취소 처리
- findByStudentId(studentId) 추가: 없을 경우 예외 발생하도록 구현

EnrollmentsTest.java
- 존재하지 않는 학생 승인/취소 시 예외 발생 테스트 추가
- 예외 메시지에 "수강 신청 내역이 없습니다" 포함되는지 검증
Session.java
- approve(studentId): Enrollments의 승인 기능 위임
- reject(studentId): Enrollments의 취소 기능 위임
- Session 레벨에서 수강신청 상태 변경을 직접 수행할 수 있도록 API 확장
JdbcSessionRepository
- INSERT 시 status 컬럼 추가
- enum은 `status.name()` 으로 저장하도록 수정
- 조회 시 `EnrollmentStatus.valueOf()` 로 상태 복원

schema.sql
- enrollment 테이블에 status 컬럼 추가 및 NOT NULL 설정
Enrollments.java
- Enrollments 목록 조회를 위한 getValues 메서드 추가

Session.java
- 세션 내 Enrollments 접근을 위한 getEnrollments 메서드 추가

SessionRepository.java
- 수강신청 상태 변경을 위한 updateEnrollment 메서드 정의

JdbcSessionRepository.java
- enrollment 상태 업데이트 SQL 구현
- session_id와 student_id 기준으로 status 갱신 로직 추가

SessionRepositoryTest.java
- 수강신청 상태 변경(updateEnrollment) 기능 테스트 추가
- 승인 상태로 변경되는지 검증
SessionRepositoryTest.java
- getValues 결과 리스트를 직접 검증하도록 assertion 개선
- 상태 검증을 extracting 방식으로 변경하여 가독성 향상
Enrollments.java
- approve, reject 메서드가 Enrollment 객체를 반환하도록 변경
- 상태 변경 후 변경된 Enrollment 반환 로직 추가

Session.java
- approve, reject 메서드가 Enrollment를 반환하도록 수정
- Enrollments 반환값 그대로 전달

SessionService.java
- 승인/거절 시 세션에서 반환된 Enrollment를 받아 updateEnrollment 호출하도록 수정
- 승인/거절 비즈니스 로직 분리 및 명확화
04-lms-requirements-update.md
- 구현 기능 전체 목록 추가
- 도메인별 체크 항목 상세 기술
- Enrollment, Session, Policy 등 주요 기능의 구현 현황 문서화
CoverImages.java
- values 필드를 private으로 변경하여 캡슐화 강화

RecruitmentStatusTest.java
- canEroll → canEnroll 오타 수정
Copy link
Contributor

@javajigi javajigi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4단계 미션의 핵심 경험이 스트랭글러 패턴 기반으로 점진적인 리팩터링 경험하는 것인데요.
pr 본문과 코드를 보니 충분히 의미있는 경험 한 것 같아요 💯
특별히 피드백할 부분이 없네요.
이 미션 여기서 마무리할께요. 수고했음다.

@javajigi javajigi merged commit df02b84 into next-step:ghtjr410 Dec 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants