이제 모든 장르를 리액트 앱에 불러올 차례다. request.js에서 가지고 왔던 장르들을 App.js에 있는 넷플릭스 오리지널과 실시간 트렌드 아래에 넣어주면 된다.
<Row title="Top Rated" fetchUrl={request.fetchTopRated} />
<Row title="Action Movies" fetchUrl={request.fetchActionMovies} />
<Row title="Comedy Movies" fetchUrl={request.fetchComedyMovies} />
<Row title="Horror Movies" fetchUrl={request.fetchHorrorMovies} />
<Row title="Romance Movies" fetchUrl={request.fetchRomanceMovies} />
<Row title="Documentaries" fetchUrl={request.fetchDocumentaries} />
해당 코드를 작성하면 사진처럼 리액트 앱에 다양한 장르를 불러올 수 있다. 다음은 App.css다. 먼저 새로 작성하기 위해 원래 존재하던 코드를 다 지워준다. 그리고 아래 코드를 작성한다.
*{
margin: 0;
}
마진 0을 주는 이유는 간단하다. 브라우저마다 기본 마진 값이 다르기 때문이다. 내가 설정한 css 값이 다른 브라우저에서도 동일하게 보이기 위해 마진 값을 0으로 초기화 해주는 것이다. 이제 다시 App.js로 넘어가겠다. 넷플릭스 오리지널 row 포스터의 크기가 크고 비율이 다른 것을 해결해야 한다.
[App.js]
<Row
title="NETFLIX ORIGINALS"
fetchUrl={request.fetchNetflixOriginals}
isLargeRow
/>
[Row.js]
{movies.map(movie => (
<img
key={movie.id}
className={`row_poster &{isLargeRow && "row_posterLarge"}`}
src={`${base_url}${isLargeRow ? movie.poster_path : movie.backdrop_path}`}
alt={movie.name} />
))}
App.js에서 넷플릭스 오리지널 안에 isLargeRow라는 property를 준다. 그리고 Row.js는 className을 변경하고 아래에 있는 src를 수정해준다. 그리고 혼자 작은 오리지널 포스터 크기를 키우기 위해 Row.css 코드를 아래처럼 작성해준다. 또한 왼쪽에 딱 붙어있는 row에 마진을 줘서 좀 떨어트리겠다.
.row {
margin-left: 20px;
}
.row__posterLarge {
max-height: 250px;
}
.row__posterLarge:hover {
transform: scale(1.09);
}
이제 Nav와 배너를 넣어야 한다. 배너부터 넣도록 하겠다. App.js에 <Banner />를 div className="app" 아래에 추가해준다. 그리고 Banner.js 파일을 새로 만들어준다.
* js 파일을 새로 만들었으니까 App.js에 import Banner from './Banner'를 넣어주는 것을 잊지 말자.
사실 하나하나 천천히 모든 과정을 올리고 싶은데 코드가 너무 길어서 ... 한 번에 올리려고 한다. 대신 주석 처리로 코드에 대한 설명을 대신하려고 한다. 아래는 Banner.js 코드이다.
import React, { useState, useEffect } from 'react';
import axios from './axios';
import requests from './requests';
import './Banner.css'
function Banner() {
const [movie, setMovie] = useState([]); // 새로고침 시 넷플 오리지널 컨텐츠 랜덤으로 추천
useEffect(() => {
async function fetchData() {
const request = await axios.get(requests.fetchNetflixOriginals);
setMovie(
request.data.results[
Math.floor(Math.random() * request.data.results.length - 1)
]
);
return request;
}
fetchData();
}, []);
console.log(movie);
function truncate(str, n) {
return str?.length > n ? str.substr(0, n - 1) + "..." : str;
}
// 영상 설명이 길어졌을 때 말줄임표(...) 생성
/*
헤더를 따로 배너로 설정한 이유는 배너의 이미지와 내용에 서로 영향을
끼치지 않게 하고 다른 효과를 주기 위해서
*/
return (
<header className="banner"
style={{
backgroundSize: "cover",
backgroundImage: `url(
"https://image.tmdb.org/t/p/original/${movie?.backdrop_path}"
)`,
backgroundPosition: "center center",
}}
>
<div className="banner__contents">
<h1 className="banner__title">
{movie?.title || movie?.name || movie?.original_name}
</h1>
<div className="banner__buttons">
<button className="banner__button">Play</button>
<button className="banner__button">My List</button>
</div>
<h1 className="banner__description">
{truncate(movie?.overview, 150)}
</h1>
</div>
<div className="banner--fadeBottom" />
</header>
);
}
export default Banner
그리고 Banner에 css를 적용하기 위해 Banner.css도 만들어준다. 위 Banner.js에는 없지만 css 파일을 반드시 js 파일에 import 해줘야 한다.
[Banner.css]
.banner {
color: white;
object-fit: contain;
height: 448px;
}
.banner__contents {
margin-left: 30px;
padding-top: 140px;
height: 190px;
}
.banner__title {
/* banner 영화 제목에 대한 css */
font-size: 3rem;
font-weight: 800;
padding-bottom: 0.3rem;
}
.banner__description {
width: 45rem;
line-height: 1.3;
padding-top: 1rem;
font-size: 0.8rem;
max-width: 360px;
height: 80px;
}
.banner__button {
cursor: pointer;
color: #fff;
outline: none;
border: none;
font-weight: 700;
border-radius: 0.2vw;
padding-left: 2rem;
padding-right: 2rem;
margin-right: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
background-color: rgba(51, 51, 51, 0.5);
}
.banner__button:hover {
color: #000;
background-color: #e6e6e6;
transition: all 0.2s;
}
.banner--fadeBottom {
/* 배너 하단 페이드 효과 */
height: 7.4rem;
background-image: linear-gradient(
180deg,
transparent,
rgba(37, 37, 37, 0.61),
#111
);
}
배너 작업을 마친 현재의 모습이다. 이제 Nav를 만들어보겠다. App.js 파일에 <Banner /> 위 <Nav />를 추가하고 Nav.js파일과 Nav.css 파일을 만든다. Nav에는 로고, 넷플릭스 아바타와 스크롤 애니메이션을 넣으려고 한다. * 그리고 좀 늦게 얘기하는 거 같은데 js 파일을 만들고 rfce 입력 후 엔터를 누르면 리액트 컴포넌트 구조가 자동으로 완성된다. (확장에서 es7을 설치했기 때문이다.)
[Nav.js]
import React, { useEffect, useState } from 'react';
import './Nav.css';
function Nav() {
const [show, handleShow] = useState(false);
useEffect(() => {
window.addEventListener("scroll", () => {
if (window.scrollY > 100) {
handleShow(true);
} else handleShow(false);
});
return () => {
window.removeEventListener("scroll");
};
}, []);
return (
<div className={`nav ${show && "nav__black"}`}>
<img
className="nav__logo"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Netflix_2015_logo.svg/340px-Netflix_2015_logo.svg.png"
alt="Netflix Logo"
/>
<img
className="nav__avatar"
src="https://upload.wikimedia.org/wikipedia/commons/0/0b/Netflix-avatar.png"
alt="Netflix Logo"
/>
</div>
)
}
export default Nav
[Nav.css]
.nav {
/* position 속성을 이용하면 요소를 겹치게 놓을 수 있다.
이 때 수직 위치를 정할 수 있는 속성이 z-index (값은 정수) */
position: fixed;
top: 0;
width: 100%;
padding: 20px;
height: 30px;
z-index: 1;
/* 애니메이션 */
transition-timing-function: ease-in;
transition: all 0.5s;
}
.nav__black {
background-color: #111;
}
.nav__logo {
position: fixed;
width: 80px;
object-fit: contain;
}
.nav__avatar {
position: fixed;
right: 20px;
width: 30px;
object-fit: contain;
}
'개발.ZIP > Web.ZIP' 카테고리의 다른 글
[#0] 음향기기 쇼핑몰 RE:PLAY 프로젝트 세팅 | React + Typescript + Vite.ZIP (0) | 2024.01.29 |
---|---|
[#4] 넷플릭스 클론코딩.ZIP | 트레일러 삽입, 최종 배포 | With React.js (0) | 2022.01.17 |
[#2] 넷플릭스 클론코딩.ZIP | 넷플릭스 오리지널, 실시간 트렌드 | With React.js (0) | 2022.01.15 |
[#1] 넷플릭스 클론코딩.ZIP | API KEY 발급 | With React.js (0) | 2022.01.15 |
[VSCODE] 네이버 주문하기 클론코딩.ZIP (0) | 2022.01.09 |