Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

TDD, 클린코드를 적용하여 레거시 코드의 학습 관리 시스템 리팩토링

Notifications You must be signed in to change notification settings

Perhona/java-lms

Repository files navigation

학습 관리 시스템(Learning Management System)


미션을 완수하고 배운 내용 by Perhona

  • 클래스의 복잡도가 올라가면 클래스를 분리하고 메서드의 크기를 줄여 유지보수, 테스트 하기 좋은 코드로 개선
  • 실무와 비슷한 환경의 레거시 코드를 TDD, 클린코드를 적용하여 구조 개선
  • 데이터가 이미 존재하는 상태에서 요구사항 변경 시 리팩토링 방안
  • DB 테이블보다 먼저 도메인 관점에서의 설계 및 개발
  • 의존의 가장 마지막 단계에 있는 기능에서 시작하는 테스트
  • public 메서드를 제공할 때, 사용자가 메서드 호출 순서를 몰라도 사용함에 오류가 없도록 설계

진행 방법

  • 학습 관리 시스템의 수강신청 요구사항을 파악한다.
  • 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다.
  • 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다.
  • 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다.

온라인 코드 리뷰 과정


🚀 1단계 - 레거시 코드 리팩터링

<질문 삭제하기> 요구사항

  • 질문 데이터의 삭제 상태를 변경한다. (deleted = true)
  • 로그인 사용자 == 질문글 작성자인 경우 삭제 가능
  • 질문글에 답변글이 없는 경우 삭제 가능
  • 질문을 삭제하면 답변 또한 삭제한다.
    • 답변의 삭제 상태를 변경한다. (deleted = true)
    • 답변글 작성자 != 로그인 사용자인 경우 답변글을 삭제할 수 없다.
    • 질문글 작성자 != 답변글 작성자인 경우 답변글을 삭제할 수 없다.

리팩터링 요구사항

  • QnaService의 deleteQuestion() 메서드의 핵심 비지니스 로직을 도메인 모델 객체에 구현
    • TDD로 구현
  • 리팩토링 후에도 QnaServiceTest의 모든 테스트는 통과해야 한다.

Todo

  • 답변을 삭제한다. (deleted = true)
  • 질문을 삭제한다. (deleted = true)
  • 답변과 질문의 삭제 이력을 가져온다.

Feedback 23.11.26

  • Wrapper 클래스에서 변수명은 values, value, 값을 가져오는 메서드명은 value(), get()으로 사용해 보기
  • delete 작업 후에 deleteHistories를 즉시 반환하도록 변경
  • Answer의 delete()가 답변만 삭제하고 싶을 경우에도 대응할 수 있도록 변경

🚀 2단계 - 수강신청(도메인 모델)

<수강신청> 기능 요구사항

  • 과정(Course)은 기수 단위로 운영되며, 여러개의 강의(Session)을 가질 수 있다.
  • 강의(Session)
    • 시작일과 종료일을 가진다.
    • 강의 커버 이미지를 가진다.
      • 이미지 크기는 1MB 이하이다.
      • 이미지 타입은 gif, jpg(jpeg 포함), png, svg만 허용한다.
      • 이미지의 width는 300픽셀, height는 200픽셀 이상이어야 하며, width와 height의 비율은 3:2여야 한다.
    • 무료강의와 유료강의로 나뉜다.
      • 무료 강의는 최대 수강 인원의 제한이 없다.
      • 유료 강의는 최대 수강 인원을 초과할 수 없다.
      • 유료 강의는 수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능하다.
      • 유료 강의의 경우 결제는 이미 완료한 것으로 가정하고 이후 과정을 구현한다.
        • 결제를 완료한 결제 정보는 payments 모듈을 통해 관리되며, 결제 정보는 Payment 객체에 담겨 반한된다.
    • 준비중, 모집중, 종료 3가지 상태를 가진다.
  • 수강신청
    • 로그인 유저는 강의를 대상으로 수강신청을 할 수 있다.
    • 수강신청은 모집중 상태인 경우에만 가능하다.

프로그래밍 요구사항

  • DB 테이블 설계 없이 도메인 모델부터 구현한다.
  • 도메인 모델은 TDD로 구현한다. 단, Service 클래스는 단위 테스트가 없어도 된다.

Feedback 23.12.15

  • 이미지 타입을 관리할 수 있는 enum을 사용해보기
  • 항상 인자가 필요한 경우 기본 생성자를 삭제하기
  • 이미지 가로, 세로 비율을 구할 때 부동소수점인 Double 외에 곱셈 혹은 BigDecimal을 사용해보기
    • 코드 가독성과 속도를 고려하여 BigDecimal이 아닌 곱셈으로 수정
  • 강의의 기간을 관리하는 클래스를 만들어서 유효성 검사를 진행해보기
  • 강의에 필수 값이 있다면 생성 시 검증을 진행해보기
  • 클래스 네임에 Info, Data는 사용하지 않고, 의미를 보다 명확히 하기
  • 수강 신청이 완료되면 수강 인원이 늘어야 할 것
  • 파일 마지막의 newLine에 대해 공부해 보고, 누락된 부분을 추가하기

Feedback 23.12.07

  • userNumber를 가져오는 방식에 대한 고민
  • 값이 필요한 객체의 경우 기본생성자를 제공하지 않고, 추가적인 validate를 진행

🚀 3단계 - 수강신청(DB 적용)

요구사항

  • 2단계에서 구현한 도메인 모델을 DB 테이블과 매핑하고, 데이터를 저장한다.
    • schema.sql 에 DB 테이블 추가
      • Session
      • CoverImage
      • NsUserSession
    • CRUD 코드 추가
      • sessionRepository
      • coverImageRepository
    • CRUD 코드에 대한 테스트 코드 추가
      • sessionRepository

Feedback 23.12.12

  • CoverImage와 Sesssion의 관계 고민
    • Session은 CoverImage를 가진다.
    • 수강신청 시 CoverImage의 정보는 필요하지 않다.
    • CoverImage는 sessionId를 반드시 안다.
  • Repository 와 DAO의 차이를 알아보고 설계를 수정
    • 현재의 설계는 DAO에 가깝다. Repository는 DAO를 이용해서 서비스단에서 사용하고자 하는 객체를 만드는데 집중한다.
  • CoverImage의 중복된 validation 제거
  • Wrapper 타입과 Primitive 타입의 쓰임새 확인 후 수정
  • SessionPaymentCondition 내 메서드의 적합한 명명 고민
  • 수강신청 로직 수정(검증)
  • 객체 저장 후 id값을 반환하도록 하기
  • Class 레벨의 @Transactional 활용

🚀 4단계 - 수강신청(요구사항 변경)

변경된 요구사항

  • (기존) 강의가 모집중일 때만 수강 신청이 가능하다.
    • (변경) 강의가 진행 중인 상태에서도 수강신청이 가능하다.
      • 강의 진행 상태 (준비중, 진행중, 종료)와 모집 상태(모집중, 비모집중)으로 분리한다.
      • 강의가 '진행중, 준비중'이고 '모집중'인 상태에서만 수강신청이 가능하다.
  • (기존) 강의는 강의 커버 이미지를 가진다.
    • (변경) 강의는 하나 이상의 강의 커버 이미지를 가질 수 있다.
  • (기존) 수강 신청은 별도의 승인이 필요 없다.
    • (변경) '선발된' 인원만 수강이 가능해야 한다.
    • 강사는 수강신청한 사람 중 선발된 인원에 대해서만 수강 승인이 가능해야 한다.
    • 강사는 수강신청한 사람 중 선발되지 않은 사람은 수강을 취소할 수 있어야 한다.
    • 수강 인원은 미리 정해져있다.
    • 수강신청은 아무나 가능하지만, 강사는 선발된 인원을 확인해서 수강 승인 혹은 취소를 진행한다.

프로그래밍 요구사항

  • DB상에 이미 데이터가 있다는 전제 하에 진행한다. (기존에 쌓인 데이터를 지우지 않아야 한다.)
    • Session 컬럼 session_status를 session_recruitment_status로 변경, session_progress_status 추가
    • CoverImage에 id pk로 추가
    • NsUserSession에 registered 필드 추가, 기존 인원은 ture로 업데이트 하는 시나리오

Feedback 23.12.14

  • create table DDL문을 수정하지 않고 Alter table 을 이용해본다.
    • session
    • ns_user_session
  • sessionStatus를 분류하되, 기존 데이터의로도 수강 신청이 가능하도록 구현
    • 강의 진행 컬럼을 추가하되, 해당 값이 존재하지 않는 기존 강의의 경우 EMPTY 값을 갖도록 함
  • 수강 신청 기능과 강사의 신청 승인은 각각의 기능으로 구현된다.
  • NsUserSession이 3가지 상태(신청, 승인, 취소)를 갖도록 구현해본다.
  • 수강 승인 기능이 생긴 시점을 기준으로 수강 승인 기능을 구현한다.
    • session 테이블에 approval_required 컬럼 추가, default value false
    • ns_user_session 테이블에 enrollment_status 컬럼 추가, default value 'APPROVED'
  • Session에 강사 정보를 추가 및 검증한다.
  • 수강 승인을 할 수 있는 인원이 최대 수강 인원으로 제한되도록 해본다.
    • 추가로, 수강 인원만큼 승인이 되었다면 추가로 수강 승인이 되지 않도록 제한한다.

Feedback 24.01.03

  • NsUserSession 과 같은 클래스 네임은 개발자의 관점으로, 도메인 입장에서 더욱 적합한 이름을 고민해야 함
    • Student, Students
  • enum에게도 역할을 위임할 수 있다. 단, 작은 메서드일 경우 과하게 느껴질 수 있음
  • 수강 승인/취소 역할을 NsUserSession이 담당하도록 해본다
  • 값을 변경할 수 없는 필드에 대해서는 final을 선언하는 습관을 들인다
  • NsUserSessions(Students) 에 새로운 수강자를 추가한 뒤 id 혹은 이름으로 찾는 메서드의 필요성
  • Session 클래스의 필드 수가 많아지며 복잡도는 증가한다. 클래스 분리를 해 볼 것
    • Enrollment 클래스를 추가하고 enroll에 대한 테스트 분리
  • 현재 수강중인 인원 수를 찾기 위해 매번 수강생 전체 리스트를 갖고 오는 것은 성능상 이슈가 생길 수 있다.
    • session 테이블 역정규화, 수강 인원 직접 관리

About

TDD, 클린코드를 적용하여 레거시 코드의 학습 관리 시스템 리팩토링

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 100.0%

AltStyle によって変換されたページ (->オリジナル) /