Search
3️⃣

프리코스 3 주차 그리고 MVC 패턴

태그
프리코스

어느새 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.RandomspickUniqueNumbersInRange() 를 활용한다.
사용자가 입력하는 값은 camp.nextstep.edu.missionutils.ConsolereadLine() 을 활용한다.

테스트

도메인 로직에 단위 테스트를 구현해야 합니다.
UI 로직은 제외합니다.