오늘의집 긴장해.. 지금 내일의집 만든다. React 개발 리뷰 (1)

Ryan·2020년 11월 29일
20

Project | HOT

목록 보기
1/4
post-thumbnail

오늘의집과 아주아주 똑같은 내일의집을 개발해보자.
커뮤니티와 스토어가 서로 연결되어 서로 자유롭게 넘나드는 웹페이지를 만들어보자.

1. 오늘의집 분석

: 오늘의집은 커뮤니티와 스토어를 서로 연결시켜 두었다는 것이 가장 큰 장점이다.
즉, 피드를 구경하다 방을 구성하고 있는 제품들에 대한 정보를 바로 알 수 있고 구매까지 할 수 있다.
또한 제품을 고르다 해당 제품이 게시된 피드를 보러 커뮤니티로 바로 이동할 수도 있다.
이것이 이번 클론의 핵심 기능이 될 것 같다.


2. 클론의 목적

  • 목적 : 클론 코딩을 통한 웹 사이트의 이해도를 높이고 실제 사용되는 기술에 대한 이해도를 높인다.
  • 개인목표 : 개인적인 목표로.... 어떤 라이브러리도 쓰지 않고 만들자!!(였는데 슬릭은 씀)
  • 기간 : 2020년 11월 16일 (월) ~ 2020년 11월 26일(목), 11일간 진행

3. 사용된 기술

  • Development environment: CRA(Creat-React-App)
  • Language: JavaScript
  • Library: React
  • Third-party: React-Router, SASS, React-Slick

4. 어떤 순서로 학습 했는가?

: 이번 클론은 백엔드와 함께하는 첫번째 프로젝트이다.
그렇기에 서버와 연결되는 부분을 항상 고려하며 진행하는 것이 중요했다.

  • 1단계: 프론트에서 요청하고자 하는 정보들을 분석해서 사전에 백엔드로 공유
  • 2단계: 백엔드는 해당 데이터들을 모델링화 실시하고 프론트는 뷰를 먼저 그렸다.
  • 3단계: 목업 데이터를 API로 활용해서 백엔드와 통신한다고 가정한 상태로 연결된 형태의 웹페이지를 구축하기로 했다.
  • 4단계: 모델링이 끝난 후 서버에 데이터를 넣고 서버와 뷰를 연결시켜보았다.
  • 5단계: 연결 후 추가 기능 구현을 통해 완성도를 높였다.

5. Code Review

<클릭해서 영상으로 이동>

내가 담당하게 된 부분은 스토어쪽이다.
이번에도 역시.....클론은 어떻게?
1픽셀까지 똑같이!!! 약간의 차이도 용납하지 않는다.

  • 어때유? 똑같쥬?

1) Category 주요 코드

(1) 사이드바

: 이번에는 서버에서 넘어오는 데이터를 사용하는 것이기 때문에 어떻게 받아서 어떻게 처리할지 고민이 아주 많았다. 처음부터 Fetch를 아무데나 받아온다면 너무 복잡해질 것 같았다.

  • 위에서 보는 것 처럼 원하는 메뉴를 클릭하면 해당 메뉴가 상단으로 올라오고 나머지 메뉴들은 아래 정렬이 된다. 우측에는 클릭한 카테고리에 해당하는 제품들로 바로 교체가 된다.
  • 사실 가장 쉬운 방법은 서버로부터 모든 데이터를 다 받아오는 방법이다. 그리고 뿌리면 끝난다. 하지만 클론이 아니라 실제 서버라면 그렇게 해도 될까?? 수천개의 제품 정보를 메뉴에서부터 들고 다니는 것이다.
  • 결국 조금은 힘들겠지만 사이드 메뉴도 둘로 나누기로 결심했다.

  • 최종적으로 Category 라는 컴포넌트와 내부에 크게는 총 4개의 컴포넌트로 분리되었다.
  • Category 컴포넌트에서는 이벤트마다 get과 post가 일어나는 최상위 부모가 되었다
  • Title에는 product를 구분지어 줄 서브 카테고리들을 모두 받아오도록 했고, Other에는 최상위 카테고리들만 받아오도록 요청했다.
  • 또한 카테고리(가구,패브릭 등)와 서브 카테고리(침구, 커튼 등)를 누를때마다 Product List의 내용이 즉각 바뀔 수 있도록 백에서 미리 제품 테이블을 구분 지어두었다.

  • 라이브러리는 쓰고 싶지 않고 자연스럽게 메뉴가 접히고 열리는 것을 구현해보기 위해 고심하다 하루종일 고민한 결과... 키프레임을 이용해 결국 구현해냈다.
  • 이제 구조화는 끝났고 직접 코드를 짜보자.

Category Component

: 사이드바 메뉴와 제품리스트를 묶는 부모 컴포넌트이다.
코드가 길어 state나 렌더 부분은 생략하였으니 양해바람ㅠ (보면 이해될테니...)

class Category extends Component {
// 첫 화면에 보여줄 정보들은 컴디마에 넣었다
  componentDidMount() {
    this.getCategoryOtherMenu();
    this.getCategoryTitleMenu();
    this.getALLProductList();
  }
// Title 부분이 서버로 요청된다.
  getCategoryTitleMenu = () => {
    fetch(`${API}/store/categories?menu=1`, {
      method: 'GET',
      headers: {
        authorization: localStorage.getItem('token'),
      },
    })
      .then((res) => res.json())
      .then((res) => {
        this.setState({
          categoryTitle: res.result,
        });
      });
  };
// Other 부분이 서버로 요청된다.
  getCategoryOtherMenu = () => {
    fetch(`${API}/store/categories`, {
      method: 'GET',
      headers: {
        authorization: localStorage.getItem('token'),
      },
    })
      .then((res) => res.json())
      .then((res) => {
        this.setState({
          categoryOther: res.result,
        });
      });
  };
// 컴디마로 첫 화면에 보여줄 '가구' 메뉴의 리스트들이다.
  getALLProductList = () => {
    fetch(`${API}/store/products?menu=1`, {
      method: 'GET',
      headers: {
        authorization: localStorage.getItem('token'),
      },
    })
      .then((res) => res.json())
      .then((res) => {
        this.setState({
          productList: res.result,
        });
      });
  };
// Other에서 메뉴를 클릭시 Title 부분이 변경되고 해당 데이터를 넘겨받게 된다.
  changeTitleMenu = (otherElement) => {
    fetch(`${API}/store/categories?menu=${otherElement.menu_id}`, {
      method: 'GET',
      headers: {
        authorization: localStorage.getItem('token'),
      },
    })
      .then((res) => res.json())
      .then((res) => {
        this.setState({
          categoryTitle: res.result,
        });
      });
  };
// 메뉴 > 하위 카테고리에 해당하는 제품 리스트들을 불러오는 요청 코드이다.
  getProductList = (categoryElement) => {
    fetch(`${API}/store/products?cat=${categoryElement.category_id}`, {
      method: 'GET',
      headers: {
        authorization: localStorage.getItem('token'),
      },
    })
      .then((res) => res.json())
      .then((res) => {
        this.setState({
          productList: res.result,
        });
        this.setState({
          catTitle: categoryElement,
        });
      });
  };
// 메뉴 > 하위 카테고리 > 서브 카테고리에 해당하는 제품 리스트들을 불러오는 요청 코드이다.
  getSubProductList = (subCategoriesElement) => {
    fetch(`${API}/store/products?sub=${subCategoriesElement.subcategory_id}`, {
      method: 'GET',
      headers: {
        authorization: localStorage.getItem('token'),
      },
    })
      .then((res) => res.json())
      .then((res) => {
        this.setState({
          subProductList: res.result,
        });
        this.setState({
          subTitle: subCategoriesElement,
        });
      });
  };
// ----- 렌더부분 생략 ----- //
  • 이렇게 모든 데이터들은 Category라는 최상위 부모 컴포넌트에서 내려주게 만들었다.
  • 해당 컴포넌트는 Get요청과 컴포넌트 분리에만 집중하고 있다. 따라서 렌더부분은 특별한 것은 없다(그러니 생략)
  • 만약 자식 컴포넌트들에서 데이터를 받아와야하는 onClick이나 onChange 같은 이벤트가 발생한다면 이벤트를 전부 이 컴포넌트로 끌어올렸다.
<SelectOption
  giveProductInfo={productList}
  giveSelectedProducts={selectedProducts}
  giveBookmarkColor={bookMarkSwitch}
  takeSelectedColor={getSelectedProductColor}
  takeSelectedOption={getSelectedProductOption}
  takeSelectedProductsValue={getProductCount}
  takeSelectedProductsDelIndex={handleDeleteProduct}
  takeSelectedProductsCart={postCartInfo} />
  • 예를 들면 이런식으로 컴포넌트들에 take로 시작하는 프롭스 이름들이 많다. 전부 부모로 끌어올려서 처리해주고 있다.

Title Component

: 부모인 카테고리 컴포넌트를 알아봤고 이제 위에서 설명한 분리된 4개 중 Title 컴포넌트를 알아보자.

class ListTitle extends Component {
  constructor() {
    super();
    this.state = {
      selectedCategory: '',
      selectedSubcategory: '',
      openCategorySwitch: false,
    };
  }
//카테고리 클릭 이벤트 함수
  selectedCategory = (categoryElement) => {
    this.setState({ selectedCategory: categoryElement.category_name });
    this.props.takeClickEvent(categoryElement);
  };
//서브 카테고리 클릭 이벤트 함수
  selectedSubcategory = (subCategoriesElement) => {
    this.setState({
      selectedSubcategory: subCategoriesElement.subcategory_name,
    });
    this.props.takeSubClickEvent(subCategoriesElement);
  };

//------ 렌더부분은 생략 ------//
  • Title 컴포넌트는 부모인 Category로 이벤트를 전달해주는 2개의 함수를 가지고 있다.
<button 
  onClick={() => this.selectedCategory(categoryElement)} 
  className={selectedCategory === categoryElement.category_name  
    ? 'subTitleList changeColorEvent ' 
    : 'subTitleList' }> 
  {categoryElement.category_name}
</button>
  • 또한 이 함수는 부모로 전달함과 동시에 클릭된 카테고리 이름이 state에 저장되어 해당 state와 동일한 이름을 가진 카테고리만 색이 바뀌도록 설정해두었다.
  • 서브카테고리도 코드는 비슷하게 생겼으니 생략한다.

Other Component

: other는 위의 사진에서 보는 것 처럼 클릭한 메뉴만 Title로 넘어가고 나머지 메뉴들이 순서대로 나열된다. 배열의 filter와 동일하게 생긴것 같아 filter만 적용하였다.
너무 간단한 코드이므로 코드는 생략!


Banner, Product List는 2화에 계속 링크에 계속!

profile
"꾸준한 삽질과 우연한 성공"

9개의 댓글

comment-user-thumbnail
2020년 12월 16일

카테고리 사이드바 코드들이 인상 깊네요 담에 사이드바 만들때 응용하기 좋겠군요

1개의 답글
comment-user-thumbnail
2020년 12월 16일

상혁 선생님 ~~ 너무 잘하는거 아니야~~~ 샘나!!!!!😏

1개의 답글
comment-user-thumbnail
2020년 12월 16일

내일의 집 발표때 정말 멋지더라구요 수고 많으셨어요!! 기업협업 하면서 같이 열심히 성장해봐요!!

1개의 답글
comment-user-thumbnail
2020년 12월 16일

캬 역시 상혁님~~~진짜 너무 잘해 이렇게 정리를 하시다니 저도 블로그 정리 법 배우고 갑니다!

1개의 답글
comment-user-thumbnail
2022년 8월 14일

https://www.zzang79.com/sands [샌즈카지노 ]
There will be no one to advise you when is the right time to stop so it is up to you to make the decision of when to walk away. Picking that moment can be tough.

답글 달기