[클론코딩] 재주상회 클론코딩

AnSuebin·2023년 1월 1일
0
post-thumbnail

제주상회 클론코딩 깃허브

🏁 01. 프로젝트 시작

항해 99 리액트 3주차 과제는 백과 협업하여 클론코딩하기였습니다. 우선 팀원 모두 커머스 사이트에 관심을 가지고 있었습니다. 일주일이라는 짧은시간에 제작할 수 있는 스코프로 기존에 관심있게 보던 재주 상회라는 기업의 홈페이지를 주제로 제시하게 되었고, 모두 동의하여 이를 주제로 프로젝트를 진행하게 되었습니다.

🏁 02. 프로젝트 목표

  • 기존에 제작하던 방법에서 벗어나, 더욱 보기쉽고, 깔끔한 코드를 짜길 원했습니다. 그래서 코드를 최대한 쪼개는 연습을 하는 것을 목표로 진행하였습니다.
  • 깃이슈를 사용하여 좀 더 원활한 협업 환경을 구축하였습니다.

🏁 03. 프로젝트 기간 및 맡은 역할

  • 개발 기간 : 2022.12.22 ~ 2022.12.29 (약 1주)
  • 맡은 역할 : 메인 페이지, 상품 리스트 페이지, 상품 주문 페이지

🏁 04. 기술 스택

  • 프론트 : react
  • 백 : spring

🏁 05. API 설계

📓 06. 페이지별 소개

1. 로그인, 회원가입 페이지

  • 회원가입 : 인풋 조건 처리, 빈칸 처리
  • 로그인 : JWT처리, 깃허브 로그인, 네이버 로그인

2. 메인 페이지

  • 헤더 : 로그인 유무에 따른 형태 변환, 스크롤 시 형태 변환, 서브 카테고리 기능
  • 메인 : 메인 이미지 캐러셀 기능, 스크롤 시 New items 노출 기능

3. 상품 리스트 페이지

  • 상품 불러오기, 스크롤시 애니메이션형태 노출 기능
  • 페이지네이션

4. 상품 상세 페이지

  • 상품 내역 불러오기 기능
  • 수량 제한 기능

5. 장바구니 및 주문 상품 리스트 페이지

  • 장바구니 내역 불러오기 기능
  • 수량 변경 시 금액 변경
  • 전체 금액 합계 기능
  • 장바구니 내역 삭제 및 주문 상품 리스트로 넘어가는 기능

🔥 07. 구현 포인트

  • 파일구조
    • 최대한 쪼개는 구조로 제작하기 위해서 컴포넌트에서 page별로 폴더를 한번 묶고,
      한번 기능 별로 폴더을 묶었습니다.
    • 그리고, 그 내에 컴포넌트, screen 등 기능에서 사용하는 내용들의 타입들을 묶어 폴더로 제작하였고, 그 안에 기능내의 기능들로 분리하여 작업하였습니다.
    • 결론적으로 100 줄 이하의 컴포넌트로 구성할 수 있었습니다.
    • 항상 길고 복잡한 구조로 도무지 어떻게 쪼갤지 막막했던 상태에서 벗어나, 조금은 컴포넌트를 다루는 것에 희망을 얻을 수 있었습니다.
  • 애니메이션
    - 재주상회의 애니메이션은 굉장히 잘 되있어, 눈에 띄이는 구조였습니다. 
    - 따라서 이를 최대한 반영하고자 노력하였습니다.
    - 특히 스크롤을 내릴 때, 헤더의 변형이 일어나는 부분과 아이템들이 올라오는 기능을 중점적으로 구현하였습니다.
    - 헤더는 스크롤을 스크롤 위치를 가져와 일정 위치 이상이면, 변화하도록 제작하였습니다.
    - 그러나, 문제는 돔에 건드는 문제가 적용되어 있다는 점입니다. 이부분을 추후 개선하고자 합니다.
    스크롤 위치 커스텀 훅
import { useState, useEffect } from "react";

const useScrollPosition = () => {
  const [scrollPosition, setScrollPosition] = useState(0);
  const updateScroll = () => {
    setScrollPosition(window.scrollY || document.documentElement.scrollTop);
  };

  useEffect(() => {
    window.addEventListener("scroll", updateScroll);
  });
  return scrollPosition;
};

export default useScrollPosition;

헤더

 return (
    <HeaderContainerSecond>
      {scrollPosition > 36 && (
        <img
          src="http://iiinjeju.com/_dj/img/logo.jpg"
          alt="iiin 로고"
          onClick={() => {
            navigate("/");
          }}
        />
      )}
      {categories.map((category) => {
        return (
          <LinkButton
            key={category}
            linkName={category}
            fontsize={scrollPosition <= 36 ? "1rem" : "0.8rem"}
            margin={scrollPosition <= 36 ? "1rem 4rem" : "0.4rem 1.5rem"}
            linkTo={`/product/list?category=${category.toLowerCase()}&page=1`}
            isHoverNeed={true}
          />
        );
      })}
      <IconContainer>
        <div>
          <img
            src={process.env.PUBLIC_URL + "/imgs/cart.svg"}
            alt="cart"
            onClick={() => {
              navigate("/cart");
            }}
          />
          <div
            onClick={() => {
              navigate("/cart");
            }}
          >
            {cartItemCount}
          </div>
        </div>
      </IconContainer>
    </HeaderContainerSecond>
  );

아이템 fadein 훅

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

const useScrollFadeIn = () => {
  const dom = useRef();

  const handleScroll = useCallback(([entry]) => {
    const { current } = dom;

    if (entry.isIntersecting) {
      current.style.transitionProperty = "opacity transform";
      current.style.transitionDuration = "1s";
      current.style.transitionTimingFunction = "cubic-bezier(0, 0, 0.2, 1)";
      current.style.transitionDelay = "0.1s";
      current.style.opacity = 1;
      current.style.transform = "translate3d(0, 0, 0)";
    }
  }, []);

  useEffect(() => {
    let observer;
    const { current } = dom;

    if (current) {
      observer = new IntersectionObserver(handleScroll, {
        threshold: 0.5,
      });
      observer.observe(current);

      return () => observer && observer.disconnect();
    }
  }, [handleScroll]);

  return {
    ref: dom,
    style: {
      opacity: 0,
      transform: "translate3d(0, 50%, 0)",
    },
  };
};

export default useScrollFadeIn;

아이템

const ItemScreen = ({ thumbnailImgUrl, name, price, caption, linkTo }) => {
  const animationItem = useScrollFadeIn();
  const navigate = useNavigate();
  return (
    <Container
      ref={animationItem.ref}
      style={animationItem.style}
      onClick={() => {
        navigate(linkTo);
      }}
    >
      <img src={thumbnailImgUrl} alt={name}></img>
      <div>{name}</div>
      <h6>{caption}</h6>
      <h5>{numeral(price).format("0,0")}원</h5>
    </Container>
  );
};
profile
고객에게 명료한 의미를 전달하고, 명료한 코드를 통해 생산성 향상에 기여하고자 노력합니다.

0개의 댓글