저번 시간에는 항목을 더하는 작업을 했습니다
이제는 이 항목을 지우는 것도 작업해 보겠습니다
먼저 할 작업은
existingCartItemIndex를 가져오는 것입니다
해당 코드를 복사해서 항목을 더하고
이 REMOVE_ITEM에 추가하겠습니다
이렇게 하는 이유는 기존의 장바구니 항목을 불변하는 방식으로
업데이트하기 위해서입니다
항목을 삭제할 때는 이 항목이
이미 배열에 있는지 확인할 필요가 없습니다
이 앱에서는 이미 배열에 존재하는 항목만
지울 수 있습니다
장바구니의 항목 수를 줄이기 위해서는
장바구니 창으로 가야 하기 때문입니다
그래서 항목이 화면에 포함되어 있는지
확인할 필요는 없지만 확인해야 하는 내용은
항목의 양(quantity)에 대한 속성입니다
만약 항목이 하나 이상 있다면
거기에서부터 수를 줄여야 하기 때문입니다
만약 숫자가 1이라면 항목 전체를
장바구니 항목 배열에서 삭제해야 합니다
그러므로 existingCartItem을 가져오겠습니다
state.item에서 가져왔습니다
그리고 existingCartItemIdex에 접근하겠습니다
그 다음에는 existingCartItem의 양에 대한 숫자가
1과 일치하는지 보겠습니다
만약 해당 경우라면
항목 전체를 장바구니에서 삭제하겠습니다
그 후에는 업데이트된 항목을 가져오겠습니다
이것은 즉 이전에 있던 항목 전부에서
이 항목을 뺀 배열입니다
그러므로 이 existingCartItem을 뺀 것입니다
이 부분은 다양한 방법으로 작업할 수 있습니다
항목을 지울 수 있는 한 가지 방법은
기존 항목의 복사본을 만드는 것입니다
즉 기존 항목에 기반한 배열을 새로 만들어서
splice 메소드를 배열에 호출합니다
Splice는 인덱스를 필요로 합니다
여기에서는 existingCartItemIndex입니다
그리고 splice를 실행해야 하는 항목의 수라 함은
삭제해야 하는 항목입니다
즉 이 인덱스에서 1을 삭제하는데
해당 항목을 인덱스에서 지우겠다는 뜻입니다
이렇게 항목을 삭제하는 것은
그 항목이 단 하나만 장바구니에 있을 때 가능합니다
만약 항목의 수가 하나보다 많다면
이제는 양에 대한 숫자를 업데이트합니다
그러므로 업데이트된 항목이 필요한데
이것은 즉 기존 항목의 복사본입니다
그리고 이 양을 업데이트할 때에는
existingCartItem.quantity에 1을 뺀 것과 동일합니다
즉 새 항목을 기존 항목에 기반하여 만들었고
수를 하나 줄였습니다
이제는 기존 항목을 업데이트하기만 하면 됩니다
이 작업을 위해서 이 updatedItems에 대한
배열을 생성한 부분을
이 if 블록 밖으로 빼겠습니다
이렇게 하면 if나 각 if를 확인하는 else에서도
사용할 수 있게 됩니다
그리고 else에서는 업데이트된 항목을
이 existingCartItemIndex에 대해 설정하여
업데이트된 항목과 동일하게 하겠습니다
이 항목은 수가 이미 줄어든 항목입니다
이렇게 하면 작업이 끝납니다
이제는 업데이트된 상태를 반환하되
이 안쪽 if 블록 바깥이지만
이 if 블록 안에 위치하면 됩니다
그리고 이것은 결국 이전의
항목을 더하는 if 블록과 동일합니다
반환하려는 객체는 이전 상태를 복사하고
항목을 업데이트합니다
이것이 장바구니 로직입니다
이 장바구니 로직은 다양한 컴포넌트로 연결할 때
컨텍스트의 도움을 통해 가능해집니다
즉 이 컨텍스트를 다른 컴포넌트로 연결할 때
먼저는 이 reducer를 사용하면서 시작해야 합니다
위치 또한 컨텍스트 제공 컴포넌트 속이어야 합니다
먼저는 이 useReducer 훅에 대한 배열의
구조 분해 할당을 시작하겠습니다
장바구니 상태는 조금 있다가 보겠습니다
이름은 장바구니(cart)와 디스패치(dispatch) 함수로 짓겠습니다
또 이름을 dispatchCartAction으로 짓겠습니다
이제 장바구니 상태를 전달할 때
이 CartContext.Provider 컴포넌트로 보내겠습니다
이 상태에 관심이 있는
다른 감싸진 컴포넌트 모두
여기에서 전달되기 때문입니다
그래서 여기에 cartContext 값 객체를 설정해서
항목 속성을 받겠습니다
이것은 이 임시 초기값에서
이미 정의한 적 있습니다
즉 이 컨텍스트 값은 항목 속성을 가지게 되고
이런 메소드 또한 가지게 됩니다
메소드는 있다가 더해 보겠지만
이렇게 항목 속성이 있고
이것을 장바구니 항목과 동일하게 했습니다
물론 장바구니 상태가 변할 때마다
이 부분 또한 변하고
새 컨텍스트가 제공되어
관련된 컴포넌트로 넘어갑니다
이 provider 컴포넌트에 값 속성을 더하고
이것을 cartContext 객체와 동일하도록
설정하겠습니다
이제 이 값을 분배하겠습니다
물론 확실히 해야 하는 것은
항목 업데이트가 잘 되는 것이므로
여기에 항목 함수를 하나 더하고
이 CartContext provider 컴포넌트에는
항목 함수를 하나 빼겠습니다
여기에서는 항목 하나를 인수로 받게 되고
여기에서는 항목의 id를 인수로 받게 됩니다
addItem에서는 dispatchCartAction을 호출해서
action 객체를 배정하겠습니다
이 부분은 직접 선택하시면 되지만
이 reducer 코드가 작동하려면
ADD_ITEM 타입이어야 합니다
그러므로 이 ADD_ITEM을 type으로 지정하겠습니다
여기는 이 배정된 객체에 대한 문자열입니다
또한 항목 속성을 이 action 객체에
추가하겠습니다
해당 item 속성을 찾고 있기 때문입니다
이 속성이 존재한다는 것을 확실히 하고
이 item과 동일하게 설정하겠습니다
이 부분을 가져오는 방식은
여기에서 같은 이름을 사용함으로써
자바스크립트 단축키을 사용한 것입니다
이제 항목 삭제를 위해 또다시 dispatchCartAction을 사용하되
이번에는 식별자가 REMOVE_ITEM입니다
reducer에서 사용하기로 한 식별자라면 뭐든 됩니다
여기에서는 reducer를 교체해야 하는데
id를 입력값으로 받고 있기 때문입니다
여기에는 id를 받아야 합니다
그러므로 이 id를 전달하되
action을 통해 reducer로 하겠습니다
그런데 reducer에서
이 REMOVE_ITEM의 경우
한 항목 속성 전체를 찾고 있습니다
문제가 되지 않는 이유는
어차피 id만을 필요로 하기 때문에
액션 id를 여기에서 얻기만 하면
모든 것이 제대로 작동합니다
여기에 항목 전체가 있을 필요는 없습니다
이는 addItem 경우와 다른데, 그 이유는 항목 삭제를 위해서는
식별을 위한 id만이 필요하기 때문입니다
그래서 이 두 가지 action을
이 cartContext에 더하도록 하겠습니다
그러므로 action 아래쪽으로 옮긴 후에
addItem 속성을 컨텍스트에 추가하고
addItem 함수의 포인터로 설정된 값도 추가하겠습니다
이 둘은 이름이 같으므로
이 부분을 더 간결하게 하고
removeItem에도 동일하게 작업하겠습니다
이제 이 두 함수는 모두
컨텍스트 객체에 포함되어 있고
앱의 다른 컴포넌트에서 접근이 가능합니다
즉 이 컴포넌트 안에서 실행이 가능합니다
그것이 다음으로 작업할 내용입니다
이 컨텍스트를 다른 컴포넌트 내에서 사용하는 것입니다
먼저는
이 CartContextProvider로 관련된 다른 컴포넌트를
모두 감싸는 것입니다
그리고 여기에서는 모든
앱 컴포넌트를 비롯한
중첩 컴포넌트를
이 provider 컴포넌트로 감싸겠습니다
이 작업을 하기에 최적의 장소는 앱 컴포넌트입니다
즉 이 앱의 루트(root) 컴포넌트입니다
여기에서 가져올 이름을 CartContextProvider로 하고
위치는 ./store/CartContext.jsx입니다
이것으로 헤더(Header)와 메뉴(Meals)를 감싸겠습니다
이제 이 컴포넌트와
이 안에 있는 자식 컴포넌트가
이 컨텍스트 및 속성에 접근 가능합니다
이제 MealItem 컴포넌트로 가겠습니다
여기에서 이 컨텍스트를 사용 가능한데
Meals 컴포넌트에서 사용 중이기 때문입니다
동일한 내용은 앱 컴포넌트에서도 사용되고
CartContextProvider에 감싸져 있습니다
MealItem에서는 버튼을 클릭할 때마다
장바구니에 항목을 하나 더합니다
이 작업을 위해 여기에 함수를 하나 더하겠습니다
이름은 handleAddMealToCart로 하겠습니다
이 함수를 이 버튼과 연결하겠습니다
onClick 속성을 사용해서
커스텀 버튼에 설정하겠습니다
여기에서는 모든 속성을
근본적인 네이티브 버튼 요소로 보내므로
MealItem.jsx에서는 onClick을
handleAddMealToCart과 동일하도록 설정하겠습니다
그리고 handleAddMealToCart에서는
이 addItem 함수를 컨텍스트로 호출해 오겠습니다
즉 이 컨텍스트에 대한 접근이 필요하고
이 작업은 바로 useContext라는
훅을 사용해서 가능합니다
이 훅은 리액트에서 가져와야 합니다
컨텍스트 사용을 위해서는 컨텍스트 객체를
식별자로 전달해야 합니다
그러므로 CartContext를 가져오겠습니다
provider 컴포넌트가 아니라
컨텍스트 객체를 직접 가져오는 것입니다
이 default export과 이 객체는
CartContext.jsx에서 정의되는데
이 내용을 한 단계 상위로 가서 가져오되
store에 있는 CartContext.jsx에 접근하겠습니다
이제 CartContext를 useContext의 값으로 전달해야 합니다
그 결과로서 사용 가능한 CartContext 객체를
받아왔습니다
이 객체는 이 안에서 사용이 가능합니다
handleAddMealToCart 내부에서 addItem 호출을 위해 사용하고
여기에서 속성으로 받는 meal을
addItem에 값으로 전달할 때 사용합니다
이제 이것으로 실행이 될 것입니다
이렇게 하면 장바구니에 항목이 추가됩니다
제대로 작동하는지 확인하기 위해
장바구니 컨텍스트 파일(CartContext.jsx)로 가서 일단
console.log문을 추가하는데
위치는 반환 직전의 provider 컴포넌트 안입니다
또 CartContext를 여기에서 반환해서
원하는 대로 업데이트가 되는지 확인하겠습니다
이제 다시 앱으로 돌아와서
새로고침 후 개발자 도구를 보면
여기에 반환된 내용이 있습니다
반환된 두 가지 객체가 있는데
이 앱이 엄격 모드(strict mode)로 실행되고 있기에
각 컴포넌트가 리액트 개발 중 두 번씩
렌더링됨으로써
잠재적인 버그와 오류에 대비하고 있습니다
여기에서 두 번 보이는 객체는
바로 CartContext 객체입니다
이는 초기에는 빈 값인 항목 배열을 포함하고
호출 가능한 이 두 함수를 포함합니다
이제 이 부분을 지우고 '장바구니에 담기' 버튼을 누르면
반환된 내용이 하나 더 나옵니다
원하던 결과입니다
객체를 확인해 보면
여기에 있는 items 배열 안에
수량이 하나인 음식 메뉴가 있습니다
이것을 다시 지우고 다시 장바구니에 담으면
배열에 동일한 항목이 아직 있는 것이 보입니다
즉 같은 항목이 두 개 있는 게 아니라
배열에 한 항목은 하나만 있습니다
이 한 항목의 수량은 두 개 있다고 하므로
원한 대로 작동하고 있습니다
이제 장바구니 기능을 적용하는 작업의
첫 단계를 마무리하기 위해
여기에서 이 items라는 데이터를 사용해 보겠습니다
여기서는 로그(log)에만 사용하는데
헤더에서 숫자를 업데이트하는 데에도 사용해 보겠습니다
이것 때문에 여기 있는 컨텍스트 기능을
사용한 것입니다
이렇게 하면 다양한 위치의 데이터에 접근할 수 있기 때문입니다
장바구니를 업데이트할 때에
MealsItem 컴포넌트 내부에서 하게 되지만
items에 대한 데이터에 접근해서
장바구니에 있는 메뉴의 수 또한
헤더 컴포넌트에 표시하겠습니다
이 작업은 다행히 아주 간단합니다
일단 useContext를 호출하겠습니다
헤더 컴포넌트로 돌아가서 리액트에서 가져오면 되고
CartContext 객체를
useContext의 식별자로서 가져와서 전달함으로써
CartContext에 접근하겠습니다
이제 이 헤더에서
장바구니에 있는 메뉴의 수를 가져올 때
CartContext 항목을 보면 됩니다
길이에 대한 값을 가져오는 것만으로는 부족합니다
장바구니에는 각 항목을 한 번만 더하고
그 후에는 수량만 늘리기 때문입니다
그러므로 내장 reduce 메소드를 사용하겠습니다
이 내장 메소드는
배열을 하나의 값으로 줄여 주는데
즉 숫자 하나로 줄여줄 수 있습니다
이것을 위해 reduce의 첫 인자로 함수를 받겠습니다
두 번째 인자로는 여기서 0으로 설정할
초기값입니다
이 초기값은 조금 있다가 더 작업하겠습니다
첫 번째 인자로 전달하는 이 함수는
인자를 두 개 받는데
이 인자는 자바스크립트가 자동으로 보내는 것입니다
이것이 파생시키려는 새 값입니다
이 경우에는 항목의 총 갯수입니다
이것으로 이 값의 내용이 확실해졌습니다
그리고 두 번째 값으로는
배열의 각 items에 reduce를 호출하겠습니다
이것은 각 item마다 실행됩니다
즉 이 함수는 배열에 있는 item 하나하나마다
새로 재실행됩니다
이에 대한 반환값은 새로운 업데이트된 값으로
이 업데이트된 값을 확장하여
다음 함수를 실행할 때
항목의 총 수에 대한 값으로 사용됩니다
그러므로 이 값은 이 다양한 값을 모두
더할 수 있게 해 줍니다
0을 쓰는 이유는 이 함수의 첫 실행에 대한
첫 항목의 초기값이기 때문입니다
그래서 여기에서는 항목의 총 수를 반환하되
항목 수량(item.quantity)을 통해 항목 배열의
모든 항목 수량을 더하겠습니다
이제 totalCartItems 숫자를 받았으므로
이것을 이 아래쪽에서 동적으로 반환할 수 있습니다
이대로 저장한 후 새로고침을 해 보면
처음에는 0으로 시작합니다
하지만 장바구니에 뭔가를 담으면
이 숫자가 증가하게 됩니다
이 부분은 의도한 대로 잘 작동됩니다
이제 다음 시간에서 할 작업은
이 장바구니 버튼을 눌렀을 때
모달을 열어서 장바구니 데이터를 보여주는 작업입니다