ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TS] 무한스크롤 뉴스 뷰어 구현
    Typescript 2023. 6. 10. 00:58

     

     

     

     

    타입스크립트를 익히기 위해서 프로젝트를 진행했는데,

    타입스크립트를 사용했다고 하기엔 민망할 정도로 타입 선언이 없다,,,,

    https://newsapi.org/

     

    News API – Search News and Blog Articles on the Web

    “Ascender AI has a mission to apply AI to the media, and NewsAPI is one of our most valuable resources. Ascender is redefining how users interact with complex information, and the NewsAPI feed is an essential showcase for our technologies.” Braddock Ga

    newsapi.org

     

    이전에 과제 때문에 JS로 뉴스뷰어를 구현한 적이 있었는데, 이번에는 TS 학습을 위해 다시 찾게 되었다.

    Response Object가 복잡하지 않아서, 크게 어려움은 없었다.

     

     

     

     

     

    Navbar.tsx

    export const NavBar = () => {
      const location = useLocation();
      const CurrentCategory: string = location.state.category;
      console.log(CurrentCategory);
    
      return (
        <>
          <NavigationBar>
            <li>
              <Link
                to="/"
                state={{ category: "all" }}
                className={CurrentCategory === "all" ? "active" : ""}
              >
                전체보기
              </Link>
            </li>
            <li>
              <Link
                to="/"
                state={{ category: "business" }}
                className={CurrentCategory === "business" ? "active" : ""}
              >
                비즈니스
              </Link>
            </li>
            <li>
              <Link
                to="/"
                state={{ category: "entertainment" }}
                className={CurrentCategory === "entertainment" ? "active" : ""}
              >
                엔터테인먼트
              </Link>
            </li>
            <li>
              <Link
                to="/"
                state={{ category: "health" }}
                className={CurrentCategory === "health" ? "active" : ""}
              >
                건강
              </Link>
            </li>
            <li>
              <Link
                to="/"
                state={{ category: "science" }}
                className={CurrentCategory === "science" ? "active" : ""}
              >
                과학
              </Link>
            </li>
            <li>
              <Link
                to="/"
                state={{ category: "sports" }}
                className={CurrentCategory === "sports" ? "active" : ""}
              >
                스포츠
              </Link>
            </li>
            <li>
              <Link
                to="/"
                state={{ category: "technology" }}
                className={CurrentCategory === "technology" ? "active" : ""}
              >
                기술
              </Link>
            </li>
          </NavigationBar>
        </>
      );
    };

    파라미터를 Route에서 넘길까 고민하다가, url 변경은 하지 않을 것이라서 Link에서 바로 넘겼다. 

     

     

     

     

     

    NewsFetch.ts

    export const NewsFetch = async (category: string, page: number) => {
      let newsUrl = "";
      const apiKey = "########################";
      const pageSize = 5;
    
      if (category === "all") {
        newsUrl = `https://newsapi.org/v2/top-headlines?country=kr&apiKey=${apiKey}&pageSize=${pageSize}&page=${page}`;
      } else {
        newsUrl = `https://newsapi.org/v2/top-headlines?country=kr&category=${category}&apiKey=${apiKey}&pageSize=${pageSize}&page=${page}`;
      }
      const getNews = await axios.get(newsUrl);
      const newsList = getNews.data;
      return newsList;
    };

     

     

     

     

     

    News.tsx

    interface NewsType {
      urlToImage: string;
      title: string;
      description: string;
      url: string;
    }
    
    export const News = () => {
      const [newsList, setNewsList] = useState<NewsType[]>([]);
      let page = 1;
      const location = useLocation();
      const category: string = location.state.category;
    
      //카테고리 변화가 있을 때
      useEffect(() => {
        (async () => {
          const getNewsList = await NewsFetch(category, page);
          setNewsList((prevNewsList) => [...getNewsList.articles]);
        })();
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [category]);
    
      // --------------- IntersectionObserver -------------------
      //page의 변화(스크롤이 닿을 때)가 있을 때
      useEffect(() => {
        const observer = new IntersectionObserver(
          (entries) => {
            entries.forEach((entry) => {
              if (entry.isIntersecting) {
                page++;
                (async () => {
                  const getNewsList = await NewsFetch(category, page);
                  setNewsList((prevNewsList) => [
                    ...prevNewsList,
                    ...getNewsList.articles,
                  ]);
                })();
              }
            });
          },
          {
            root: null,
            rootMargin: "10px",
            threshold: 1.0,
          }
        );
    
        const target = document.querySelector(".loading") as HTMLElement;
        if (target) {
          observer.observe(target);
        }
    
        return () => {
          if (target) {
            observer.unobserve(target);
          }
        };
      }, [category, page]);
    
      // --------------------------------------------------------
      if (!newsList) {
        return (
          <Loading className="loading">
            <img src={spinner} alt="spinner" />
          </Loading>
        );
      }
    
      return (
        <>
          {newsList.map((news) => {
            return (
              <NewsLink to={news.url}>
                <Thumbnail src={news.urlToImage} alt="뉴스 이미지"></Thumbnail>
                <Contents>
                  <Title>{news.title}</Title>
                  <Description>{news.description}</Description>
                </Contents>
              </NewsLink>
            );
          })}
          <Loading className="loading">
            <img src={spinner} alt="spinner" />
          </Loading>
        </>
      );
    };

     

    Intersection Observer API를 사용해서 무한스크롤을 구현하였다.

     

    카테고리의 변화와 페이지의 변화를 따로 useEffect 안에 넣었는데,

    이렇게 하는 게 맞는지는 모르겠지만 ㅠㅠ 잘 동작은 한다.

     

     

     

     

     

     

     

     

     

     

    잠깐 푸념을 해보자면, 타입스크립트가 너무 어려워서 현타가 살짝 왔다.

    개발자의 자질이 없다고 생각하여 이직을 포기해야 하나 생각이 잠깐 들었지만,

    다른 프로젝트들을 하나씩 해보면서 다시 생각을 해보기로,,,, (지금도 현타)

     

     

     

     

     

     

    댓글