ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [고양이 사진 검색 사이트] 스크롤 페이지 구현, 랜덤 고양이 배너 section,EventDelegation
    React/과제 테스트 2023. 8. 7. 13:59

    프로그래머스 과제 테스트 준비

    프로그래머스는 html,css, javaScript이기 때문에 실질적으로 도움이 될 만한 React로 준비했다. 

    테스팅에 목적이 아닌 학습을 위한 구현자료로써, 디자인은 하지 않았고 기능에 초점을 두었다. 

     

     

    프로그래머스

    코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

    programmers.co.kr

     

    🐇 스크롤 페이징 구현

    유저가 스크롤바 끝가지 이동 시 다음페이지 로딩하도록 만들어야 한다

    1. api 호출시 page 추가

    export const getSearchCatData = async (keyword, pages) => {
      return fetchData(
        `${API_ENDPOINT}/api/cats/search?q=${keyword}&page=${pages}`
      );
    };
     

    2. 현재 page state로 관리

     const [currentPages, setCurrentPages] = useState(1);

    3. 스크롤 이벤트 함수 구현

    // 스크롤 이벤트 처리 함수
      const handleScroll = () => {
        // 브라우저의 스크롤 위치와 문서의 전체 높이를 비교하여 끝까지 도달했는지 체크
        const { scrollTop, clientHeight, scrollHeight } = document.documentElement;
        if (scrollTop + clientHeight >= scrollHeight - 10) {
          // 끝까지 도달하면 다음 페이지를 로딩
          loadMoreCats();
        }
      };

    4. 스크롤시 api 호출

    // 스크롤 페이징 구현
      const loadMoreCats = async () => {
        if (isLoading) return;
        setIsLoading(true);
        try {
          const response = await getSearchCatData(keyword, currentPages + 1);
          const newCatsList = response.data;
          setCatsList([...catsList, ...newCatsList]);
          setCurrentPages(currentPages + 1);
        } catch (error) {
          console.error('Error while loading more cats:', error);
        } finally {
          setIsLoading(false);
        }
      };

    5. 페이지 진입시 스크롤 이벤트 리스너 추가

    useEffect(() => {
        // 컴포넌트가 처음 렌더링 될 때 스크롤 이벤트 리스너를 추가
        window.addEventListener('scroll', handleScroll);
        return () => {
          window.removeEventListener('scroll', handleScroll);
        };
      }, []);

    🐇 랜덤고양이 배너 섹션

    • 현재 검색 결과 목록 위에 배너형태의 랜덤 고양이 섹션을 추가한다.
    • 앱이 구동될 때 /api/cats/random50 api요청 이후 받은 결과를 별도 섹션에 노출
    • 겸색결과가 많더라도 화면에 5개만 노출하여 각 이미지는 좌,우 슬라이등 버튼을 가진다
    • 좌, 우 버튼을 클릭하면, 현재 노출된 이미지는 사라지고, 이전 또는 다음 이미지를 보여준다. 트랜지션은 선택이다

    1. randomCatBanner 컴포넌트 생성

     

    import React, { useEffect, useState } from 'react';
    import { getRandomCatData } from '../api/api';
    import styled from 'styled-components';
    import Loading from './Loading';
    import SlideShow from './SlideShow';

    const BannerContainer = styled.section`
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 20px;
    `;

    const RandomCatBanner = ({ isDarkMode }) => {
      const [isLoading, setIsLoading] = useState(true);
      const [randomCats, setRandomCats] = useState([]);

      useEffect(() => {
        const fetchRandomCats = async () => {
          try {
            setIsLoading(true);
            const response = await getRandomCatData();
            setRandomCats(response.data);
            setIsLoading(false);
          } catch (error) {
            console.error('Error while fetching random cats:', error);
            setRandomCats([]);
          }
        };

        fetchRandomCats();
      }, []);

      if (isLoading) return <Loading isDarkMode={isDarkMode} />;
      return (
        <BannerContainer>
          {randomCats && (
            <SlideShow catsList={randomCats.slice(0, 5)} isDarkMode={isDarkMode} />
          )}
        </BannerContainer>
      );
    };

    export default RandomCatBanner;

     

    2. SlideShow 컴포넌트 생성

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

    const SlideShowContainer = styled.article`
      display: flex;
      align-items: center;
      overflow: hidden;
      background-color: ${({ isDarkMode }) => (isDarkMode ? 'black' : 'white')};
      border-radius: 5%;
    `;

    const SlideShowItem = styled.img`
      width: 200px;
      height: 200px;
      margin: 0 5px;
      transition: transform 0.3s ease-in-out;
      border-radius: 20%;
    `;

    const SlideShowButton = styled.button`
      background-color: transparent;
      border: none;
      font-size: 20px;
      cursor: pointer;
    `;

    const SlideShow = ({ catsList, isDarkMode }) => {
      const [currentIndex, setCurrentIndex] = useState(0);

      const handlePrev = () => {
        setCurrentIndex((prevIndex) =>
          prevIndex === 0 ? catsList.length - 1 : prevIndex - 1
        );
      };

      const handleNext = () => {
        setCurrentIndex((prevIndex) =>
          prevIndex === catsList.length - 1 ? 0 : prevIndex + 1
        );
      };

      return (
        <SlideShowContainer>
          <SlideShowButton onClick={handlePrev}>&#10094;</SlideShowButton>
          <SlideShowItem
            key={catsList[currentIndex].id}
            src={catsList[currentIndex].url}
            alt={catsList[currentIndex].id}
            title={catsList[currentIndex].name}
            loading="lazy"
          />
          <SlideShowButton onClick={handleNext}>&#10095;</SlideShowButton>
        </SlideShowContainer>
      );
    };

    export default SlideShow;

    🐇 추가 요구사항

    🥕 SearchResult에 각 아이템을 클릭하는 이벤트를 EventDelegation을 이용하여 수정

    ※ 이벤트 위임(Event Delegation)

    JavaScript의 대부분 이벤트는 이벤트 흐름(Event Flow)의 이벤트 캡처링, 이벤트 버블링 단계를 거칩니다. 일반적으로 addEventListener() 메서드의 세 번째 매개변수로 true를 전달하지 않은 이상 이벤트 캡처링 단계는 수행되지 않고 이벤트 버블링 단계를 거치는데, 이벤트 버블링은 타겟에서 이벤트가 발생하면 타겟에서 위로 이벤트를 전파하는 것을 말합니다.

    1. img click -> container click

    <S.ImgContainer onClick={handleItemClick}>
                {catsList.map((v, i) => (
                  <S.Img
                    loading="lazy"
                    src={v.url}
                    key={i}
                    alt={v.id}
                    title={v.name}
                   //이전 : onClick={() => displayModal(v.id)} />
                  />
                ))}
              </S.ImgContainer>
     

     

    2. img 클릭 이벤트 생성

    // Event delegation 함수
      const handleImgClick = (event) => {
        const clickedImage = event.target.closest('img');
        if (clickedImage) {
          const id = clickedImage.getAttribute('alt');
          displayModal(id);
        }
      };

     

    🥕 각 컴포넌트의 내부함수와 Util함수를 잘게 나누기

    component / utils 함수 (api) 구별

     

Designed by Tistory.