1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
| # READ.md
# 개발 환경
- java 11 - spring 2.7.5 - jpa 2.7.5 - jdbc 2.7.5 - web 2.7.5 - gradle 7.5.1 - h2 2.1.214
# 실행 방법
- 실행 전 build.gradle 파일 내부 dependenies에 대해 설치 필요 - 프로그램 실행 src.main.java….homework.`HomeworkApplication` 실행 - 테스트 코드 실행 src.test.java….homework.service.* 각각의 Test파일 실행
# 프로젝트 구조
## 워크플로우
![스크린샷 2022-11-22 오전 1.40.10.png](homework_workflow.png)
## 간단한 설명
- `OrderController` : 각 Service들을 호출하는역할 및 주문 프로그램의 시작과 끝 책임 - `OrderScanner` : 사용자의 주문을 입력받고, 해당 내용 validation에 대한 책임 - `ProductService` : 주문 프로그램에서 jpa repository를 통한 h2 DB와의 올바른 데이터 반환 책임 - `OrderService` : 주문서 작성에 대한 책임
# 프로젝트 구현
## 목표
SOLID 원칙에 따라 합당한 구조 설계
추후의 주문 프로그램에 개선 및 확장이 일어날 경우 해당 책임과 역할을 가진 코드만 수정 가능하도록 설계 노력 (OOP, SRP)
목적에 맞는 올바른 동작을 확인하는 세부적인 테스트 코드 작성
## 올바른 스펙 정의
### 주문에 대한 입력값 정의
해당 주문 프로그램에서는 클라이언트에게 상품번호, 주문수량 입력 받게된다.
이때 상품번호, 주문수량에 대한 스펙을 명확히 다음과 같이 정의하여, 목적에 맞게 올바른 동작을 확인하는 테스트 코드를 작성하였다.
- 상품 번호 : 6자리의 숫자로 구성 - 주문 수량 : 숫자로 구성
### DTO, Entity에 대한 스펙 정의
- `Order`: 주문 하나마다 객체화, DTO역할 (productId, productCount로 구성) - `ProductDTO`: `Product Entity`에 대한 DTO역할 (id, name, price, stock로 구성) - `Product Entity`: 데이터 베이스와의 통신 기준 (id, name, price, stock로 구성)
## 주요 동작 방식
1. `OrderController`가 주문 프로그램의 시작과 끝 사용자 응답 대기 2. 주문 시작시 `productService.checkStock()` 호출하여 현재 DB 제품목록 출력 3. `orderScanner.makeOrders()` 호출 4. `OrderScanner`가 사용자의 주문을 입력받고, 해당 내용 validation(상품 번호, 주문 수량에 대해) 체크 후 Order 객체 생성, 주문 모두를 가진 `orderList` 반환 5. `productService.saveProductList(orderList)` 호출 6. `ProductService` 에서 해당 `orderList` 의 `Order`들에 대해 해당하는 `Product` 찾고, 모든 `Product`에 대해 `update` 성공시(더티체킹) 아무런 반환없음 7. `orderService.makeOrderSheet(orderList)` 호출 8. `OrderService`에서 해당 `orderList` 의 `Order`들에 대해 해당하는 `Product` 찾고, 각 `Product` 의 가격(price)와 해당 `Order`의 수량을 곱한 값을 모두 더해 최종 주문 금액, 지불 금액 출력까지 성공시 아무런 반환없음 9. 1번부터 다시 시작
## 사전 동작
`HomeworkApplication` 클래스의 main 함수 호출 시 다음과 같은 과정 존재
**DatabaseConfig**
- spring 실행후, 초기화 후 `@Order(1)` 첫번째로 `run()` 호출 (`ApplicationRunner` 상속)
**OrderController**
- **DatabaseConfig실행후** `@Order(2)` 이므로 `run()` 호출 (`ApplicationRunner` 상속)
## 주요 함수 스펙 및 동작
개선, 확장 등 변경이 일어났을 시에도 각 함수들이 목적에 맞는 올바른 동작하는지 확인하는 세부적인 테스트 코드 작성완료
### OrderController의 대표적인 함수
각 Service들을 호출하는역할 및 주문 프로그램의 시작과 끝 동작
**run()**
- 성공: o, order, q, quit 입력 시 - 실패: [o, order, q, quit] 외의 입력 시
### OrderScanner의 대표적인 함수
OrderScanner는 사용자의 주문을 입력받고, 해당 내용에서 상품 번호, 주문 수량 값을 validation에 대한 책임을 갖는다. 참고로, 올바른 데이터의 스펙은 다음과 같다.
- 상품 번호 : 000000 ~ 999999 범위 내의 6자리의 숫자 - 주문 수량 : 1 ~ 2147483647 범위 내의 숫자
**isOrderConclude()**
- 성공: 2개의 파라미터 모두 `“ “`, `“ “` 일때 - 실패: 2개의 파라미터 모두 `“ “`, `“ “` 일때 아닐때
**checkValidProductId()**
- 성공: 000000 ~ 999999 범위 내의 숫자로 구성된 String 값 - 실패: null, Blank, 6글자 X, 숫자가 아닌 다른 문자로 구성
**checkValidProductCount()**
- 성공: 1 ~ 2147483647 범위 내의 숫자로 구성된 String 값 - 실패: null, Blank, 숫자가 아닌 문자열 포함된 String ,숫자가 아닌 다른 문자로 구성, 마이너스 숫자, int값 범위 넘어선 숫자, 0
**checkValidOrderList()**
- 성공: 리스트 파라미터가 비어있지 않을 때 - 실패: 리스트 파라미터가 비어있을 때
### ProductService의 대표적인 함수
주문 프로그램에서 jpa repository를 통한 h2 DB와의 올바른 데이터 반환 책임
**findProductById()**
- 성공: `productId` 파라미터에 해당하는 `Product Entity` 존재 - 실패: `productId` 파라미터에 해당하는 `Product Entity` 부재
**findWithIdForUpdate()**
- 성공: `productId` 파라미터에 해당하는 `Product Entity` 존재 - 실패: `productId` 파라미터에 해당하는 `Product Entity` 부재
**saveProductList()**
이중 주문 완료 처리시 싱글 쓰레드 환경과 멀티 쓰레드 환경에 대해 동일한 order는 동일한 결과값을 나타내야한다. SoldOutException의 횟수를 카운트하여 테스트를 검증하였다.
- 성공: 한 트랜잭션으로 `orderList` 파라미터의 모든 `Order` 재고 부족없이 모두 stock update 처리 성공 - 실패: `orderList` 파라미터의 모든 `Order` 중 하나라도 재고부족 시 `SoldOutException` 및 현재 트랜잭션 실패 처리(롤백)
### OrderService의 대표적인 테스트 코드
주문서 작성에 대한 책임
**getTotalPrice()**
- 성공: `orderList` 파라미터의 모든 `Order` 의 가격*주문수량을 취합 - 실패: `orderList` 파라미터의 모든 `Order` 중 가격*주문수량이 OverFlow가 일어남
**checkValidMultiple()**
- 성공: 음수가 아닌 숫자 둘을 곱한 후 OverFlow가 일어나지않는 값 - 실패: params 중 하나 음수값, params 모두 음수값, params 곱한후 OverFlow가 일어나는 값
|