Simple React Shopping Mall 만들기(3)

Jo Seung Hyun
8 min readAug 7, 2018

--

Prerequisites

Simple React Shopping Mall 만들기(1)

Simple React Shopping Mall 만들기(2)

GitHub 코드

- git clone https://github.com/Joe1220/project.git- cd project- git reset --hard f6481438ae1d7db80830245f414f0f39cdbd5a4c

제품 렌더링하기

App.js파일에서 전체 제품을 렌더링하는 코드는 다음과 같다.

App.js
20 line: renderFoodDetail이라는 method를 만들어서 products state를 map함수를 통해 렌더링, 그리고 각각 제품의 url을 생성하고 있다. map에 관해서는 밑에서 조금 더 자세하게 서술하겠다.
26 line: Route 안에서 Component의 렌더링과 props전달 방법이다. map함수를 통해 전체 제품갯수의 url이 생성되고, 각 제품 component에 props를 전달한다.
57번째 줄, 즉 Switch Component안에서 이를 호출할 수 있다.

51번째 줄에 보면, App Component에서 Main Component에게 product를 props로 전달하고 있다.

Main.js
5 line: 실제 project에서는 fetch로 불러온 product는 길이가 0이하이거나, 어떠한 오류로 아무것도 렌더링 하지 않을수도 있다. 이런 경우 Component는 렌더링 할 것이 없이때문에 오류가 날 수 있다. 이를 방지하기 위해서는, if문을 사용하여 product가 있는 경우와 없는 경우로 나뉘어 작성하면 된다.
16 line: map함수를 사용하여 각 제품의 Link을 만들었다. 각각의 제품들의 실제 Component는 App.js의 renderFoodDetail 함수를 참조하면 된다.

장바구니 만들기

제품 렌더링을 하였으니, 실제로 이 제품들을 App.js폴더에 생성한 cart state에 담을 수 있어야 한다. 우선 App.js 파일을 좀 더 수정해야 한다.

App.js
12 line: cart state를 최상단 Component에서 작성하여, 하위 컴포넌트에서 이를 조작할 수 있게 하겠다. quantity state는 각 제품의 수량을 위해 만들었다. totalAmount는 장바구니에 담긴 제품의 총 가격 합을 구하기 위해 정의하였으나, 실제 프로젝트에서는 사용하지는 않았다. 참고만 하기 바란다.18 - 20 lines : constructor 안에서 이벤트를 bind(바인드)하고 있다. 이렇게 bind하지 않을경우 render 안에서 우리가 생성한 이벤트가 Component를 가르키지 않고 오류가 발생하게 된다. 이러한 방법이 번거롭다면, es6에서 새롭게 나온 화살형 함수을 이용하도록 한다.24 line: 실제로 제품을 cart state, 즉 장바구니에 담는 method다. 54번째 줄은 보게 되면, 각 Item Component에 이를 props로 전달하고 있다. 상위 Component에서 생선된 state를 이런 방식으로 하위 Component에서 조작할 수 있다. handleAddToCart함수는 하위 Component에서 매개변수로 selectedProducts를 받고, 이를 통해 최상단 cart state를 조작하게 된다.70 line: 내가 선택한 제품이 이미 cart state에 포함되어 있다면 다시 cart에 담지않고 제품의 개수만을 올려준다.93 line: localStorage라는 속성이 새로 나타났다. 사용자 로컬에 있는 storage객체에 접근하게 해준다. 이를 통해 React를 새로고침하여도 localStorage에 있는 값을 읽어들어 state를 유지할 수 있게 되었다. 95 line: prevState라는 값이 새로 나타났다. this.state와는 조금 다르게 이전 state값이라는 뜻인데, 이 밑에 부분에서 좀 더 자세히 서술하도록 하겠다.103 line: componentDidUpdate method는 prevProps, prevState매개변수를 사용하는 대표적 예이다. 참고로 componentDidUpdate는 사용할 때 반드시 이 두개의 매개변수를 사용한다. Component의 Updation단계의 마지막에 실행되는 method로, Component가 리렌더링 된 후 실행되는 method이다. 여기에서는 리렌더링되기 이전 cart state와 현재 cart state를 비교한 후, 달라진 점이 있을 때 localStore에
cart state를 저장하고 있다.

코드 설명을 계속하기 전에, 잠시 prevState에 대해 조금 더 서술하도록 하겠다.

prevProps, prevState

prevState, prevProps 둘 다 이름 그대로 DOM이 변경되기 이전의 props와 state의 값을 읽어들이는 것이다. 그렇다면 기존 state와 props 대신에 사용되는 이유는 무엇인가?

공식문서에 따르면, this.state와 this.props는 asynchronously(비동기)적으로 업데이트 될 수 있다고 나와있다. 예를들어, 내가 setState method를 사용하여 state를 변경한 뒤 리렌더링을 기대하였는데, 이 기능이 나의 기대대로 작동하지 않을 경우가 있다는 뜻이다.

wrong!

React docs에 나와있는 코드이다. 위에 코드는 counter state값의 증가가 업데이트되는 것이 실패할 수 있다. 그렇다면 이를 해결하는 방법은?

correct

간략히 설명하면, correct한 방법으로 코드를 작성할 경우, Componenet의 즉각적인 리렌더링을 기대할 수 있게 되는 것이다. state가 아닌 props를 사용할 경우 prevProps를 사용하게 된다.

prevProps

장바구니에 제품 담기

이제 실제로 장바구니에 제품을 담는 코드를 살펴보도록 하겠다. App.js파일에 54,55번째 줄을 보면 addToCart, productQuantity라는 props를 Item Component에 전달하고 있다. 이 두 가지 props를 통해 장바구니에 제품을 담을 수 있게 된다.

Item.js
11 line: addToCart라는 method를 Item에서 새로이 만들고 있다. App.js의 addToCart와는 다른 method이지만, 이름을 같게 한 이유는 두 method가 연동하기 때문이였다...하지만, 이 부분이 혼동을 일으킨다면 다른 method의 이름을 사용하도록 하자. 매개변수로 props로 만든 제품의 id, name, price, 그리고 App.js의 quantity state를 제품의 수량을 정하는 용도로 사용하고 있다. 이러한 방식으로 제품의 내용들과 수량들을 selectedProduct state의 내용으로 지정한 뒤, 최상단 App.js의 state를 변경할 수 있게 21번째 줄에서 props로 받은addToCart Method를 이용하는 것이다. 최종적으로 상단 Component의 state를 하위 Component에서 변경하게 된다.41 line: method의 binding을 constructor이 아닌 render 안에서 실행하고 있다. 최종적으로 새로 만든 button 을 통해 간단한 장바구니 기능이 완성되었다.

최종 결과물은 다음과 같다.

main
cart

최종적으로 인증, css, 결제와 같이 detail한 부분은 없지만 제품의 렌더링과 장바구니 기능이 구현되었다. 새로고침을 하여도 장바구니에 담긴 제품은 localStorage에 담겼기 때문에 사라지지 않게 된다.이렇게 해서 Simple Shopping Mall 프로젝트가 완료되었다.

React만을 사용한 프로젝트는 약간의 어려운 점이 있다. 상위 Component와 하위 Component와의 거리가 넓어지면 props전달이 어려워지거나, Authentication(인증)같은 중요한 정보를 계속해서 저장하기 어렵다던지….

실제 프로젝트는 이처럼 react만으로 구성되지 않고 Redux를 많이 사용한다. 하지만 이번 프로젝트의 목적은 간단한 기능을 구현해봄으로써 react에 대한 기술설명이였기 때문에 redux 사용을 배제하였다.

Redux에 관한 부분도 기회가 된다면 작성해보도록 하겠다!

--

--