wecode 2차 프로젝트: Laneige

유휘찬·2020년 9월 13일
0
post-thumbnail
post-custom-banner

Wecode 2차 프로젝트

🤗 프로젝트 소개

  • 대한민국 화장품 브랜드 Laneige 사이트 디자인 클론 및 기능 구현

💁‍♂️ 팀원 구성

  • FE: 11기 유휘찬, 11기 강예지, 11기 한태규
  • BE: 11기 권창식, 11기 이지연

🛠 기술 스택 및 구현 기능

주요 기술 스택

  • React.js(함수형 컴포넌트)
  • hooks
  • React router
  • styled components
  • AWS

구현 기능

이번 2차 프로젝트에서 나는 상품 리스트 페이지를 담당했다.

  1. 상품 리스트 페이지✅
  • 정적인 사항을 담당하는 카테고리 부분은 js파일로, 동적인 사항을 담당하는 상품 리스트 부분은 json 파일로 mock data 관리
  • map 함수를 사용하여 상품 리스트 구현
  • axios 를 활용한 비동기 처리 및 pagination 기능 구현
  • useRef 를 이용하여 상품 리스트 hover 이미지 변경
  • git rebase 를 적용하여 commit 내역을 깔끔하게 관리
  1. 회원가입, 로그인 페이지

  2. 메인 페이지

  3. 상세 페이지

기억에 남는 코드

axios 를 활용한 비동기 처리와 pagination

이번 프로젝트에서는 fetch 대신 axios 를 사용할 기회가 있었다. pagination 을 구현해야 할 상황이 있었는데 여러 자료를 찾아보다 axios 를 적용하게 되었다.

일단 axios 란 http 통신을 하는데 매우 인기있는 js 라이브러리 이며 promise 를 기반으로 하며 async/await 문법을 사용하여 XHR 요청을 매우 쉽게 할 수 있다고 한다.

npm install axios

ProductList.js

import React, { useState, useEffect } from "react";
import axios from "axios";
const [currentPage, setCurrentPage] = useState(1);
const [postsPerPage] = useState(16);

// useEffect 에서 백엔드에서 넘겨주는 데이터를 저장
useEffect(() => {
  const fetchPosts = async () => {
    const res = await axios.get(`${API}/product/list`);
    setPosts(res.data.data);
  };
  
  fetchPosts();
}, []);
const indexOfLastPost = currentPage * postsPerPage;
const indexOfFirstPost = indexOfLastPost - postsPerPage; 
const currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost);
const paginate = (pageNumber) => {
  setCurrentPage(pageNumber);
  window.scrollTo({ top: 320, behavior: "smooth" });
}
return (
  ~~~
  {
  currentPosts.map((post) => (
    <Item
      key={post.id}
      image={posts.image[0]}
      hover={
        post.image[0] === post.image[1]
          ? post.image[0]
          : post.image[1]
        }
      hashFirst={posts.tag[0]}
      hashSecond={posts.tag[1]}
      productName={post.korean_name}
      >
    ))
  }
  ~~~
  <Pagination 
    postsPerPage={postsPerPage}
    totalPosts={posts.length}
    paginate={paginate}
  />
);

Pagination.js

import React, { useState } from "react";
import styled from "styled-components";

function Pagination({ postsPerPage, totalPosts, paginate }) {
  const pageNumber = [];
  const [activeBtn, setActiveBtn] = useState(1);
  
  for(let i = 1; i <= Math.ceil(totlaPosts / postsPerPage); i++) {
    pageNumber.push(i);
  }
  
  return (
    <PaginationOuter>
      {pageNumbers.map((number) => (
        <PageBtn
          key={number}
          onClick={() => {
            paginate(number);
            setActiveBtn(number);
          }}
          backgroundColor={activeBtn === number ? "#2b2b2b" : ""}
          color={activeBtn === number ? "white" : "#2b2b2b"}
          hover={activeBtn === number ? "#4478be" : ""}
        >
          {number}
        </PageBtn>
      ))}
    </PaginationOuter>
  );
}

useRef 를 이용한 상품리스트 hover 이미지 변경

1차 프로젝트 때 다양한 크기의 스크린에서 스크롤 이벤트가 발생했을 때 scrollY 를 사용하지 않고 정확한 타겟으로 스크롤 되는 기능을 구현하기 위해 useRef 라는 것을 사용하면 된다는 것을 알게 되었다. 하지만 개념자체도 이해하기 쉽지 않았고, 적용하기란 더욱 어려운 일이었다.

2차 프로젝트 때에 꼭 적용하고 싶다는 생각을 했다. 1차 때 구현해내지 못한 기능이 똑같이 적용되어져 있는 페이지가 있길래 꼭 useRef 를 사용하여 기능구현을 해보리라 다짐했다. 하지만 시간이 부족해서 그 페이지 자체를 구현하지 못하게 되는 상황이 생겼다.

아쉬워 하던 중 상품 리스트 페이지를 구현하면서 useRef 를 사용할 수 있게 되었다.


ItemUseHover.js

import { useRef, useState, useEffect } from "react";

function useHover() {
  const [value, setValue] = useState(false);

  const ref = useRef(null);

  const handleMouseOver = () => setValue(true);
  const handleMouseOut = () => setValue(false);

  useEffect(() => {
    const node = ref.current;

    if (node) {
      node.addEventListener("mouseover", handleMouseOver);
      node.addEventListener("mouseout", handleMouseOut);

      return () => {
        node.removeEventListener("mouseover", handleMouseOver);
        node.removeEventListener("mouseout", handleMouseOut);
      };
    }
  }, []);
  return [ref, value];
}

export default useHover;

Item.js

import React from "react";
import { useHistory } from "react-router-dom";
import styled from "styled-components";
import New from "./New";
import Best from "./Best";
import useHover from "./ItemUseHover";

function Item({
  isId,
  isNew,
  isBest,
  image,
  hashFirst,
  hashSecond,
  productName,
  hover,
}) {
  const [hoverRef, isHoverd] = useHover();
  const history = useHistory();

  const goToDetail = () => {
    history.push(`/product/list/${isId}`);
  };

  return (
    <ItemOuter onClick={goToDetail}>
      <ItemImg ref={hoverRef} src={isHoverd ? hover : image} />
      <Mark>
        {isNew === "true" && <New />}
        {isBest === "true" && <Best />}
      </Mark>
      <HashWrapper>
        <HashFirst>{hashFirst}</HashFirst>
        <HashSecond>{hashSecond}</HashSecond>
      </HashWrapper>
      <ProductName>{productName}</ProductName>
    </ItemOuter>
  );
}

후기

위코드의 마지막 프로젝트가 끝났다. 이 모든 일들이 정말 순식간에 지나갔다. 모두가 서로의 성장을 위해 함께 애썼다. 고생한 팀원들에게 감사하다는 말을 전하고 싶다. 항상 열심히 했지만 아쉬움이 남는다. 1차 프로젝트에 비해 중간중간 새로 배우는 내용들이 많아 적용하는데 시간을 더욱 들여야 했다. redux 의 존재를 알고 필요를 매우 느꼈지만 시간관계상 프로젝트가 우선이었기에 포기해야 했다. 화려하고 멋진 기능들을 가진 페이지를 하나라도 더 구현해보고자 욕심이 앞서 페이지를 선택했지만 결국 시도조차 못한 페이지가 있다.

아쉬움이 남기도 하지만 모르는 부분을 스스로 공부하여 적용시켜 구현해낸 기능들이 나를 뿌듯하게 만들기도 한다. useRef 는 아직 정확하게 이해하진 못했지만 타겟을 지정하여 어떠한 효과를 줄 수 있는 듯하다. pagination 역시 유튜브 동영상을 보고 적용시켰는데 적용시켜보고 기능구현이 되는 것 자체가 신기했고 뿌듯했다. styled-components 는 css 파일을 따로 만들지 않고 곧바로 js 파일에서 스타일 작업을 하는 것, 그리고 props 를 넘길 때도 정말 유용하다는 것을 알게 되었다. 너무 신기하고 더 공부해보고 싶은 것들이 넘친다.

다음주 부터는 기업협업을 나가게 된다. 정말 많이 부족하지만, 배워야 할 것들이 산더미같을 테지만 멘탈관리, 시간관리, 체력관리 정말 잘해서 멋진 과정과 결과를 만들어내고 싶다.

profile
tenacity
post-custom-banner

0개의 댓글