React & Redux 데스크탑 장바구니 애플리케이션
- 🎯 TDD 시도!
- 🎯 redux-saga테스트 with redux-saga-test-plan
- 🎯private을 유지하는 걸 고집해야 할까?(재사용 계획이 없는 순수 함수를 테스트하다가)
- 🎯redux toolkit reducer 테스트하기!(너무 간편하드아~)
- 🎯상태 업데이트를 순수 함수를 통해서 vs 하나의 상태로 따로 관리해서 ?
- 🎯[swith(true)-case로 가독성 확보하기?]if? else? early return? switch-case?
- 🎯리덕스 스토어 저장 전 데이터 전처리 로직을 어디까지 밀어버릴까? reducer? saga? service?
- 🎯 products 관련 action들을 다루며 은닉화 실패..
- 🎯유틸 함수 분리로 선언적인 코드 작성에 대한 고민
- 🎯 payload 타입 지정, 설계에 대한 고민
- 🎯 test 할 때 주입 받는 값의 위치에 대한 고민
- 🎯 리듀서에서 state와 action 구조 분해 할당이 가독성을 높힐 수 있을지에 대한 고민
- 🎯 어떻게하면 request에 관련된 method, url 을 효율적으로 관리할 수 있을지에 대한 고민
- 🎯 하나의 모델과 관련된 액션들을 하나로 관리하는 것이 좋을지? 아니면 비동기 액션은 따로 관리하는 것이 좋을지?
- 🎯 함수라면 함수임을 알려주는 네이밍이어야 될까?
- 🎯 TS 이것저것
- 🎯 test 환경에서 redux-logger 없애기
- 🎯 next.js + jest + testing-library 환경설정
- 🎯 useDispatch 과 useSelector 의 typed versions!
- 🎯 에러핸들링
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---|---|---|---|---|---|
All files | 83.56 | 54.74 | 65.78 | 85.23 | |
components/@atom/BottomUpModal | 75 | 41.66 | 66.66 | 77.77 | |
BottomUpModal.tsx | 75 | 41.66 | 66.66 | 77.77 | 18-23 |
components/@atom/Button | 62.96 | 31.57 | 100 | 62.96 | |
Button.tsx | 62.96 | 31.57 | 100 | 62.96 | 13-27 |
components/@atom/ScrollBottomObserver | 72.22 | 0 | 50 | 72.22 | |
ScrollBottomObserver.tsx | 72.22 | 0 | 50 | 72.22 | 11-13,16-17 |
components/@atom/Spinner | 62.96 | 23.52 | 100 | 62.96 | |
Spinner.tsx | 62.96 | 23.52 | 100 | 62.96 | 13-27 |
components/Carts/CartList | 61.64 | 37.5 | 23.52 | 68.25 | |
CartList.tsx | 61.64 | 37.5 | 23.52 | 68.25 | 28,35,40-47,51,55,58,63-65,68-72,100 |
components/Carts/CartListContainer | 100 | 75 | 100 | 100 | |
CartListContainer.tsx | 100 | 75 | 100 | 100 | 11 |
components/Carts/CartListItem | 96 | 75 | 83.33 | 96 | |
CartListItem.tsx | 96 | 75 | 83.33 | 96 | 31 |
components/ConfirmAddCartsModal | 100 | 75 | 100 | 100 | |
ConfirmAddCartsModal.tsx | 100 | 75 | 100 | 100 | 11 |
components/GNB | 100 | 75 | 100 | 100 | |
GNB.tsx | 100 | 75 | 100 | 100 | 12 |
components/Orders/OrderList | 100 | 87.5 | 100 | 100 | |
OrderList.tsx | 100 | 87.5 | 100 | 100 | 12 |
components/Orders/OrderListContainer | 100 | 75 | 100 | 100 | |
OrderListContainer.tsx | 100 | 75 | 100 | 100 | 11 |
components/Orders/OrderListItem | 100 | 75 | 100 | 100 | |
OrderListItem.tsx | 100 | 75 | 100 | 100 | 11 |
components/Orders/OrderedProduct | 88 | 75 | 40 | 88 | |
OrderedProduct.tsx | 88 | 75 | 40 | 88 | 25-59 |
components/Products/ProductItem | 100 | 75 | 100 | 100 | |
ProductItem.tsx | 100 | 75 | 100 | 100 | 12 |
components/Products/ProductList | 90.62 | 83.33 | 57.14 | 93.33 | |
ProductList.tsx | 90.62 | 83.33 | 57.14 | 93.33 | 24,50 |
components/Products/ProductListContainer | 100 | 75 | 100 | 100 | |
ProductListContainer.tsx | 100 | 75 | 100 | 100 | 12 |
hooks/service | 88.6 | 83.33 | 52.94 | 95.89 | |
useCarts.ts | 88.88 | 83.33 | 62.5 | 94.11 | 25,49 |
useOrders.ts | 86.95 | 100 | 40 | 95.23 | 24 |
useProducts.ts | 90 | 100 | 50 | 100 | |
service | 97.72 | 100 | 94.44 | 100 | |
cartsService.ts | 95.65 | 100 | 87.5 | 100 | |
ordersService.ts | 100 | 100 | 100 | 100 | |
service/apis | 56.41 | 100 | 0 | 56.41 | |
carts.ts | 53.84 | 100 | 0 | 53.84 | 10-19 |
orders.ts | 53.33 | 100 | 0 | 53.33 | 11-23 |
products.ts | 63.63 | 100 | 0 | 63.63 | 10-15 |
shared/constants | 64.28 | 100 | 28.57 | 64.28 | |
css.ts | 100 | 100 | 100 | 100 | |
url.ts | 54.54 | 100 | 28.57 | 54.54 | 19-64 |
shared/fixtures | 100 | 100 | 100 | 100 | |
cartItem.ts | 100 | 100 | 100 | 100 | |
carts.ts | 100 | 100 | 100 | 100 | |
cartsFromServer.ts | 100 | 100 | 100 | 100 | |
cartsReducerInitialState.ts | 100 | 100 | 100 | 100 | |
cartsWithQuantity.ts | 100 | 100 | 100 | 100 | |
orderItem.ts | 100 | 100 | 100 | 100 | |
orders.ts | 100 | 100 | 100 | 100 | |
product.ts | 100 | 100 | 100 | 100 | |
shared/utils | 80 | 62.5 | 66.66 | 78.26 | |
createMockRouter.ts | 100 | 100 | 100 | 100 | |
fetcher.ts | 60 | 50 | 50 | 60 | 16-23 |
redux.ts | 83.33 | 100 | 60 | 81.81 | 9-15 |
test-utils.ts | 100 | 100 | 100 | 100 | |
store | 96.87 | 66.66 | 100 | 96.77 | |
index.ts | 96.87 | 66.66 | 100 | 96.77 | 25 |
store/modules | 96.42 | 66.66 | 100 | 96.42 | |
index.ts | 96.42 | 66.66 | 100 | 96.42 | 36 |
store/modules/carts | 80.24 | 50 | 72.72 | 83.56 | |
saga.ts | 82.14 | 50 | 80 | 82.14 | 46-53 |
slice.ts | 77.55 | 50 | 70.58 | 82.92 | 55-65,89-90 |
types.ts | 100 | 100 | 100 | 100 | |
store/modules/orders | 66.66 | 37.5 | 42.85 | 66.66 | |
saga.ts | 47.36 | 37.5 | 50 | 47.36 | 15-36 |
slice.ts | 78.94 | 100 | 33.33 | 78.94 | 28-32 |
types.ts | 100 | 100 | 100 | 100 | |
store/modules/products | 100 | 66.66 | 100 | 100 | |
saga.ts | 100 | 66.66 | 100 | 100 | 10-21 |
slice.ts | 100 | 100 | 100 | 100 | |
types.ts | 100 | 100 | 100 | 100 |
react-shopping-cart/client 디렉토리에서 실행해주세요.
- 최초 실행 시
# npm
npm run server:first
# yarn
yarn server:first
- 이후 실행 시
# npm
npm run server
# yarn
yarn server
http://localhost:3003
method | uri |
---|---|
GET | /products |
{
"response": [
{
"id": 1,
"price": 10000,
"name": "치킨",
"imageUrl": "http://example.com/chicken.jpg"
},
{
"id": 2,
"price": 20000,
"name": "피자",
"imageUrl": "http://example.com/pizza.jpg"
}
]
}
method | uri |
---|---|
POST | /products |
{
"requestBody": {
"products": {
"price": 10000,
"name": "치킨",
"imageUrl": "http://example.com/chicken.jpg"
}
}
}
method | uri |
---|---|
GET | /products/{id} |
{
"response": {
"id": 1,
"price": 10000,
"name": "치킨",
"imageUrl": "http://example.com/chicken.jpg"
}
}
method | uri |
---|---|
DELETE | /products/{id} |
{
"response": {}
}
method | uri |
---|---|
GET | /carts |
{
"response": {
"id": 1,
"product": {
"name": "test",
"price": 1234,
"imageUrl": "test.com",
"id": 1
}
},
{
"id": 5,
"product": {
"name": "tes11111t",
"price": 1234,
"imageUrl": "test.com",
"id": 10
}
},
}
method | uri |
---|---|
POST | /carts |
{
"requestBody": {
"product": {
"id": 10,
"name": "tes11111t",
"price": 1234,
"imageUrl": "test.com"
}
}
}
method | uri |
---|---|
DELETE | /carts/{cartId} |
{
"response": {}
}
method | uri |
---|---|
POST | /orders |
{
"requestBody": {
"orderDetails": [
{
"id": 1,
"price": 10000,
"name": "치킨",
"imageUrl": "http://example.com/chicken.jpg",
"quantity": 5
},
{
"id": 2,
"price": 20000,
"name": "피자",
"imageUrl": "http://example.com/pizza.jpg",
"quantity": 3
}
]
}
}
method | uri |
---|---|
GET | /orders |
{
"response": [
{
"id": 1,
"orderDetails": [
{
"id": 1,
"price": 10000,
"name": "치킨",
"imageUrl": "http://example.com/chicken.jpg",
"quantity": 5
},
{
"id": 2,
"price": 20000,
"name": "피자",
"imageUrl": "http://example.com/pizza.jpg",
"quantity": 3
}
]
},
{
"id": 2,
"orderDetails": [
{
"id": 1,
"price": 10000,
"name": "치킨",
"imageUrl": "http://example.com/chicken.jpg",
"quantity": 5
},
{
"id": 2,
"price": 20000,
"name": "피자",
"imageUrl": "http://example.com/pizza.jpg",
"quantity": 3
}
]
}
]
method | uri |
---|---|
GET | /orders/{id} |
{
"response": {
"id": 1,
"orderDetails": [
{
"id": 1,
"price": 10000,
"name": "치킨",
"imageUrl": "http://example.com/chicken.jpg",
"quantity": 5
},
{
"id": 2,
"price": 20000,
"name": "피자",
"imageUrl": "http://example.com/pizza.jpg",
"quantity": 3
}
]
}
}