본문 바로가기

프로젝트/나무(나누고 나눔받는 무한 지식 품앗이)

[React&Firestore] 캐러셀 컴포넌트 데이터 가져오기

반응형

오늘은 캐러셀 컴포넌트에 보일 태그 별 게시글 리스트 데이터 가져오기를 진행했다. 에러 5 종류 이상은 만나 겨우 성공했다. 진짜 온갖 진을 다 뺀 것 같다. 그래도 해결하고 나니 후련하다.

코드를 살펴보기 전에 내가 만들어준 db 구조를 먼저 보자. 데이터 평면화를 위해 다음과 같이 구조를 짰다. 

사용자가 선택한 태그 목록 중 현재 캐러셀 컴포넌트에서 보여주고 있는 태그의 인덱스가 키포인트다. 이 값을 useState를 통해 관리해준다. 태그 별 게시글 리스트 저장을 위해 carouselData 라는 이름의 객체 또한 useState를 통해 관리해준다.

posts 컬렉션에 있는 문서들 중 tags 필드의 값인 배열에 해당 tag를 포함하는 문서를 찾아 배열에 넣고, 그 배열을 carouselData 객체에 태그 명의 값으로 저장해준다. 

이를 통해 사용자가 선택한 태그에 따른 각각의 게시글 리스트를 캐러셀 컴포넌트를 통해 조회할 수 있다. 

import {
  doc,
  collection,
  query,
  where,
  onSnapshot,
  getDoc,
  orderBy
} from 'firebase/firestore';
import { db } from '../../firebase';
import CarouselItem from './carouselItem';
import { GreenButton } from '../UI/button';

const Carousel = ({ setPostDetail, tagList, setComp }) => {
  const carouselRef = useRef(null);
  const [tagIdx, setTagIdx] = useState(0);
  const [carouselData, setCarouselData] = useState({});

  const fetchPostsByTags = async (selectedTagIdx) => {
    try {
      const updatedCarouselData = JSON.parse(JSON.stringify(carouselData));

      const tagRef = doc(db, 'tags', tagList[selectedTagIdx]);
      const tagSnapshot = await getDoc(tagRef);

      if (tagSnapshot.exists()) {
        const q = query(
          collection(db, 'posts'),
          where('tags', 'array-contains', tagList[selectedTagIdx]),
          orderBy('createdAt', 'desc')
        );
        const unsubscribe = onSnapshot(q, (snapshot) => {
          const tagPosts = [];
          snapshot.forEach((d) => {
            const postData = d.data();
            tagPosts.push({ id: d.id, ...postData });
          });

          updatedCarouselData[tagList[selectedTagIdx]] = tagPosts;
          setCarouselData(updatedCarouselData);
        });

        return () => unsubscribe();
      }
    } catch (error) {
      console.error('Error fetching posts by tags: ', error);
    }
  };

  useEffect(() => {
    fetchPostsByTags(tagIdx);
  }, [tagIdx]);

  const settings = {
    lazyLoad: true,
    infinite: true,
    speed: 500,
    slidesToShow: 1,
    slidesToScroll: 1,
    initialSlide: 2,
    arrows: true,
    beforeChange: (current, next) => {
      const carouselElement = carouselRef.current;

      if (carouselElement && carouselElement.scrollTo) {
        carouselElement.scrollTo({
          top: 0,
          behavior: 'smooth'
        });
      }

      if (next > current) {
        if (tagIdx < tagList.length - 1) setTagIdx((prevIdx) => prevIdx + 1);
        else setTagIdx(0);
      } else if (next < current) {
        if (tagIdx > 0) setTagIdx((prevIdx) => prevIdx - 1);
        else setTagIdx(tagList.length - 1);
      }
    }
  };

  return (
    <>
      {tagList.length ? (
        <CarouselWrapper>
          <TagWrapper>
            <p>{`# ${tagList[tagIdx]}`}</p>
            <button onClick={() => setComp('tag')}>태그 추가</button>
          </TagWrapper>
          <Slider ref={carouselRef} {...settings}>
            {tagList.map((_, idx) => (
              <CarouselItemContainer key={idx}>
                {carouselData[tagList[tagIdx]] &&
                  carouselData[tagList[tagIdx]].map((post, i) => (
                    <CarouselItem
                      key={i}
                      category={tagList[tagIdx]}
                      title={post.title}
                      content={post.content}
                      createdAt={post.createdAt}
                      idx={i}
                      setPostDetail={setPostDetail}
                    />
                  ))}
              </CarouselItemContainer>
            ))}
          </Slider>
        </CarouselWrapper>
      ) : (
        <GuideWrapper>
          <div>아직 태그를 설정하지 않았어요!</div>
          <div>지금 태그를 설정하러 가볼까요?</div>
          <GreenButton>태그 설정하러 가기</GreenButton>
        </GuideWrapper>
      )}
    </>
  );
};

참고로 timestamp는 다음과 같이 출력해줘야 한다. 직접 출력해주려고 하면 오류가 뜬다. 나도 알고 싶지 않았다^^ 이것 때문에 모니터 부실뻔 '-'

const formattedDate = new window.Date(createdAt.seconds * 1000);

return (
    <ItemWrapper
      <Date>{formattedDate.toLocaleString()}</Date>
    </ItemWrapper>
  );
};

 

참고한 글)

https://stackoverflow.com/questions/30130241/typeerror-date-is-not-a-constructor

 

TypeError: Date is not a constructor

So, I've been making forms for my company for some time now with pretty easy Javascript that has worked for me in the past. However all of a sudden it's kicking out the error: TypeError: Date is no...

stackoverflow.com

 

반응형