[React] 넷플릭스 클론코딩 (4)

노유성·2023년 5월 12일
0
post-thumbnail

⭐Row component

🪐App.js에서 가져오기

import './App.css';
import requests from './api/requests';
import Banner from './components/Banner';
import Footer from './components/Footer';
import Nav from './components/Nav';
import Row from './components/Row';
function App() {
  return (
    <div className="App">
    // 기존 component
      <Nav />
      <Banner />
      <Row 
      title="NETFILX ORIGINALS"
      id="NO"
      fetchUrl={requests.fetchNetflixOriginals}
      isLargerRow/>
    
    // 추가된 Row component, props가 내려가고 있다.
      <Row title="Trending Now"
      id="TN"
      fetchUrl={requests.fetchTrending}/>
      <Row title="Top Rated"
      id="TR"
      fetchUrl={requests.fetchTopRated}/>
      <Row title="ACTION Movies"
      id="AM"
      fetchUrl={requests.fetchActionMovies}/>
      <Row title="NETFILX ORIGINALS"
      id="CM"
      fetchUrl={requests.fetchComedyMovies}/>
      <Footer />
    </div>
  );
}

export default App;

App.js 파일에 Row 컴포넌트들을 추가한다. api 모듈에 있는 requests에 정의된 앤드포인트들과 각 컴포넌트들을 식별하기 위한 고유 id, 그리고 어떤 정보를 나타내고 있는지 title, 총 3개를 props로 내려준다.

title은 해당 Row 컴포넌트들이 어떤 컴포넌트인지 사용자에게 보여주기 위해 앱에 타이틀을 걸어놓을 건데 우리는 컴포넌트들을 재활용해서 사용할 것이기 때문에 유동적으로 title을 설정하기 위해서 props로 내려주며 url도 마찬가지이다.

🪐Row.js

🌈소스코드

import axios from '../api/axios';
import React, { useEffect, useState } from 'react'
import './Row.css'
import MovieModal from './MovieModal';
export default function Row({ isLargerRow, title, id, fetchUrl }) {

  const [movies, setMovies] = useState([]);
  const [modalOpen, setModalOpen] = useState(false);
  const [movieSelected, setMovieSelected] = useState({})

  const handleClick = (movie) => {
    setModalOpen(true);
    setMovieSelected(movie);
  }

  useEffect(() => {
    fetchMovieData();
  }, [])

  const fetchMovieData = async () => {
    const request = await axios(fetchUrl);
    setMovies(request.data.results)
  }
  return (
    <section className='row'>
      <h2>{title}</h2>
      <div className='slider'>
        <div className='slider__arrow-left'>
          <span className='arrow'
            onClick={() => {
              document.getElementById(id).scrollLeft -= window.innerWidth - 80;
            }}>
            {"<"}
          </span>
        </div>
        <div id={id} className='row__posters'>
          {movies.map((movie) => (
            <img
              key={movie.id}
              className={`row__poster ${isLargerRow && "row__posterLarge"}`}
              src={`https://image.tmdb.org/t/p/original/${isLargerRow ? movie.poster_path : movie.backdrop_path}`}
              loading='lazy'
              alt='movie.name'
              onClick={() => handleClick(movie)}
            />
          ))}
        </div>
        <div className='slider__arrow-right'>
          <span className='arrow'
            onClick={() => {
              document.getElementById(id).scrollLeft += window.innerWidth - 80;
            }}>
            {">"}
          </span>
        </div>
      </div>  
      {
        modalOpen && <MovieModal {...movieSelected} setModalOpen={setModalOpen}/>
      }
    </section>
  )
}

🌠useEffect()

의존성 배열에 아무것도 넣지 않음으로써, 처음 한 번 렌더링 시에만 실행되는 함수를 넣어놓았다.

fetchMovieData는 부모로부터 받은 props 중에 영화 정보들이 담긴 endpoint를 받아서 axios로 데이터를 가져온다.

🌠movies state

fetchMovieData로 받아온 데이터는 movies state에 저장한다. request 변수에 request.data.results property에 데이터가 존재하는 것으로 유추된다.

🌠handleClick()

modal을 다루는 함수이다. 우리가 Row component를 완성하면

위와 같은 화면을 볼 수 있는데 위 화면에서 특정 영화를 클릭하면은 상세 정보를 보여주는 모달창을 만들 예정이다.
그런 과정에서 특정 이미지를 클릭했을 때 클릭이벤트를 받아서 처리하는 함수이다.
modalOpen state변수(flag 변수)를 true로 변경하며 선택된 영화의 정보를 moive state 변수에 저장한다.

UI 부분을 보면 movies state로 map을 돌리면서 이미지들을 생성하는데 각각의 이미지에 handleClick() 함수가 이벤트리스너로 등록된다.

⭐슬라이딩 구현


특정 Row의 끝 부분에 마우스 포인터를 올려놓으면 더 많은 영화정보를 보여주기 위한 "화살표 버튼"이 UI로 구성되어 있다. 누르면은 더 많은 정보를 보여준다

🪐scroll property

<span className='arrow'
            onClick={() => {
              document.getElementById(id).scrollLeft -= window.innerWidth - 80;
            }}>
</span>

이벤트리스너로 등록되어 있는 화살표 함수의 저 한 줄이 스크롤(슬라이딩)을 구현한 부분이다.

부모 컴포넌트로 부터 Row component가 받아온 id를 getElementById()를 이용해 컴포넌트를 선택한다.

연산 단위는 px이며 window객체의 innerWidth를 통해 현재 브라우저 창에서 전체 넓이를 구할 수 있다.

🌈자세한 내용

https://developer.mozilla.org/ko/docs/Web/API/Document

⭐Footer

import React from "react";
import styled from "styled-components";

export default function Footer() {
    return (
        <FooterContainer>
            <FooterContent>
                <FooterLinkContainer>
                    <FooterLinkTitle>넷플릭스 대한민국</FooterLinkTitle>
                    <FooterLinkContent>
                        <FooterLink href="https://help.netflix.com/ko/node/412">
                            넷플릭스 소개
                        </FooterLink>
                        <FooterLink href="https://help.netflix.com/ko">
                            고객 센터
                        </FooterLink>
                        <FooterLink href="https://help.netflix.com/ko/">
                            미디어 센터
                        </FooterLink>
                        <FooterLink href="https://help.netflix.com/ko/">
                            이용 약관
                        </FooterLink>
                    </FooterLinkContent>
                    <FooterDescContainer>
                        <FooterDescRights>
                            Netflix Rights Reserved.
                        </FooterDescRights>
                    </FooterDescContainer>
                </FooterLinkContainer>
            </FooterContent>
        </FooterContainer>
    );
}

const FooterContainer = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 40px 0;
  border-top: 1px solid rgb(25, 25, 25);
  width: 100%;
  position: relative;
  z-index: 100;

  @media (max-width: 769px) {
    padding: 20px 20px;
    padding-bottom: 30px;
  }
`;

const FooterContent = styled.div``;

const FooterLinkContainer = styled.div`
  width: 500px;

  @media (max-width: 768px) {
    width: 100%;
  }
`;

const FooterLinkTitle = styled.h1`
  color: gray;
  font-size: 17px;
`;

const FooterLinkContent = styled.div`
  display: flex;
  justify-content: space-bewteen;
  flex-wrap: wrap;
  margin-top: 35px;

  @media (max-width: 768px) {
    margin-top: 26px;
  }
`;

const FooterLink = styled.a`
  color: gray;
  font-size: 14px;
  width: 110px;
  margin-bottom: 21px;
  text-decoration: none;

  &:hover {
    text-decoration: underline;
  }

  @media (max-width: 768px) {
    margin-bottom: 16px;
  }
`;

const FooterDescContainer = styled.div`
  margin-top: 30px @media (max-width: 768px) {
    margin-top: 20px;
  }
`;

const FooterDescRights = styled.h2`
  color: white;
  font-size: 14px;
  text-align: center;
`;

홈페이지의 마지막 부분에 회사에 대한 정보, 앱에 대한 정보를 보여주는 부분이다.

『[React] 넷플릭스 클론코딩 (3)』에서 기술했던 styled component를 이용해서 만든 Footer이다. 기술적인 내용은 없고 각 css가 어떻게 구성되어 있는지 나중에 확인해야겠다.

🌌정리하며

다음에 만들 모달창은 Row컴포넌트의 끝단에 컴포넌트의 형태로 추가가 되는데 결국엔 모달창도 현재 페이지에 html로 구성되는 거다.

하지만 화면을 드래깅하고 특정 이벤트가 발생하면 모달창이 이상하게 사라지거나 보여질 수 있는데 이 부분을 강사님은 css로 조절했다.

profile
풀스택개발자가되고싶습니다:)

0개의 댓글