23.05.18 웹개발_솔로프로젝트(Product list page 만들기)

Yeondong Choe·2023년 5월 18일
0
post-thumbnail

회고 Tip
1. 지금 현재, 기분과 느낌을 표현(구체적)
2. 오늘 학습한 내용의 단어를 모두 나열
3. 나열한 단어를 하나씩 설명
4. 설명하기 어려운 단어가 있다면 그 이유를 생각
5. 스스로 설명하기 위해 질문한다면 어떻게 질문할것인가 생각

웹개발_솔로프로젝트 데일리 후기

오늘은 솔로프로젝트 마지막날... 쓰고싶은 후기가 많지만 따로 블로그를 하기로하고 지금은 상품리스트페이지 구현에 대한 이야기를 주로 해보려고 한다.

오늘도 역시나 어려움이 많았다. 이후 회고때도 언급하겠지만 처음에 플래닝의 중요성을 크게 깨달은거같다.
Modal을 만들때도 Card 컴포넌트를 만들때도 파일의 위치가 어디며 props를 어떻게 내려줄지에 따라 코드 작성법이 달라짐을 느꼇다.

상품리스트페이지를 구현하면서 막혔던 부분들을 이야기해보자면 첫번째로는 무한스크롤이였다.
구글링을하며 익숙하지 않은 코드들을 보고 익숙하지 않은 코드들로 구현을 했다보니 이해가 쉽지 않았고 오늘 무한스크롤부분을 확실하게 정리하는게 하나의 목표다
두번째는 상품리스트를 보여줄때 상품의 type별로 filter를 시켜주는 tap을 구현하는것인데 앞서 언급한대로 파일의 구조가 확실히 정하지않고 시작했다보니 기능을 하나 구현할때마다 대공사를 해야하거나 이전에 구현한 부분에 영향을 많이 받게되었다.
시간안에 모든 기능을 구현하지 못했을뿐더러 두번째 문제였던 언급했던 filter tab도 구현하지 못했기에 아쉬움이 크게 남는거같다.

수정이 필요한 부분

  1. Mainpage 화면 비율 맞추기
  2. 서버에서 받아온 데이터 전부를 localStorege가 아닌 대안을 찾기
  3. 상품리스트페이지에 filter tab과 그에 맞는 리스트 보여주기

기억해둘 코드

  1. 무한스크롤 (기록하고싶은 기능구현1 참고)

Product list page 요구사항 (6h)

Product list page 구현 요구사항

  • 연결주소는 /products/list
  • 페이지 상단에 필터기능의 탭 존재
  • 무한스크롤
  • 컴포넌트 정렬
  • 클릭했을때 모달이 띄워짐
  • 상품에 존재하는 북마크 버튼 클릭마다 상태변경

마주했던 에러(1)

아이콘과 버튼을 png, jpg 형태의 이미지로 넣었는데 페이지가 전환되고 새로고침이 될때마다 이미지가 깨지는현상이 발생하여 svg형태로 모두 바꿔주었다.

기록하고싶은 기능구현(1)

오늘의 핵심주제라고 생각하는 무한스크롤이다.
먼저 'useEffect' 훅 내에서 IntersectionObserver 객체를 생성한다.
또한 두번째 인자로 bottomRef 변수를 주어서 bottomRef가 변경되는 경우에만 useEffect 콜백함수가 실행된다.
IntersectionObserver는 요소의 가시성 변화를 감지하는 기능을 제공한다.
IntersectionObserver에 콜백함수를 가지고 있고 콜백함수가 가시성변화를 처리하게된다.

콜백함수에 전달되는 인자 entries란?

entries는 배열형태로서 IntersectionObserverEntry객체를 포함하고 있으며 IntersectionObserverEntry객체는 교차영역정보 및 대상 요소에 대한 추가적인 정보를 포함한다고 한다. 그리고 IntersectionObserverEntry객체의 isIntersecting속성을 사용하여 요소가 교차 영역에 들어왔는지 여부를 확인할 수 있다.

entries의 0번째 인덱스가 isIntersecting true라면 서버에서 받아온 data를 랜더링하게된다.

IntersectionObserver의 두번째 인자는 Observer의 옵션 설정을 할 수 있다. threshold는 콜백함수가 실행되기 위해 관촬 대상 요소가 뷰포트에 최소한 보여야하는 %를 설정하므로 0과 1사이의 숫자로 지정할 수 있으며 rootMargin은 스크롤 된 후 루트로부터 얼마의 공간을 남길것인지 설정할 수 있다.

IntersectionObserver에서는 observer.observe를 호출하여 해당 요소를 관찰 대상으로 등록 할 수 있는데 이때 useRef 훅을 사용하여 관찰 요소를 설정해준다.

useRef 훅 이란?

리엑트의 훅 중 하나로 DOM 요소나 다른 값을 참조하기 위해 사용한다. useReg를 사용하여 생성된 ref 객체는 컴포넌트의 렌더링과 상관없이 유지되며 초기값을 null로 초기화된 객체를 선언할 수 있고 current 속성을 통해 현재 값에 접근할 수 있다.

따라서 const bottomRef = useRef(null); 선언해준 후 페이지 하단에

적어주므로 current를 사용해 bottomRef에 접근했다면 observer.observe를 호출하여 관찰 대상으로 bottomRef.current를 해주는것이다.

위 조건에 맞게 처리가 끝났다면 즉, 언마운트되거나 bottomRef가 변경된다면 더이상의 메모리 누수를 방지하기 위해 componentWillUnmount 메서드가 호출되면서 클린업(정리)함수가 실행된다.
따라서 언마운트 상태에서 bottomRef.current라면 observer.unobserve(bottomRef.current)를 통해 DOM요소의 가시성 변화를 더 이상 감지하지 않게 할 수 있다 즉, IntersectionObserver는 관찰 대상에서 해당 요소를 제거한다.

또한 서버에서 Data를 받아와서 변수에 저장할때 서버의 파라미터가 page로 API요청을 하는데 변수 page를 사용하여 동적으로 페이지의 값을 전달해서 Data를 받아오고 있다.
그렇게 받아온 Data를 상태로 저장하는데 이전의 Data에 누적으로 저장하고 Data를 받아올때마다 서버에 요청할 page에 값을 변경해주기 위해 아래처럼 적어준다

또한 상품을 서버로 부터 받아온 Data가 보여지고 스크롤을 할때 page * 4만큼씩 보여지게 하였다.

setItems((prevItems) => [...prevItems, ...data]);
setPage((prevPage) => prevPage + 1);

<PreviewLi>
 {items.slice(0, page * 4).map((item, index) => 
  <Card key={index} item={item}/>
 )}
</PreviewLi>
import { useEffect, useState, useRef } from 'react';

function Productpage() {
    const [items, setItems] = useState([]);
    const [page, setPage] = useState(1);
    const bottomRef = useRef(null);
  
      
    useEffect(() => {
      loadItems();
    }, []);
  
    useEffect(() => {
      const observer = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting) {
          loadItems();
          }
        },
        { threshold: 0.30, rootMargin: '100px' }
      );
  
      if (bottomRef.current) {
        observer.observe(bottomRef.current);
      }
  
      return () => {
        if (bottomRef.current) {
          observer.unobserve(bottomRef.current);
        }
      };
    }, [bottomRef]);
  
    const loadItems = () => {
      fetch('http://cozshopping.codestates-seb.link/api/v1/products?page=' + page)
        .then((response) => response.json())
        .then((data) => {
          setItems((prevItems) => [...prevItems, ...data]);
          setPage((prevPage) => prevPage + 1);
        })
        .catch((error) => {
          console.error(error);
        });
    };

      return(
  			(..중략)

        <PreviewSection>
            <PreviewLi>
                {items.slice(0, page * 4).map((item, index) => 
                 <Card key={index} item={item}/>
                )}
            </PreviewLi>
          </PreviewSection>
          <div ref={bottomRef} />
        </MainContainer>
    )
}

export default Productpage;

오늘은 무한스크롤과 씨름을했다. 많은 시간을 투자한만큼 잘 이해하고 넘어갔으면 좋겠다. 이번 솔로프로젝트때 Modal, Dropdown Tab을 구현했는데 이전에 공부하면서 충분히 복습했다고 생각했는데 막상 다시 구현하려니 많이 잊어먹은것을 인지했다. 남겨둔 기록을 보면서 구현했지만 조금더 익숙해질때까지 연습해야겠다. 그리고 기록을 잘해두는게 도움이 많이 되는거같아서 앞으로 진행될 프로젝트들도 기록을 잘 남겨서 필요할때마다 볼 수 있도록 해야겠다. 이제 조금은 쉬고 여유가 될때 이번 솔로프로젝트의 회고를 적어봐야겠다.

profile
개발자 동동

0개의 댓글