S-FLEX(NOMFLIX CLONE) - Loading & Footer

짜스의 하루 ·2024년 7월 4일

Loading 구현

보통 페이지 처음 들어가면
로딩되는 화면을 보여주는 것을 많이 봤는데 나는 S-Flex 인 만큼 브랜드(?) 를 중점으로 보여주고 싶었다!


이렇게 2초정도 로딩 페이지를 보여준 뒤, 메인 페이지가 뜨게 된다.
위에 자세히 살펴보면 페이지 속 내부 페이지를 이동할 때에는 Spinner를 사용해서 로딩되고 있음을 알려주고 있다!

MainLoading

import React, { useState, useEffect, ReactNode, Children } from 'react';
import styled from 'styled-components';
import { LoadingLogo } from '../CategoryFont';
import { motion } from 'framer-motion';

// 스타일 정의
const Wrapper = styled.div`
  width: 100vw;
  height: 100vh;
  background-color: ${(props) => props.theme.black.veryDark};
`;

const LoadingContainer = styled(motion.div)`
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
`;

const LogoVariants = {
  stop: { rotate: 0 },
  active: {
    rotateY: 360,
    transition: { duration: 3, loop: Infinity },
  },
};

type LoadingProps = {
  children: React.ReactNode; // children 속성은 JSX의 특성으로, React.ReactNode으로 타입을 지정합니다.
};

const MainLoading = ({ children }: LoadingProps) => {
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const timer = setTimeout(() => {
      setIsLoading(false);
    }, 2500);

    return () => clearTimeout(timer);
  }, []);

  return isLoading ? (
    <Wrapper>
      <LoadingContainer variants={LogoVariants} initial="stop" animate="active">
        {LoadingLogo}
      </LoadingContainer>
    </Wrapper>
  ) : (
    <div>{children}</div>
  );
};

export default MainLoading;

여기서 LoadingLogo는 svg 파일이다. 내가 이전에 이야기 한 적 있는데 (Header편에 들어가보면 로고 만들 수 있는 사이트가 있다)

MainLoading 컴포넌트는 LoadingProps 타입을 props로 받는데, children props는 자식 요소를 포함한다.

--> setTimeout 함수를 사용하여 2500밀리초(2.5초) 후에 setIsLoading(false)를 호출하여 로딩 상태를 false로 변경하게 된다.
useEffect의 반환 함수를 사용하여 컴포넌트가 언마운트될 때 clearTimeout을 호출하여 타이머가 정리되게 된다.

간단하게 설명하자면, 화면이 로딩 중일 때 로딩 애니메이션을 보여주고, 로딩이 완료되면 자식 컴포넌트(children)를 렌더링하는 React 컴포넌트이다.

Loading

처음에는 리액트 스피너로 로딩을 구현하면 된다고 해서 따라하다가 gif 파일이 import가 되지 않길래
그래 내가 하던 방식으로 하자 ^^ 해서 fontawesome에서 아이콘을 가져와서 Loading을 구현했다.

const Loading = () => {
  return (
    <>
      <Background>
        <LoadingText>잠시만 기다려 주세요.</LoadingText>
        <Spinner icon={faSpinner} spin />
      </Background>
    </>
  );
};

export default Loading;

정말 간단하게 화면만 구성해두고,
Loading을 구현이 필요한 컴포넌트에 가서
--> react-query 라이브러리의 isLoading 속성을 사용하여 로딩 상태를 관리하면 된다.
react-query는 데이터를 가져오는 동안의 로딩 상태를 자동으로 관리하며, 데이터가 로드되면 자동으로 해당 데이터를 제공하게 된다.

즉, 데이터를 가져오는 동안은 내가 구현한 <Loading /> 를 보여주게 되고, 데이터 로딩이 끝나면, 데이터를 보여주는 것이다.

const Home = () => {
  const { data, isLoading } = useQuery(
    ['movies', 'distinct'],
    getDistinctMovies
  );

  return (
    <>
      <Wrapper>
        {isLoading ? (
          <Loading />
        ) : (
          <>
            <Banner
              bgPhoto={makeImagePath(data?.nowPlaying[0].backdrop_path || '')}
            >
              <Title>{data?.nowPlaying[0].title}</Title>
              <Overview>{data?.nowPlaying[0].overview}</Overview>
            </Banner>

            <Movie />
          </>
        )}
      </Wrapper>
    </>
  );
};

export default Home;

예로 Home 컴포넌트를 확인하면,
usequery가 제공하는 isLoading속성을 사용해서
isLoading일 때 ? --> <Loading/> 컴포넌트 보여줘~ 라고 이야기 해준 것이라고 생각하면 된다!

그럼 데이터를 불러오는 동안은

이런 화면이 나타나게 된다.
물론 저 원도 회전하게 된다!


쿠팡플레이를 보니까

이렇게 하단(footer)를 구성해놨다! 사실 하단이 매우 허전하기도 하고 뭔가 없어보였는데 바로 비슷하게 만들어 보았다.

const Footer = () => {
  return (
    <Container>
      <Logo width="50" height="15" viewBox="0 0 41.998 13.222">
        {SFlexLogo}
      </Logo>
      <VerticalLine />
      <Explain>
        S-FLEX(주) | 대표이사: 이서연 <br /> 서울시 강서구 화곡동
      </Explain>
      <Explain>
        고객센터: 0906-1104 <br />
        대표 메일: lsy_0906@naver.com <br />
        제휴 문의: lsy_0906@naver.com
      </Explain>

      <LinkIcon>
        <a
          href="https://github.com/seoyeon1123"
          target="_blank"
          rel="noopener noreferrer"
        >
          <GitLogo icon={faGithub} />
        </a>
        <a
          href="https://velog.io/@leeeee/posts"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Velog icon={faHome} />
        </a>
      </LinkIcon>
    </Container>
  );
};

export default Footer;

fontawesome을 통해서 github로고와 blog 로고를 가져와 클릭할 때, 나의 사이트들로 이동할 수 있도록, 코드를 작성해 두었고,
나머지도 정보들도 쿠팡 플레이와 비슷하게 작성해 두었다!

<Footer/> 컴포넌트의 경우에는 모든 화면에 공통적으로 등장해야 하는 컴포넌트이기 때문에,

const App = () => {
  return (
    <>
      <BrowserRouter basename="S-FLEX">
        <Header />
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/movies/:category/:movieId" element={<Home />} />

          <Route path="/tv" element={<TV />} />
          <Route path="/tv/:category/:tvId" element={<TV />} />

          <Route path="/genre/movies" element={<GenreMovie />} />
          <Route path="/genre/movies/:movieId" element={<GenreMovie />} />

          <Route path="/genre/tv" element={<GenreTv />} />
          <Route path="/genre/tv/:tvId" element={<GenreTv />} />

          <Route path="/search" element={<Search />} />
          <Route path="/search/movies/:movieId" element={<Search />} />
          <Route path="/search/tv/:tvId" element={<Search />} />
          <Route path="/search?keyword=:keyword" element={<Search />} />
        </Routes>
        <Footer />
      </BrowserRouter>
    </>
  );
};

export default App;

이렇게 위치상 맨 하단에 <Footer/> 를 추가해 주었다
--> 이로써 모든 화면이 렌더링 될 때 자동으로 Footer 컴포넌트가 렌더링 되게 된다!


길고 긴 여정의 끝!

와 진짜로 생각보다 빡세고
힘들고 어려웠다 ..🤦‍♀️ 근데 신기한건 해보고 싶은것도, 만들고 싶은것도 정말 많았다

그리고 코드가 중복되는게 있는데 이를 어떻게 처리해야 할지도 고민이 된다.
슬라이드 부분, BigMovie(BigTv)부분 등 겹치거나 거의 똑같은 부분이 많았는데 이를 정리하는 부분이 어려워 정리가 되지 않은 점이 아쉬움이 남는다.

그래도 이번 프로젝트(클론 코딩)을 하면서 처음에는 니꼬쌤을 따라하면서 틀은 잡았지만,
나머지 부분은 내가 하고 싶은 대로 (Genre, Search) 부분을 만들어 보면서 조금은 리액트와 익숙해지는 시간을 가질 수 있었던 것 같다

나름 여러 컴포넌트로 쪼개보고 나눠보고 코드를 줄여보려고 시도도 해보는 발전할 수 있었던 작은 프로젝트였다.

나는 열심히 했다고 생각하기 때문에 다들 내 깃허브 배포 주소에 놀러와서
구경하고 가라!

S-FLEX

혹시 수정 사항이나 더 좋은 아이디어가 있다면 언제든지 알려주면 좋겠다!

profile
2024. 01. 02 ~ 백앤드 공부 시작, 2024. 04.01 ~ 프론트 공부 시작

0개의 댓글