따라하며 배우는 리액트 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)
댓글 남기기