어느새 3 주차
어느새 다가온 3주차 원티드 백엔드 인턴십과 병행하다보니 이전 주차들에는 신경쓰지 못했던 것들과 코드 리뷰를 받으며 알게 된것들을 적용하였습니다. 또한 이번에는 디자인 패턴에 대한 의도가 있는 것 같아 MVC 패턴에 대해 공부하고, 적용하는 시간을 가져보았습니다
MVC 패턴이란 뭘까?
정의
Model (모델)
•
데이터와 비즈니스 로직을 관리합니다.
•
일반적으로 데이터베이스, 파일, 메모리 등에 저장된 데이터와 그 데이터를 처리하는 로직을 포함합니다.
•
모델은 어떤 정보를 표시할 것인지, 어떤 정보를 갱신해야 하는지 등의 규칙을 정의합니다.
•
모델은 직접적으로 사용자 인터페이스와 상호작용하지 않습니다.
View (뷰)
•
사용자에게 보여지는 부분입니다, 즉 사용자 인터페이스(UI)를 담당합니다.
•
HTML, CSS, JavaScript 등을 사용해 웹 애플리케이션에서는 사용자가 볼 수 있는 웹 페이지를 생성합니다.
•
뷰는 모델이 가진 정보를 사용자에게 표시하는 방법을 정의하지만, 어떤 정보가 표시되어야 하는지 또는 어떻게 처리되어야 하는지는 모르는 것이 일반적입니다.
Controller (컨트롤러)
•
사용자의 입력을 처리하고 모델과 뷰 사이의 상호작용을 관리합니다.
•
사용자의 액션에 반응하여 모델을 업데이트하거나, 모델의 변경 사항에 따라 뷰를 갱신합니다.
•
컨트롤러는 사용자가 데이터를 어떻게 요청하고 조작하는지에 대한 로직을 캡슐화합니다.
그래서 뭐가 좋은걸까?
•
관심사의 분리(Separation of Concerns)
: 각 구성요소의 책임이 분명하게 구분되므로 유지보수와 확장성이 높아집니다.
•
재사용성과 유연성
: 모델과 뷰가 분리되어 있으므로, 동일한 데이터를 다른 형식으로 쉽게 표시할 수 있습니다.
•
테스트 용이성
: 각 컴포넌트를 독립적으로 테스트할 수 있습니다.
하지만 항상 좋은걸까?
•
복잡성 증가
: 작은 프로젝트에서 MVC 패턴을 사용하면, 오히려 관리해야 할 컴포넌트가 많아져 복잡성이 증가할 수 있습니다.
•
컴포넌트 간의 의존성
: 컨트롤러와 뷰 사이에 의존성이 생길 수 있어, 때로는 각 컴포넌트의 역할이 혼란스러워질 수 있습니다.
MVC 패턴을 차용한 기능 명세
이번에 처음으로 MVC 패턴을 사용해 보았습니다. 또한 요구조건도 이전보다 더 상세해지고 어려워져서, 기능 명세를 계속해서 수정 했던 것 같습니다.
Model
로또 번호 생성기
•
랜덤하게 6개의 로또 번호를 생성합니다.
•
사용자로부터 받은 당첨 로또를 생성합니다.
당첨 검증기
사용자가 구입한 로또 번호와 당첨 번호를 비교하여 결과를 확인합니다.
당첨은 1등부터 5등까지 있으며, 당첨 기준과 금액은 아래와 같습니다.
- 1등: 6개 번호 일치 / 2,000,000,000원
- 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원
- 3등: 5개 번호 일치 / 1,500,000원
- 4등: 4개 번호 일치 / 50,000원
- 5등: 3개 번호 일치 / 5,000원
Plain Text
복사
결과 저장기
각 결과의 발생 횟수를 저장합니다.
수익률 계산기
•
총 투자 대비 수익률을 계산합니다.
•
수익률은 소수점 둘째 자리에서 반올림합니다.
◦
ex 100.0%, 51.5%, 1,000,000.0%
Controller
inputPurchasedPrice
•
사용자로부터 로또 구입 금액을 입력받습니다.
•
금액을 검증하고, 잘못된 값을 입력하여 예외발생시 다시 입력을 받습니다.
generateLottos
•
구입 금액에 따른 로또 티켓들을 생성합니다.
•
생성된 로또를 출력합니다.
•
로또는 오름차순으로 정렬되어야 합니다.
generateWinningCondition
•
당첨 번호를 생성합니다.
•
생성된 로또를 검증하고, 잘못된 값을 입력하여 예외발생시 다시 입력을 받습니다.
inputBonusNumber
•
보너스 번호를 입력받습니다.
•
받은 번호를 검증하고, 잘못된 값을 입력하여 예외발생시 다시 입력을 받습니다.
generateWinningResult
•
각 로또 티켓의 당첨 결과를 계산합니다.
•
당첨 결과를 뷰를 통해 출력합니다.
displayFinalResult
•
로또의 당첨 결과를 기반으로 수익률을 계산합니다.
•
계산된 수익률을 뷰를 통해 출력합니다.
View
입력 인터페이스 뷰
사용자로부터 구입 금액, 당첨 번호, 보너스 번호를 입력 받습니다.
결과 출력 뷰
당첨 결과와 수익률을 사용자에게 보여줍니다.
출력 결과
구입금액을 입력해 주세요.
8000
8개를 구매했습니다.
[8, 21, 23, 41, 42, 43]
[3, 5, 11, 16, 32, 38]
[7, 11, 16, 35, 36, 44]
[1, 8, 11, 31, 41, 42]
[13, 14, 16, 38, 42, 45]
[7, 11, 30, 40, 42, 43]
[2, 13, 22, 32, 38, 45]
[1, 3, 5, 14, 22, 45]
당첨 번호를 입력해 주세요.
1,2,3,4,5,6
보너스 번호를 입력해 주세요.
7
당첨 통계
---
3개 일치 (5,000원) - 1개
4개 일치 (50,000원) - 0개
5개 일치 (1,500,000원) - 0개
5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
6개 일치 (2,000,000,000원) - 0개
총 수익률은 62.5%입니다.
Plain Text
복사
예외 처리
•
구입 금액은 1000 원 이상 이여야 한다.
•
구입 금액은 숫자로만 이루어져야 한다.
•
당첨 번호로 생성된 리스트의 사이즈는 6 이여야 한다.
•
당첨 번호로 생성된 리스트는 중복이 되어서는 안된다.
•
보너스 번호 또한 1 에서 45 사이 여야 한다.
•
보너스 번호는 숫자로만 이루어져야 한다.
•
보너스 번호는 당첨번호로 생성된 리스트에 포함되면 안된다.
기능 Flow
•
사용자는 입력 인터페이스 뷰를 통해 구입 금액, 당첨 번호, 보너스 번호를 입력합니다.
•
메인 컨트롤러는 입력 데이터 모델에 사용자의 입력을 저장합니다.
•
메인 컨트롤러는 로또 번호 생성기를 사용하여 사용자가 구매한 로또 번호를 생성합니다.
•
생성된 번호들과 사용자의 당첨 번호를 당첨 검증기가 검사하여 결과를 결과 저장기에 저장합니다.
•
메인 컨트롤러는 결과 저장기에서 결과를 가져와 수익률 계산기를 사용하여 수익률을 계산합니다.
•
마지막으로 결과 출력 뷰를 통해 사용자에게 당첨 통계와 수익률을 보여 줍니다.
도메인 로직 단위 테스트
당첨 검증기 테스트
•
사용자의 로또 번호와 당첨 번호를 비교하여 일치하는 숫자를 제대로 계산 하는지 테스트 합니다.
•
보너스 번호 일치 여부가 올바르게 확인 되는지 테스트 합니다.
수익률 계산기 테스트
•
수익률이 정확하게 계산되는지 테스트합니다.
입력값 검증기 테스트
•
사용자가 입력한 값들이 예상값과 일치하는지 테스트 합니다.
구입금액
•
숫자 외의 문자가 들어왔을 때 예외를 반환하는지 테스트 합니다.
•
1000 원 이하일 때 예외를 반환하는지 테스트 합니다.
당첨로또
•
당첨 로또의 사이즈가 6 이 아닐 때 예외를 반환하는지 테스트 합니다.
•
당첨 로또 숫자에 중복이 있는 경우 예외를 반환하는지 테스트 합니다.
•
당첨 로또에 숫자 외의 문자가 들어왔을 때 예외를 반환하는지 테스트 합니다.
•
당첨 로또의 숫자가 1 이상 45 이하인지 테스트 합니다.
보너스 숫자
•
보너스 숫자에 숫자 외의 문자가 들어왔을 때 예외를 반환하는지 테스트 합니다.
•
보너스 숫자가 1 이상 45 이하인지 테스트 합니다.
•
당첨 로또에 보너스 숫자가 들어가면 예외를 반환하는지 테스트 합니다.
요구사항
명확한 에러 유형 처리
•
[ERROR] 로 시작해야 한다.
•
IllegalArgumentException, IllegalStateException ...
[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.
Plain Text
복사
•
입력에서 IllegalArgumentException 예외를 던지면 그 후에 그 부분 부터 입력을 다시 받습니다.
메서드
•
길이가 15 라인을 넘어가지 않도록 구현해야 합니다.
•
함수가 한가지 일만 하도록 구현합니다.
그 외
•
else 를 사용하지 않습니다.
•
Enum 을 사용합니다.
•
로또 클래스를 활용해 구현해야 합니다.
라이브러리
•
Random 값 추출은 camp.nextstep.edu.missionutils.Randoms 의 pickUniqueNumbersInRange() 를 활용한다.
•
사용자가 입력하는 값은 camp.nextstep.edu.missionutils.Console 의 readLine() 을 활용한다.
테스트
•
도메인 로직에 단위 테스트를 구현해야 합니다.
◦
UI 로직은 제외합니다.