[개인 포트폴리오] [트위터 클론 SNS Deli] 분석 Part. 13 'timeline.tsx'

2024. 7. 8. 19:04웹개발 포트폴리오

728x90
반응형

timeline.tsx

import styled from "styled-components";
import { Timestamp, collection, deleteDoc, doc, limit, onSnapshot, orderBy, query } from "firebase/firestore";
import { db, storage } from '../firebase';
import Tweet from "./post"; // 트윗 컴포넌트 가져오기
import { Unsubscribe } from "firebase/auth";
import { useEffect, useState } from "react";
import { getDownloadURL, ref } from "firebase/storage";
import { media } from "../media-query-file';

// 트윗 인터페이스 정의
export interface ITweet {
  id: string;
  photos?: string[];
  tweet: string;
  userId: string;
  username: string;
  createdAt: Timestamp;
  profileImage?: string;
}

// 스타일드 컴포넌트 정의
const Wrapper = styled.div`
  display: flex;
  gap: 10px;
  flex-direction: column;
  overflow-y: scroll;

  /* Chrome, Safari, Opera에서 스크롤바 숨기기 */
  &::-webkit-scrollbar {
    display: none;
  }

  /* IE, Edge, Firefox에서 스크롤바 숨기기 */
  -ms-overflow-style: none;
  scrollbar-width: none;

  ${media.mobile(`
    body {
      padding: 0 10px !important;
    }
  `)}

  ${media.tablet(`
    body {
      padding: 0 30px !important;
    }
  `)}

  ${media.desktop(`
    body {
      padding: 0 50px !important;
    `)}
`;

// 타임라인 컴포넌트 정의
const Timeline = () => {
  const [tweets, setTweets] = useState<ITweet[]>([]); // 트윗 상태 정의

  useEffect(() => {
    let unsubscribe: Unsubscribe | null = null;
    const fetchTweets = async () => {
      const tweetsQuery = query(
        collection(db, "tweets"),
        orderBy("createdAt", "desc"),
        limit(25) // 최신 25개의 트윗을 가져옴
      );

      // 실시간으로 트윗을 가져옴
      unsubscribe = onSnapshot(tweetsQuery, async (snapshot) => {
        const tweetsData = await Promise.all(
          snapshot.docs.map(async (doc) => {
            const data = doc.data();
            let profileImage = "";
            try {
              // 사용자 프로필 이미지 URL 가져오기
              profileImage = await getDownloadURL(ref(storage, `avatars/${data.userId}`));
            } catch (e) {
              console.log(e);
            }
            return {
              id: doc.id,
              tweet: data.tweet,
              createdAt: data.createdAt instanceof Timestamp ? data.createdAt : Timestamp.fromDate(new Date(data.createdAt)),
              userId: data.userId,
              photos: data.photos || [],
              profileImage,
              username: data.username
            };
          })
        );
        setTweets(tweetsData as ITweet[]);
      });
    };

    fetchTweets();

    return () => {
      if (unsubscribe) {
        unsubscribe();
      }
    };
  }, []);

  // 트윗 삭제 함수
  const onDeleteTweet = async (tweetId: string) => {
    const ok = confirm('포스트를 삭제하시겠습니까?');
    if (!ok) return;
    try {
      await deleteDoc(doc(db, 'tweets', tweetId));
      setTweets(tweets.filter(tweet => tweet.id !== tweetId)); // 트윗 상태 업데이트
    } catch (e) {
      console.log(e);
    }
  };

  return (
    <Wrapper>
      {tweets.map((tweet) => (
        <Tweet
          key={tweet.id}
          id={tweet.id}
          photos={tweet.photos || []}
          tweet={tweet.tweet}
          userId={tweet.userId}
          username={tweet.username}
          createdAt={tweet.createdAt}
          profileImage={tweet.profileImage || ""}
          onDeleteTweet={onDeleteTweet}
        />
      ))}
    </Wrapper>
  );
};

export default Timeline;

 

위 코드는 포스트 타임라인을 표시하는 컴포넌트입니다. 주요 기능은 다음과 같습니다:

  1. 포스트 데이터 가져오기: Firestore에서 최신 25개의 포스트를 실시간으로 가져와서 tweets 상태에 저장합니다. 이를 위해 onSnapshot 함수를 사용하여 실시간 업데이트를 구독합니다.
  2. 프로필 이미지 가져오기: 포스트 작성자의 프로필 이미지를 Firebase Storage에서 가져와서 포스트 데이터에 포함시킵니다.
  3. 포스트 삭제: 사용자가 특정 포스트를 삭제할 수 있습니다. onDeleteTweet 함수가 이를 처리하며, Firestore에서 해당 포스트를 삭제하고 tweets 상태를 업데이트합니다.
  4. 스타일링: 여러 styled-components를 사용하여 타임라인의 레이아웃과 스타일을 정의합니다. 스크롤바를 숨기기 위해 특정 스타일을 적용했습니다.
728x90
반응형