따라하며 배우는 리액트 A-Z
[프론트엔드, 웹 개발] 강의입니다.
이 강의를 통해 리액트 기초부터 중급까지 배우게 됩니다. 하나의 강의로 개념도 익히고 실습도 하며, 리액트를 위해 필요한 대부분의 지식을 한번에 습득할 수 있도록 만들었습니다.
✍️
이런 걸
배워요!
리액트
NextJS
타입스크립트
정적 사이트 자동 배포
도커
강의: https://www.inflearn.com/course/%EB%94%B0%EB%9D%BC%ED%95%98%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8#
강의 자료 : https://github.com/braverokmc79/DiagramPDF
소스: https://github.dev/braverokmc79/react-netflix-clone
[4]. Netflix 앱 만들기 시작
32.만들게 될 Netfilx 애플리케이션 소개
강의:

33.Create-React-App으로 리액트 설치하기
강의 :

35.The Movie DB API Key 생성하기
강의 :
https://www.themoviedb.org/documentation/api
API Key 가져오기
https://www.themoviedb.org/settings/api

Get Movie BY Latest
https://api.themoviedb.org/3/movie/latest?api_key=<<api_key>>&language=ko-KR
Get Movie Detail
https://api.themoviedb.org/3/movie/{movie_id}?api_key=<<api_key>>&language=ko-KR
Get Movie Reviews
https://api.themoviedb.org/3/movie/{movie_id}/reviews?api_key=<<api_key>>&language=ko-KR&page=1
Get Trending
https://api.themoviedb.org/3/movie/latest?api_key=<<api_key>>&language=ko-KR
https://image.tmdb.org/t/p/original/wwemzKWzjKYJFfCeiB57q3r4Bcm.svg
https://image.tmdb.org/t/p/original/wwemzKWzjKYJFfCeiB57q3r4Bcm.png
https://image.tmdb.org/t/p/w500/wwemzKWzjKYJFfCeiB57q3r4Bcm.png
36.The Movie DB API 요청을 위한 Axios 인스턴스 생성 및 요청 보내기
강의 :
npm i axios

Get Movie BY Latest
https://api.themoviedb.org/3/movie/latest?api_key=<<api_key>>&language=ko-KR
Get Movie Detail
https://api.themoviedb.org/3/movie/{movie_id}?api_key=<<api_key>>&language=ko-KR
Get Movie Reviews
https://api.themoviedb.org/3/movie/{movie_id}/reviews?api_key=<<api_key>>&language=en-US&page=1
Get Trending
https://api.themoviedb.org/3/movie/latest?api_key=<<api_key>>&language=ko-KR
src/api/axios.js
import axios from 'axios';
const instance = axios.create({
baseURL: "https://api.themoviedb.org/3/",
params: {
api_key: "08d90cc4e7968b1f8e51588a0d42cf06",
language: "ko-KR",
}
});
export default instance;
src/api/requests.js
const requests = {
fetchNowPlaying: "movie/now_playing",
fetchNetflixOriginals: "/discover/tv?with_networks=213",
fetchTrending: "/trending/all/week",
fetchTopRated: "/movie/top_rated",
fetchActionMovies: "/discover/movie?with_genres=28",
fetchComedyMovies: "/discover/movie?with_genres=35",
fetchHorrorMovies: "/discover/movie?with_genres=27",
fetchRomanceMovies: "/discover/movie?with_genres=10749",
fetchDocumentaries: "/discover/movie?with_genres=99",
}
export default requests;
37.넷플릭스 애플리케이션 전체 구조 생성하기
강의 :


39.네비게이션 바 컴포넌트 생성하기
강의 :

40.이미지 배너 생성하기
강의 :

api/components/Nav.js
import React, { useEffect, useState } from 'react';
import './Nav.css';
const Nav = () => {
const [show, setShow] = useState(false);
useEffect(() => {
window.addEventListener("scroll", () => {
console.log("scrollY :", window.scrollY)
if (window.scrollY > 50) {
setShow(true);
} else {
setShow(false);
}
});
return () => {
window.removeEventListener("scroll", () => { });
}
}, []);
return (
<nav className={`nav ${show && "nav__black"}`}>
<img
alt="Netflix logo"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Netflix_2015_logo.svg/170px-Netflix_2015_logo.svg.png"
className='nav__logo'
onClick={() => window.location.reload()}
/>
<img
alt='User logged'
src="https://occ-0-4796-988.1.nflxso.net/dnm/api/v6/K6hjPJd6cR6FpVELC5Pd6ovHRSk/AAAABbme8JMz4rEKFJhtzpOKWFJ_6qX-0y5wwWyYvBhWS0VKFLa289dZ5zvRBggmFVWVPL2AAYE8xevD4jjLZjWumNo.png?r=a41"
className='nav__avatar'
/>
</nav>
);
};
export default Nav;
api/components/Nav.css
.nav{
position: fixed;
top:0;
width: 100%;
height: 30px;
z-index: 1;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
transition-timing-function: ease-in;
transition: all 0.5s;
}
.nav__black{
background-color: #111;
}
.nav__logo{
position: fixed;
left: 40px;
width: 80px;
object-fit: contain;
}
.nav__avatar{
position: fixed;
right:40px;
width:30px;
object-fit: contain;
}
41.이미지 배너 생성하기
강의:
src/components/Banner.js
import React, { useState, useEffect } from 'react';
import axios from '../api/axios';
import requests from '../api/requests';
import './Banner.css';
const Banner = () => {
const [movie, setMovie] = useState([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
//현재 상영중인 영화 정보를 가져오기(여러 영화)
const request = await axios.get(requests.fetchNowPlaying);
//여러 영화 중 영화 하나의 ID 를 가져오기
const movieId =
request.data.results[Math.floor(Math.random() * request.data.results.length)].id;
//특정 영화의 더 상세한 정보를 가져오기 (비디오 정보도 포함)
const { data: movieDetail } = await axios.get(`/movie/${movieId}`,
{
params: { append_to_response: "videos" }
}
);
setTimeout(() => {
setMovie(movieDetail);
}, 100);
console.log("movie3 : ", movie);
};
const truncate = (str, n) => {
return str?.length > n ? str.substr(0, n - 1) + "..." : str;
}
return (
<header
className="banner"
style={{
backgroundImage: `url("https://image.tmdb.org/t/p/original/${movie.backdrop_path}")`,
backgroundPosition: "top center",
backgroundSize: "cover",
}}
>
<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">
Play
</button>
<button className="banner__button info">More Information</button>
</div>
<h1 className="banner__description">
{truncate(movie.overview, 100)}
</h1>
</div>
<div className="banner--fadeBottom" />
</header>
);
};
export default Banner;
src/components/Banner.css
.banner {
color: white;
object-fit: contain;
height: 448px;
}
.banner__contents {
margin-left: 40px;
padding-top: 140px;
height: 190px;
}
.banner__title {
font-size: 3rem;
font-weight: 800;
padding-bottom: 0.5rem;
}
.banner__description {
width: 45rem;
line-height: 1.3;
padding-top: 1rem;
font-weight: 500;
font-size: 1rem;
max-width: 400px;
height: 80px;
}
.banner--fadeBottom {
height: 7.4rem;
background-image: linear-gradient(
180deg,
transparent,
rgba(37, 37, 37, 0.61),
#111
);
}
.banner__buttons {
display: flex;
flex-direction: row;
}
.banner__button {
display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
cursor: pointer;
outline: none;
border: none;
font-size: 1rem;
font-weight: 700;
border-radius: 0.2vw;
padding: 0.4rem 1.8rem 0.4rem 1rem;
margin-right: 1rem;
}
.banner__button:hover {
color: #000;
background-color: rgba(170, 170, 170, 0.9);
transition: all 0.2s;
}
.play {
background-color: white;
color: black;
}
.info {
background-color: rgba(109, 109, 110, 0.7);
color: white;
}
.info:hover {
background-color: rgb(74, 74, 74);
color: white;
}
.space {
margin-left: 4px;
}
@media (min-width: 1500px) {
.banner {
position: relative;
height: 600px;
}
.banner--fadeBottom {
position: absolute;
bottom: 0;
width: 100%;
height: 40rem;
}
}
@media (max-width: 768px) {
.banner__contents {
width: min-content !important;
padding-left: 2.3rem;
margin-left: 0px !important;
}
.banner__description {
font-size: 0.8rem !important;
width: auto !important;
}
.info {
text-align: start;
padding-right: 1.2rem;
}
.space {
margin-left: 6px;
}
.banner__button {
font-size: 0.8rem !important;
border-radius: 4px !important;
}
}
@media (max-width: 560px) {
.banner__contents {
width: 80% !important;
padding-left: 2.3rem;
margin-left: 0px !important;
padding-top: 40px;
}
.banner__description {
font-size: 0.8rem !important;
width: auto !important;
}
.info {
text-align: start;
padding-right: 1.2rem;
}
.space {
margin-left: 6px;
}
.banner__buttons{
justify-content: space-evenly;
}
.banner__button {
font-size: 0.8rem !important;
border-radius: 4px !important;
}
.banner--fadeBottom {
padding-bottom: 70px !important;
}
}
42.Styled Component에 대해서
강의:

43.Styled Component를 이용한 비디오 배너 생성하기
강의 :

src/components/Banner.js
import React, { useState, useEffect } from 'react';
import axios from '../api/axios';
import axiosEn from '../api/axiosEn';
import requests from '../api/requests';
import './Banner.css';
import styled from 'styled-components';
const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 100vh;
background:black;
`;
const HomeContainer = styled.div`
width: 100%;
height: 100%;
`;
const Iframe = styled.iframe`
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.65;
border: none;
&::after{
content:"" ;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
`;
const Banner = () => {
const [movie, setMovie] = useState([]);
const [movieKey, setMovieKey] = useState("");
const [isClicked, setIsClicked] = useState(false);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
//현재 상영중인 영화 정보를 가져오기(여러 영화)
const request = await axios.get(requests.fetchNowPlaying);
const results = request.data.results.filter(data => {
return data.overview.length > 1
})
console.log(" results", results);
//여러 영화 중 영화 하나의 ID 를 가져오기
const movieId = results[Math.floor(Math.random() * results.length)].id;
//특정 영화의 더 상세한 정보를 가져오기 (비디오 정보도 포함)
const { data: movieDetail } = await axios.get(`movie/${movieId}`, {
params: { append_to_response: "videos" },
});
setMovie(movieDetail);
//비디오가 없다면 다음을 실행
if (movie.videos === undefined) {
const { data: movieDetailEn } = await axiosEn.get(`movie/${movieId}`, {
params: { append_to_response: "videos" },
});
setMovieKey(movieDetailEn.videos.results[0].key);
} else {
setMovieKey(movie.videos.results[0].key)
}
};
const truncate = (str, n) => {
return str?.length > n ? str.substr(0, n - 1) + "..." : str;
}
if (!isClicked) {
return (
movie.backdrop_path && <header
className="banner"
style={{
backgroundImage: `url("https://image.tmdb.org/t/p/original/${movie.backdrop_path}")`,
backgroundPosition: "top center",
backgroundSize: "cover",
}}
>
<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" onClick={() => setIsClicked(true)}>
Play
</button>
<button className="banner__button info">More Information</button>
</div>
<h1 className="banner__description">
{truncate(movie.overview, 100)}
</h1>
</div>
<div className="banner--fadeBottom" />
</header>
);
} else {
return (
<Container >
<HomeContainer>
<Iframe
width="640"
height="360"
src={`https://www.youtube.com/embed/${movieKey}?controls=0&autoplay=1&loop=1&mute=1&playlist=${movieKey}`}
title="YouTube video player"
frameborder="0"
allow="autoplay; fullscreen"
allowfullscreen
></Iframe>
</HomeContainer>
</Container >
);
}
};
export default Banner;














댓글 ( 4)
댓글 남기기