미새문지

24.08.29 day56 작업 일지 본문

개발 TIL

24.08.29 day56 작업 일지

문미새 2024. 8. 29. 22:51
728x90

랭킹 페이지와 랭킹 상세 페이지 UI를 구현했다. 이제 UI는 다 구현했지만, 아직 기획에 안들어간 페이지도 몇 있고 기능 구현에 맞춰서 수정해야 하는 UI도 있기에 디테일한 부분은 나중에 구현하고 기능부터 구현을 들어갈 예정이다.

 

import styled from "styled-components";
import AboutHeader from "../components/common/aboutHeader";
import MainHeader from "../components/common/mainHeader";
import SearchBar from "../components/ranking/searchBar";
import RankingList from "../components/ranking/rankingList";

const Ranking: React.FC = () => {
  return (
    <>
      <RankingWrapper>
        <AboutHeader Title="제품 랭킹" onBack={""} />
        <SearchBar />
        <RankingList />
      </RankingWrapper>
    </>
  );
};
export default Ranking;

const RankingWrapper = styled.div`
  width: 100%;
`;

먼저 랭킹페이지는 상단 뒤로가기 버튼과 검색창 랭킹 제품 리스트로 이루어져있다.

뒤로가기 버튼은 공용 컴포넌트를 사용했고 검색창은 메인페이지에 쓰인 검색창으로 사용할려고 했으나 검색기능을 구현해보고 동일하다 싶으면 공용 컴포넌트로 합치려고 한다. 

랭킹 제품 리스트는 필터와 제품 목록으로 컴포넌트가 나누어져 있으며 필터는 메인필터와 서브 필터로 나누어져 있다.

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

const RankingMainFilter: React.FC = () => {
  const [select, setSelect] = useState<string>("");

  const handleSelect = (filter: string) => {
    setSelect(filter);
  };

  return (
    <>
      <RankingMainFilterWrapper>
        <FilterItem
          isSelected={select === "category"}
          onClick={() => handleSelect("category")}
        >
          카테고리별
        </FilterItem>
        <FilterItem
          isSelected={select === "skin"}
          onClick={() => handleSelect("skin")}
        >
          피부별
        </FilterItem>
        <FilterItem
          isSelected={select === "age"}
          onClick={() => handleSelect("age")}
        >
          연령대별
        </FilterItem>
      </RankingMainFilterWrapper>
    </>
  );
};
export default RankingMainFilter;

const RankingMainFilterWrapper = styled.div`
  width: 90%;
  margin: 30px auto 20px auto;
  display: flex;
  justify-content: space-evenly;
  border-bottom: 1px solid #ffa4e4;
  padding-bottom: 15px;
`;

const FilterItem = styled.div<{ isSelected: boolean }>`
  cursor: pointer;
  font-weight: ${({ isSelected }) => (isSelected ? "bold" : "normal")};
`;

해당 코드는 메인 필터이며 특정 필터를 클릭 시 해당 value값이 useState에 담겨 전달되게 된다.

서브 필터도 이와 동일하지만 UI적인 부분에서 스크롤을 위로 옮기려 했으나 여러 방법을 사용해도 잘 안되서 일단 보류해놨다.

사용한 방법은 두 가지 인데 하나는 해당 스크롤을 뒤집어서 위로 옮기는 방식이다

  overflow-x: auto; /* 수평 스크롤 허용 */
  direction: rtl; /* 스크롤바를 오른쪽에서 왼쪽으로 설정 */
  transform: scaleX(-1); /* 컨테이너를 거꾸로 뒤집기 */

먼저 스크롤을 만들기 위한 overflow-x를 스크롤을 할 수 있게 적용해주고 direction을 rtl로 하면 스크롤바를 반대쪽으로 뒤집을 수 있다고 한다. 그리고 해당 div를 transform을 이용해 뒤집은다음 자식 컴포넌트에도 뒤집어주면 된다고 하는데, 뒤집히는건 적용되나 스크롤이 반대편으로 가지진 않았다.

 

그래서 자주 쓰이는 방식인 스크롤을 없애고 커스텀 스크롤을 만드는 건데 

const RankingSubFilter: React.FC = () => {
  const [select, setSelect] = useState<string>("");

  const scrollWrapperRef = useRef<HTMLDivElement>(null);
  const scrollShadowRef = useRef<HTMLDivElement>(null);

  const handleSelect = (filter: string) => {
    setSelect(filter);
  };

  // 스크롤 동기화
  useEffect(() => {
    const syncScroll = () => {
      if (scrollWrapperRef.current && scrollShadowRef.current) {
        scrollWrapperRef.current.scrollLeft = scrollShadowRef.current.scrollLeft;
      }
    };

    const shadow = scrollShadowRef.current;
    if (shadow) {
      shadow.addEventListener('scroll', syncScroll);
    }

    return () => {
      if (shadow) {
        shadow.removeEventListener('scroll', syncScroll);
      }
    };
  }, []);

  return (
    <>
      <ScrollShadow ref={scrollShadowRef}>
        <div style={{ width: '200%' }}></div>
      </ScrollShadow>
      <RankingSubFilterWrapper ref={scrollWrapperRef}>
        <FilterItem
          isSelected={select === "건성"}
          onClick={() => handleSelect("건성")}
        >
          건성
        </FilterItem>
        <FilterItem
          isSelected={select === "지성"}
          onClick={() => handleSelect("지성")}
        >
          지성
        </FilterItem>
        <FilterItem
          isSelected={select === "중성"}
          onClick={() => handleSelect("중성")}
        >
          중성
        </FilterItem>
        <FilterItem
          isSelected={select === "복합성"}
          onClick={() => handleSelect("복합성")}
        >
          복합성
        </FilterItem>
        <FilterItem
          isSelected={select === "민감성"}
          onClick={() => handleSelect("민감성")}
        >
          민감성
        </FilterItem>
        <FilterItem
          isSelected={select === "여드름"}
          onClick={() => handleSelect("여드름")}
        >
          여드름
        </FilterItem>
        <FilterItem
          isSelected={select === "아토피"}
          onClick={() => handleSelect("아토피")}
        >
          아토피
        </FilterItem>
        <FilterItem
          isSelected={select === "등등"}
          onClick={() => handleSelect("등등")}
        >
          등등
        </FilterItem>
      </RankingSubFilterWrapper>
    </>
  );
};

해당 div바깥에 커스텀 스크롤을 만들기 위한 div를 하나 만들어주고 기존의 div를 ref로 가져와 해당 스크롤과 묶어준다.

그리고 padding을 어느정도 줘서 텍스트가 가려지지 않게 조율하는 것인데, 이 방식에서도 스크롤이 제대로 적용되지 않고 기본 스크롤만 생기는 사태가 발생해서 포기했다. 크게 중요한 요소는 아니나 UI에서 되게 거슬리더라 그래서 나중에 바꿀 수 있으면 꼭 바꾸려고 한다.

 

랭킹 제품 상세 페이지는 랭킹페이지에서 제품 클릭 시 넘어가지는 페이지인데 현재 기획된 페이지 UI가 제품의 간단 정보만 보여주는 식으로 기획되어있어서 페이지 컴포넌트에 바로 작성해줬다.

import styled from "styled-components";
import BackHeader from "../components/common/backHeader";
import Lotion from "../img/화장품1.jpg";
import MainFooter from "../components/common/mainFooter";

const RankingDetail: React.FC = () => {
  return (
    <>
      <RankingDetailWrapper>
        <BackHeader onBack={""} />
        <DetailItemBox>
          <ItemImg>
            <img src={Lotion} alt="" />
          </ItemImg>
          <ItemInfo>
            <h3>제품 이름</h3>
            <ItemPrice>
              <span>정가</span>
              <span>18,000원 / 80ml</span>
            </ItemPrice>
            <ItemEffect>제품 효과 박스</ItemEffect>
          </ItemInfo>
        </DetailItemBox>
      </RankingDetailWrapper>
      <MainFooter />
    </>
  );
};
export default RankingDetail;

const RankingDetailWrapper = styled.div`
  width: 100%;
  display: flex;
  flex-direction: column;
`;

const DetailItemBox = styled.div`
  width: 90%;
  margin: 20% auto 0 auto;
`;

const ItemImg = styled.div`
  width: 80%;
  height: auto;
  aspect-ratio: 1/1;
  object-fit: cover;
  margin: 0 auto;
  border: 1px solid #d9d9d9;

  img {
    width: 100%;
    height: auto;
    aspect-ratio: 1/1;
    object-fit: cover;
  }
`;

const ItemInfo = styled.div`
  width: 80%;
  margin: 10% auto 0 auto;
`;

const ItemPrice = styled.div`
  display: flex;
  justify-content: flex-start;

  span {
    &:nth-child(1) {
      font-size: 14px;
      color: #848484;
      margin-right: 30px;
    }

    &:nth-child(2) {
      font-size: 14px;
      color: black;
    }
  }
`;

const ItemEffect = styled.div`
  width: 100%;
  height: 120px;
  border: 1px solid #d9d9d9;
  background-color: #d9d9d9;
  border-radius: 13px;
  margin-top: 10%;
`;

어제같은 scss 실수를 줄이기 위해 해당 구조마다 다시 클래스 명이나 태그명을 붙여주었다. 그래도 페이지 자체가 훵하다보니 나중에 추가적인 수정이 들어갈 것 같아, 그 때가 되면 컴포넌트를 나눌 예정이다.

 

이제 API 기능을 구현하면서 백엔드 팀원과 소통할 시간이 왔다. 지금 백엔드 팀원이 데이터베이스를 클라우드 형식으로 띄울 거라고 해서 본인도 데베에 접근할 수 있게 해준다고 하니까 열심히 작업해야겠다.

728x90

'개발 TIL' 카테고리의 다른 글

24.09.02 day58 작업 일지  (4) 2024.09.02
24.08.30 day57 작업 일지  (0) 2024.08.30
24.08.28 day55 작업 일지  (2) 2024.08.28
24.08.27 day54 작업 일지  (0) 2024.08.27
24.08.26 day53 작업 일지  (2) 2024.08.26