- 이글은 Netflix Modal창을 참고했던 프로젝트를 분석한 글입니다.
.
├── public
└── src
├── components
├── constants
├── containers
├── context
├── fixtures
├── helpers
├── hooks
├── lib
├── pages
├── service
└── utils
기본 폴더구조 입니다. 지난번 프로젝트 분석과 같은 구조로 파일 내용들만 바뀌었습니다.
index.js 파일 구성
import React from 'react';
import { render } from 'react-dom';
import 'normalize.css'
import App from './app';
import { GlobalStyles } from './global-styles'
import { firebase } from './lib/firebase.prod'
import {FirebaseContext} from './context/firebase'
render(
<>
<FirebaseContext.Provider value={{ firebase }}>
<GlobalStyles />
<App />
</FirebaseContext.Provider>
</>, document.getElementById('root'));
동일하게 render로 구성되었습니다.
import Firebase from 'firebase/app'
import 'firebase/firestore'
import 'firebase/auth'
// import { seedDatabase } from '../seed'
//config
const config = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain:process.env.REACT_APP_AUTHDOMAIN_API_KEY,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGE_BUKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
measurementId: process.env.REACT_APP_MEASUREMENTID
}
const firebase = Firebase.initializeApp(config)
// seedDatabase(firebase)
export { firebase }
firebase의 내용들도 동일하게 구성되었습니다. app.js 에 내용들도 저버과 동일하기 때문에 다음으로 넘어가겠습니다.
import { BrowserRouter as Router, Switch } from 'react-router-dom'
import * as ROUTES from './constants/routes'
import { Home, Browse, Signin, Signup } from './pages/'
import { IsUserRedirect, ProtectedRoute } from './helpers/routes';
import { useAuthListener } from './hooks';
export default function app() {
const {user} = useAuthListener()
return (
<Router>
<Switch>
<IsUserRedirect
user={user}
loggedInPath={ROUTES.BROWSE}
path={ROUTES.SIGN_IN}
>
<Signin />
</IsUserRedirect>
<IsUserRedirect
user={user}
loggedInPath={ROUTES.BROWSE}
path={ROUTES.SIGN_UP}
>
<Signup />
</IsUserRedirect>
<ProtectedRoute
user={user}
path={ROUTES.BROWSE}
>
<Browse />
</ProtectedRoute>
<IsUserRedirect
user={user}
loggedInPath={ROUTES.BROWSE}
path={ROUTES.HOME}
exact
>
<Home />
</IsUserRedirect>
</Switch>
</Router>
);
}
app.js 도 저번과 동일합니다. Home에 exact를 붙이고 맨밑에서부터 우선권을 줍니다. ProtectedRoute는 조건이 만족할때 갈수있는 Route항목이기때문에 유저로 인식되지 않을때 Home에서 바로 Browse로 넘어가는게 아닌 첫번째 항목인 Signin 항목으로 넘어갑니다.
import { useFetchContent } from "../hooks"
import selectionFilter from "../utils/selection-filter"
import { BrowseContainer } from "../containers/browse"
import requests from "../service/requests"
export default function Browse() {
// const { series } = useContent('series')
// const { films } = useContent('films')
const trending = useFetchContent(requests.fetchTrending)
const netflixOriginals = useFetchContent(requests.fetchNetflixOriginals)
const actionAndAdventureSeries = useFetchContent(requests.fetchActionAndAdventureSeries)
const animationSeries = useFetchContent(requests.fetchAnimationSeries)
const comedySeries = useFetchContent(requests.fetchComedySeries)
const crimeSeries = useFetchContent(requests.fetchCrimeSeries)
const documentarySeries = useFetchContent(requests.fetchDocumentarySeries)
const dramaSeries = useFetchContent(requests.fetchDramaSeries)
const familySeries = useFetchContent(requests.fetchFamilySeries)
const kidsSeries = useFetchContent(requests.fetchKidsSeries)
const mysterySeries = useFetchContent(requests.fetchMysterySeries)
const newsSeries = useFetchContent(requests.fetchNewsSeries)
const realitySeries = useFetchContent(requests.fetchRealitySeries)
const scifiSeries = useFetchContent(requests.fetchScifiSeries)
const soapSeries = useFetchContent(requests.fetchSoapSeries)
const talkSeries = useFetchContent(requests.fetchTalkSeries)
const warAndPoliticsSeries = useFetchContent(requests.fetchWarAndPoliticsSeries)
const westernSeries = useFetchContent(requests.fetchWesternSeries)
const topRatedMovies = useFetchContent(requests.fetchTopRated)
const actionMovies = useFetchContent(requests.fetchActionMovies)
const adventureMovies = useFetchContent(requests.fetchAdventureMovies)
const animationMovies = useFetchContent(requests.fetchAnimationMovies)
const comedyMovies = useFetchContent(requests.fetchComedyMovies)
const crimeMovies = useFetchContent(requests.fetchComedyMovies)
const documentaries = useFetchContent(requests.fetchDocumentaries)
const dramaMovies = useFetchContent(requests.fetchDramaMovies)
const familyMovies = useFetchContent(requests.fetchFamilyMovies)
const fantasyMovies = useFetchContent(requests.fetchFantasyMovies)
const historyMovies = useFetchContent(requests.fetchHistoryMovies)
const horrorMovies = useFetchContent(requests.fetchHorrorMovies)
const musicMovies = useFetchContent(requests.fetchMusicMovies)
const mysteryMovies = useFetchContent(requests.fetchMysteryMovies)
const romanceMovies = useFetchContent(requests.fetchRomanceMovies)
const scifiMovies = useFetchContent(requests.fetchScifiMovies)
const tvmovieMovies = useFetchContent(requests.fetchTvMovies)
const thrillerMovies = useFetchContent(requests.fetchThrillerMovies)
const warMovies = useFetchContent(requests.fetchWarMovies)
const westernMovies = useFetchContent(requests.fetchWesternMovies)
const slides = selectionFilter({
trending,
netflixOriginals,
actionAndAdventureSeries,
animationSeries,
comedySeries,
crimeSeries,
documentarySeries,
dramaSeries,
familySeries,
kidsSeries,
mysterySeries,
newsSeries,
realitySeries,
scifiSeries,
soapSeries,
talkSeries,
warAndPoliticsSeries,
westernSeries,
topRatedMovies,
actionMovies,
adventureMovies,
animationMovies,
comedyMovies,
crimeMovies,
documentaries,
dramaMovies,
familyMovies,
fantasyMovies,
historyMovies,
horrorMovies,
musicMovies,
mysteryMovies,
romanceMovies,
scifiMovies,
tvmovieMovies,
thrillerMovies,
warMovies,
westernMovies
})
return <BrowseContainer slides={slides} />
}
로그인 완료후 browse 컴포넌트로 넘어갑니다. 저번 코드와 다르게 request를 사용해서 영화 항목들을 함수로 선언해서 불러오고 있습니다.
export const API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
const requests = {
fetchTrending: `/trending/all/week?api_key=${API_KEY}&language=en-US`,
fetchNetflixOriginals: `/discover/tv?api_key=${API_KEY}&with_networks=213`,
fetchActionAndAdventureSeries: `/discover/tv?api_key=${API_KEY}&with_genres=10759`,
fetchAnimationSeries: `/discover/tv?api_key=${API_KEY}&with_genres=16`,
fetchComedySeries: `/discover/tv?api_key=${API_KEY}&with_genres=35`,
fetchCrimeSeries: `/discover/tv?api_key=${API_KEY}&with_genres=80`,
fetchDocumentarySeries: `/discover/tv?api_key=${API_KEY}&with_genres=99`,
fetchDramaSeries: `/discover/tv?api_key=${API_KEY}&with_genres=18`,
fetchFamilySeries: `/discover/tv?api_key=${API_KEY}&with_genres=10751`,
fetchKidsSeries: `/discover/tv?api_key=${API_KEY}&with_genres=10762`,
fetchMysterySeries: `/discover/tv?api_key=${API_KEY}&with_genres=9648`,
fetchNewsSeries: `/discover/tv?api_key=${API_KEY}&with_genres=10763`,
fetchRealitySeries: `/discover/tv?api_key=${API_KEY}&with_genres=10764`,
fetchScifiSeries: `/discover/tv?api_key=${API_KEY}&with_genres=10765`,
fetchSoapSeries: `/discover/tv?api_key=${API_KEY}&with_genres=10766`,
fetchTalkSeries: `/discover/tv?api_key=${API_KEY}&with_genres=10767`,
fetchWarAndPoliticsSeries: `/discover/tv?api_key=${API_KEY}&with_genres=10768`,
fetchWesternSeries: `/discover/tv?api_key=${API_KEY}&with_genres=37`,
fetchTopRated: `/movie/top_rated?api_key=${API_KEY}&language=en-US`,
fetchActionMovies: `/discover/movie?api_key=${API_KEY}&with_genres=28`,
fetchAdventureMovies: `/discover/movie?api_key=${API_KEY}&with_genres=12`,
fetchAnimationMovies: `/discover/movie?api_key=${API_KEY}&with_genres=16`,
fetchComedyMovies: `/discover/movie?api_key=${API_KEY}&with_genres=35`,
fetchCrimeMovies: `/discover/movie?api_key=${API_KEY}&with_genres=80`,
fetchDocumentaries: `/discover/movie?api_key=${API_KEY}&with_genres=99`,
fetchDramaMovies: `/discover/movie?api_key=${API_KEY}&with_genres=18`,
fetchFamilyMovies: `/discover/movie?api_key=${API_KEY}&with_genres=10751`,
fetchFantasyMovies: `/discover/movie?api_key=${API_KEY}&with_genres=14`,
fetchHistoryMovies: `/discover/movie?api_key=${API_KEY}&with_genres=36`,
fetchHorrorMovies: `/discover/movie?api_key=${API_KEY}&with_genres=27`,
fetchMusicMovies: `/discover/movie?api_key=${API_KEY}&with_genres=10402`,
fetchMysteryMovies: `/discover/movie?api_key=${API_KEY}&with_genres=9648`,
fetchRomanceMovies: `/discover/movie?api_key=${API_KEY}&with_genres=10749`,
fetchScifiMovies: `/discover/movie?api_key=${API_KEY}&with_genres=878`,
fetchTvMovies: `/discover/movie?api_key=${API_KEY}&with_genres=10770`,
fetchThrillerMovies: `/discover/movie?api_key=${API_KEY}&with_genres=53`,
fetchWarMovies: `/discover/movie?api_key=${API_KEY}&with_genres=10752`,
fetchWesternMovies: `/discover/movie?api_key=${API_KEY}&with_genres=37`,
}
export default requests
영화 장르를 불러올 fetch requests 항목들입니다. 지난 번과 다르게 각각 장르들을 나눠서 requests로 불러오고 있습니다. requests로 선언된 순간
export default function selectionFilter({
trending,
netflixOriginals,
actionAndAdventureSeries,
animationSeries,
comedySeries,
crimeSeries,
documentarySeries,
dramaSeries,
familySeries,
kidsSeries,
mysterySeries,
newsSeries,
realitySeries,
scifiSeries,
soapSeries,
talkSeries,
warAndPoliticsSeries,
westernSeries,
topRatedMovies,
actionMovies,
adventureMovies,
animationMovies,
comedyMovies,
crimeMovies,
documentaries,
dramaMovies,
familyMovies,
fantasyMovies,
historyMovies,
horrorMovies,
musicMovies,
mysteryMovies,
romanceMovies,
scifiMovies,
tvmovieMovies,
thrillerMovies,
warMovies,
westernMovies
}) {
return {
series: [
{
title: 'Trending Now',
data: trending.slice(1, 10)
},
{
title: 'Netflix Originals',
data: netflixOriginals.slice(1, 10)
},
{
title: 'Action & Adventure',
data: actionAndAdventureSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Animation',
data: animationSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Comedy',
data: comedySeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Crime',
data: crimeSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Documentary',
data: documentarySeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Drama',
data: dramaSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Family',
data: familySeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Kids',
data: kidsSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Mystery',
data: mysterySeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'News',
data: newsSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Reality',
data: realitySeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Science Fiction',
data: scifiSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Soap',
data: soapSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Talk',
data: talkSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'War And Politics',
data: warAndPoliticsSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Western',
data: westernSeries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
],
films: [
{
title: 'Top Rated',
data: topRatedMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Action',
data: actionMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Adventure',
data: adventureMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Animation',
data: animationMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Comedy',
data: comedyMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Crime',
data: crimeMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Documentaries',
data: documentaries.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Drama',
data: dramaMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Family',
data: familyMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Fantasy',
data: fantasyMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'History',
data: historyMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Horror',
data: horrorMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Music',
data: musicMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Mystery',
data: mysteryMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Romance',
data: romanceMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Science Fiction',
data: scifiMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'TV Movie',
data: tvmovieMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Thrilller',
data: thrillerMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'War',
data: warMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
{
title: 'Western',
data: westernMovies.sort(() => Math.random() - Math.random()).slice(1, 10)
},
],
}
}
selection-filter를 통해 장르들의 title 과 data에 requests로 선언한 api들을 명시해주고 sort를 동해서 표시할 영화 개수들을 slice로 (1열 10개 영화) 순으로 나열해줍니다.
import { SelectProfileContainer } from "./profiles"
import { FooterContainer } from "./footer"
import { FirebaseContext } from '../context/firebase'
import { useContext, useEffect, useState } from "react"
import { Card, Loading, Header, Player } from '../components'
import requests from "../service/requests"
import axios from '../service/axios'
import * as ROUTES from '../constants/routes';
import logo from '../logo.svg';
import * as SOURCE from "../constants/source"
import Fuse from 'fuse.js'
export function BrowseContainer({ slides }) {
const [category, setCategory] = useState('series')
const [searchTerm, setSearchTerm] = useState('')
const [background, setBackGround] = useState([])
const [profile, setProfile] = useState({})
const [loading, setLoading] = useState(true)
const [slideRows, setSlideRows] = useState([])
// const [trailerUrl,setTrailerUrl]=useState('')
const { firebase } = useContext(FirebaseContext)
const user = firebase.auth().currentUser || {}
useEffect(() => {
setTimeout(() => {
setLoading(false)
}, 3000);
}, [profile.displayName])
useEffect(() => {
setSlideRows(slides[category])
}, [slides, category])
useEffect(() => {
async function fetchData() {
const request = await axios.get(requests.fetchNetflixOriginals);
setBackGround(
request.data.results[
Math.floor(Math.random() * request.data.results.length - 1)
]
);
return request;
}
fetchData();
}, []);
useEffect(() => {
const fuse = new Fuse(slideRows, {
keys: ['data.description', 'data.title', 'data.genre']
})
const results = fuse.search(searchTerm).map(({ item }) => item)
if (slideRows.length > 0 && searchTerm.length > 3 && results.length > 0) {
setSlideRows(results)
}
else {
setSlideRows(slides[category])
}
}, [searchTerm])
return profile.displayName ? (
<>
{loading ? <Loading src={user.photoURL} /> : <Loading.ReleaseBody />}
<Header src={background.backdrop_path !== undefined ? `${SOURCE.BASE_IMG_URL}${background.backdrop_path}` : '/images/misc/home-bg.jpg'} dontShowOnSmallViewPort>
<Header.Frame>
<Header.Group>
<Header.Logo to={ROUTES.HOME} src={logo} alt="Netflix" />
<Header.TextLink
active={category === 'series' ? 'true' : 'false'}
onClick={() => setCategory('series')}
>
Series
</Header.TextLink>
<Header.TextLink
active={category === 'films' ? 'true' : 'false'}
onClick={() => setCategory('films')}
>
Movies
</Header.TextLink>
</Header.Group>
<Header.Group>
<Header.Search searchTerm={searchTerm} setSearchTerm={setSearchTerm} />
<Header.Profile>
<Header.Picture src={user.photoURL} />
<Header.Dropdown>
<Header.Group>
<Header.Picture src={user.photoURL} />
<Header.TextLink>{user.displayName}</Header.TextLink>
</Header.Group>
<Header.Group>
<Header.TextLink onClick={() => firebase.auth().signOut()}>Sign Out</Header.TextLink>
</Header.Group>
</Header.Dropdown>
</Header.Profile>
</Header.Group>
</Header.Frame>
<Header.Feature>
<Header.FeatureCallOut>Watch {background.title || background.name} Now</Header.FeatureCallOut>
<Header.Text>
{background.overview}
</Header.Text>
<Header.ButtonLayout>
<Header.Button>Play</Header.Button>
<Header.Button>My List</Header.Button>
</Header.ButtonLayout>
</Header.Feature>
</Header>
<Card.Group>
{slideRows.map((slideItem) => (
<Card key={`${category}-${slideItem.title.toLowerCase()}`}>
<Card.Title>{slideItem.title}</Card.Title>
<Card.Entities>
{slideItem.data.map((item) => (
<Card.Item key={item.docId || item.id} item={item}>
<Card.Image src={`${SOURCE.BASE_IMG_URL}${item.poster_path}` || `/images/${category}/${item.genre}/${item.slug}/small.jpg`} />
<Card.Meta>
<Card.SubTitle>{item.title || item.name}</Card.SubTitle>
<Card.Text>{item.description || item.overview}</Card.Text>
</Card.Meta>
</Card.Item>
))}
</Card.Entities>
<Card.Feature category={category}>
<Player>
<Player.Button />
<Player.Video />
</Player>
</Card.Feature>
</Card>
))}
</Card.Group>
<FooterContainer />
</>
) : (<SelectProfileContainer user={user} setProfile={setProfile} />)
}
containers에 browse에서 선언한 requests들을 불러와주고 tmdb의 기본 주소를 받아오는 axios도 가져옵니다.
useEffect(() => {
async function fetchData() {
const request = await axios.get(requests.fetchNetflixOriginals);
setBackGround(
request.data.results[
Math.floor(Math.random() * request.data.results.length - 1)
]
);
return request;
}
fetchData();
}, []);
fetchData로 제일 먼저 오는 requests를 선언해줍니다. 로그인 후 browse에 처음부터 나열할 항목은 넷플릭스 오리지널 영화이므로 get방식으로
fetchNetflixOriginals를 불러와 주었습니다. setBackGround는 영화의 이미지를 나타낼 함수 입니다.
const [background, setBackGround] = useState([])
영화 이미지는 배열로 받아오기 때문에 []를 명시합니다.
const [category, setCategory] = useState('series')
Navigation을 구성하기 위해서는 영화 / 드라마 로 나누어줄 category가 필요합니다. useState로 접속시 시리즈 항목을 먼저 불러오게합니다.
{slideRows.map((slideItem) => (
<Card key={`${category}-${slideItem.title.toLowerCase()}`}>
<Card.Title>{slideItem.title}</Card.Title>
<Card.Entities>
{slideItem.data.map((item) => (
<Card.Item key={item.docId || item.id} item={item}>
<Card.Image src={`${SOURCE.BASE_IMG_URL}${item.poster_path}`
|| `/images/${category}/${item.genre}/${item.slug}/small.jpg`} />
<Card.Meta>
<Card.SubTitle>{item.title || item.name}</Card.SubTitle>
<Card.Text>{item.description || item.overview}</Card.Text>
</Card.Meta>
</Card.Item>
))}
영화 slide 항목을 map함수로 돌려서 항목들을 나열해줍니다. 배열 안에 각각 Item들과 타이틀 서브 타이들 들을 명시합니다.
const [category, setCategory] = useState('series')
<Header.TextLink
active={category === 'series' ? 'true' : 'false'}
onClick={() => setCategory('series')}
>
Series
</Header.TextLink>
<Header.TextLink
active={category === 'films' ? 'true' : 'false'}
onClick={() => setCategory('films')}
>
Movies
</Header.TextLink>
slideItem배열에 category로 영화 목록을 나열했기 떄문에 category는 영화 드라마라는 장르를 갖고 있습니다.
카테고리에 true false로 대입해서 true는 현재 값이고 만약 다른 값으로 이동할때 false로 비활성화 되도록 만들었습니다.
이러면 Series에서 Movies로 이동시 series에 category 값은 false가 되고 films의 값이 true로 바뀌면서 Movies 항목으로 이동하게 됩니다.
Series로 이동시 해당되는 filter로 바뀌는 모습을 볼수 있습니다.
<Card.Feature category={category}>
<Player>
<Player.Button />
<Player.Video />
</Player>
</Card.Feature>
다음은 영화 목록 클릭시 나오는 Modal 부분입니다. Feature안에 category 를 명시해서 아까 배열로 돌린 영화 목록들의 정보를 나타낼 구간입니다.
export const FeatureContext = createContext()
Card.Feature = function CardFeature({ children, category, ...restProps }) {
const { showFeature, setShowFeature, itemFeature } = useContext(FeatureContext)
const is_MediaType_Undedfined = itemFeature.media_type === undefined
const mediaType=is_MediaType_Undedfined ? category === 'series' ? 'tv' : 'movie' : itemFeature.media_type
showFeature ? itemFeature.rating = useFetchRating(itemFeature.id, mediaType) : null
return showFeature ? (
<Feature {...restProps} src={`${SOURCE.BASE_IMG_URL}${itemFeature.backdrop_path}`}>
<Content>
<FeatureTitle>{itemFeature.title || itemFeature.name}</FeatureTitle>
<FeatureText>{itemFeature.description || itemFeature.overview}</FeatureText>
<FeatureClose onClick={() => setShowFeature(false)}>
<img src="/images/icons/close.png" alt="close" />
</FeatureClose>
<Group margin="30px 0" flexDirection="row" alignItems="center">
<Rating vote_average={itemFeature.vote_average}>
{itemFeature.vote_average}
</Rating>
<Maturity rating={itemFeature.rating}>
{itemFeature.rating!==undefined? itemFeature.rating : "NR"}
</Maturity>
{itemFeature.genre_ids.map((id) => (
<FeatureCategory key={id} fontWeight="bold">
{mediaType === "tv" ?
tvGenres.filter((genre) => genre.id === id).map((item) => (
item.name
))
: movieGenres.filter((genre) => genre.id === id).map((item) => (
item.name
))}
</FeatureCategory>
))}
</Group>
{children}
</Content>
</Feature>
) : null
}
ContextAPI를 사용해서 FeatureContext에 createContext로 데이터를 전달해줍니다.
Feature안에 showFeature , itemFeature 값들을 useContext에 담아줍니다. media_type 영화 드라마 항목에 맞지않는 것들은 undefined를 반환 해줍니다. undefined 가 아니라면 tv movie 카테고리안에 아이템 정보들을 불러와줍니다.
Modal안에 명시할 영화 드라마 제목 설명 평점 등 정보들을 itemFeature함수로 선언해주었습니다.
FeatureClose 컴포넌트는 x 버튼을 말하고 클릭시 modal이 닫히게 선언해주었습니다. setShowFeature변수를 false로 만들어 Modal 상태를
on 에서 off 로 변환해서 닫힘상태로 만들어주었습니다.
close 아이콘을 클릭하면 modal 창은 닫히게 됩니다.
'포트폴리오 분석 > React' 카테고리의 다른 글
React 포트폴리오 참고 분석 (1) - Netflix (Karl Hadwen) (0) | 2021.09.16 |
---|