[리팩토링] 가이드 페이지 구현

yoon Y·2022년 3월 14일
0

[2nd_Project] WaffleCard

목록 보기
12/15

컴포넌트 역할

GuidePage

  • 모달 형식
  • 모달 닫기 로직 관리
  • GuideSlider, ClearIcon컴포넌트 렌더링

GuideSlider컴포넌트

  • GUIDE_SLIDE_DATA로 Slide컴포넌트 생성
  • 슬라이드 이동 로직 관리, 해당 핸들러 함수를 ArrowIcons컴포넌트 (이전, 다음 아이콘)에 전달
  • 슬라이드의 현재 순서를 알려주는 Dots컴포넌트 렌더링

Slide컴포넌트

  • GuideSlider컴포넌트에서 제목 text, imageUrl을 받아 렌더링

구현 방법

  • GuideSlider에 index를 상태로 저장 0으로 초기화
  • 이전 버튼 누를 시 index -1, 다음 버튼 누를 시 +1
    • index가 0보다 작아지면 슬라이드 최대 갯수로,
      슬라이드 최대 갯수를 초과할 시 0으로 바꿔주어 순환하게 함
  • 현재 상태에 담긴 index에 해당하는 Slide컴포넌트를 display:flex로 설정

주요 로직

const GuideSlider = ({
  ...props
}: React.ComponentProps<'div'>): JSX.Element => {
  const [slideIndex, setSlideIndex] = useState(0);
  const maxLength = Object.keys(GUIDE_SLIDE_DATA).length;

  const handleClickPrev = () => {
    setSlideIndex(state => {
      if (state - 1 < 0) {
        return maxLength - 1;
      } else {
        return state - 1;
      }
    });
  };

  const handleClickNext = () => {
    setSlideIndex(state => {
      if (state + 1 >= maxLength) {
        return 0;
      } else {
        return state + 1;
      }
    });
  };

  return (
    <Slider {...props}>
      {GUIDE_SLIDE_DATA.map(({ text, url }, index) => (
        <Slide
          key={text}
          text={text}
          imageUrl={url}
          style={{ display: index === slideIndex ? 'flex' : 'none' }}
        />
      ))}
      <Dots>
        {Object.entries(GUIDE_SLIDE_DATA).map(([key, _], index) => (
          <Dot key={key} active={index === slideIndex}></Dot>
        ))}
      </Dots>
      <ArrowIcons onClickPrev={handleClickPrev} onClickNext={handleClickNext} />
    </Slider>
  );
};

문제점

가이드 페이지 이미지 슬라이드 반응형 적용에서 애를 먹었다.
이미지 사이즈가 다 다르고, 텍스트가 2줄이 될 때 slide컨테이너의 높이가 변하는게 문제였다.
heigth가 auto일 시 자식들의 높이를 따라가기 때문이다.


해결책

  • Container와 ImageContainer의 고정된 높이를 정하고,
  • Container에 flex-direction: column, justify-content: space-between를 설정해서 텍스트는 가장 위로, 이미지는 가장 아래로 정렬되도록 했다.
const Slide = ({ text, imageUrl, ...props }: SlideProps): JSX.Element => {
  return (
    <Container {...props}>
      <Title>{text}</Title>
      <ImageContainer>
        <StyledImg src={imageUrl} />
      </ImageContainer>
    </Container>
  );
};

const Container = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  width: 70%;
  height: 510px;
  @media ${Common.media.md} {
    width: 80%;
    height: 450px;
  }
  @media ${Common.media.sm} {
    width: 100%;
    height: 300px;
  }
  ...
`;

const ImageContainer = styled.div`
  width: 100%;
  height: 400px;
  @media ${Common.media.sm} {
    height: 250px;
  }
  ...
`;
profile
#프론트엔드

0개의 댓글