노마드 코더님 영화 사이트 클론코딩
노마드 코더님의 강좌를 보고 제작하였습니다.(+ 몇가지는 제가 기능적으로 추가함)
해당 자료를 클론코딩했으며 이후 한국영화 서비스로 만들려고함.
Reactjs로 영화 웹 서비스 만들기 클론 코딩
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Test</h1>
</header>
{movies.map( movie=> {
return <Movie title={movie.title} poster={movie.poster} ></Movie>
})}
{/* 위와 같은 문법은 다음과같다.
<Movie title={moives[0].title} poster={moives[0].poster}/> 0~2까지 배열길이 만큼 돌려줌 이터레이터와 비슷한 개념일듯 이와 같은문법임
*/}
</div>
);
}
}
이렇게 할경우
VM1202 bundle.js:25527 Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of `App`. See https://fb.me/react-warning-keys for more information.
in Movie (at App.js:47)
in App (at index.js:7)
React에는 같은 자식에게 어레이를 넘겨줄때 유니크한 '키'를 줘야하나봐 그래서 키를 만들어야할듯
그래서 데이터에 id를 추가함.
const movies =[
{
id: 1,
title : "m1",
} ,
{
id: 2,
title : "m2",
} ,
{
id: 3,
title : "m3",
}
]
와 같이해도되지만
Map에 index가 포함되어잇으니 그거 사용함
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
{movies.map( (movie , index) => {
return <Movie title={movie.title} poster={movie.poster} key={index} ></Movie>
})}
타입같은걸 정해줄려면
React.PropTypes.[타입]
근데 패키지가 없다.
설치
static propTypes ={
title : PropTypes.string.isRequired,
poster : PropTypes.string.isRequired,
}
Title과 poster는 필수값으로 지정.
componentDidMount(){
setTimeout( ()=> {
this.setState({
greeting : "hi",
movies : [
...this.state.movies, -> 기존리스트에 덧대어서
{
title : "m4" ,
}
]
}
)
}, 5000)
}
메인 페이지에 영화를 추가하거나 업데이트할때 이런식으로 하면될꺼같다.
로딩을구현 5초후에 setTimeout으로 setstate가 발생되고 moives라는 배열이 추가된다.
추가되었다면 {this.state.movies ? this._renderMovies() : 'Loding'} 로 되어있는 부분에
renderMoives가 호출되니깐 리스트가 뜬다.
componentDidMount(){
setTimeout( ()=> {
this.setState({
greeting : "hi",
movies : [
//...this.state.movies,
{
title : "m1",
} ,
{
title : "m2",
} ,
{
title : "m3",
}
]
}
)
}, 5000)
}
_renderMovies = () => {
const movies = this.state.movies.map( (movie , index) => {
return <Movie title={movie.title} poster={movie.poster} key={index} ></Movie>
})
return movies
}
render() {
console.log("render");
return (
<div className="App">
<header className="App-header">
<h1 className="App-title"> {this.state.greeting} </h1>
</header>
{this.state.movies ? this._renderMovies() : 'Loding'}
{/* 위와 같은 문법은 다음과같다.
<Movie title={moives[0].title} poster={moives[0].poster}/> 0~2까지 배열길이 만큼 돌려줌 이터레이터와 비슷한 개념일듯 이와 같은문법임
*/}
</div>
);
이전처럼
Render에서 직접적으로 접근하지않는이유는
언제 해당데이터가 들어올지 모르기때문에 그런거
Return 만 하기위해 존재한느 컴포넌트들은 function 컴포넌트로 교체 될수있다.
아래 컴포너트는 실제로 poster를 받아서 랜더링하고 그것만 리턴해준다
이 컴포넌트는 state가 필요없기에
Function으로 제작되어도 무관함.
function MoivePoster({poster}){
return (
<img src={poster} alt="poster"/>
)
}
class MoviePoster extends Component{
render(){
return(
<img src={this.props.poster}/>
)
}
}
로 변할수있음 다음과같이 바꿔볼수있음
function Movie( {title, poster}){
return(
<div>
<h1>{title}</h1>
<MoviePoster poster={poster}></MoviePoster>
</div>
)
}
function MoviePoster ({poster}) {
return(
<img src={poster} alt="poster" />
)
}
MoviePoster.propsTypes = {
poster : PropTypes.string.isRequired,
}
Movie.propsTypes = {
title : PropTypes.string.isRequired ,
poster : PropTypes.string.isRequired ,
}
export default Movie;
Ajax fatch
fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')
console.log( fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating'))
Promise? 비동기+ 시나리오 라고하는데 잘모르겠다.
fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')
.then(data => console.log(data))
.catch(err => console.log(err))
Fetch url
Then 데이터 결과
Catch 에러처리
완성본
노마드 코더님의 강좌를 보고 제작하였습니다.(+ 몇가지는 제가 기능적으로 추가함)
해당 자료를 클론코딩했으며 이후 한국영화 서비스로 만들려고함.
Reactjs로 영화 웹 서비스 만들기 클론 코딩
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Test</h1>
</header>
{movies.map( movie=> {
return <Movie title={movie.title} poster={movie.poster} ></Movie>
})}
{/* 위와 같은 문법은 다음과같다.
<Movie title={moives[0].title} poster={moives[0].poster}/> 0~2까지 배열길이 만큼 돌려줌 이터레이터와 비슷한 개념일듯 이와 같은문법임
*/}
</div>
);
}
}
이렇게 할경우
VM1202 bundle.js:25527 Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of `App`. See https://fb.me/react-warning-keys for more information.
in Movie (at App.js:47)
in App (at index.js:7)
React에는 같은 자식에게 어레이를 넘겨줄때 유니크한 '키'를 줘야하나봐 그래서 키를 만들어야할듯
그래서 데이터에 id를 추가함.
const movies =[
{
id: 1,
title : "m1",
} ,
{
id: 2,
title : "m2",
} ,
{
id: 3,
title : "m3",
}
]
와 같이해도되지만
Map에 index가 포함되어잇으니 그거 사용함
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
{movies.map( (movie , index) => {
return <Movie title={movie.title} poster={movie.poster} key={index} ></Movie>
})}
타입같은걸 정해줄려면
React.PropTypes.[타입]
근데 패키지가 없다.
설치
static propTypes ={
title : PropTypes.string.isRequired,
poster : PropTypes.string.isRequired,
}
Title과 poster는 필수값으로 지정.
componentDidMount(){
setTimeout( ()=> {
this.setState({
greeting : "hi",
movies : [
...this.state.movies, -> 기존리스트에 덧대어서
{
title : "m4" ,
}
]
}
)
}, 5000)
}
메인 페이지에 영화를 추가하거나 업데이트할때 이런식으로 하면될꺼같다.
로딩을구현 5초후에 setTimeout으로 setstate가 발생되고 moives라는 배열이 추가된다.
추가되었다면 {this.state.movies ? this._renderMovies() : 'Loding'} 로 되어있는 부분에
renderMoives가 호출되니깐 리스트가 뜬다.
componentDidMount(){
setTimeout( ()=> {
this.setState({
greeting : "hi",
movies : [
//...this.state.movies,
{
title : "m1",
} ,
{
title : "m2",
} ,
{
title : "m3",
}
]
}
)
}, 5000)
}
_renderMovies = () => {
const movies = this.state.movies.map( (movie , index) => {
return <Movie title={movie.title} poster={movie.poster} key={index} ></Movie>
})
return movies
}
render() {
console.log("render");
return (
<div className="App">
<header className="App-header">
<h1 className="App-title"> {this.state.greeting} </h1>
</header>
{this.state.movies ? this._renderMovies() : 'Loding'}
{/* 위와 같은 문법은 다음과같다.
<Movie title={moives[0].title} poster={moives[0].poster}/> 0~2까지 배열길이 만큼 돌려줌 이터레이터와 비슷한 개념일듯 이와 같은문법임
*/}
</div>
);
이전처럼
Render에서 직접적으로 접근하지않는이유는
언제 해당데이터가 들어올지 모르기때문에 그런거
Return 만 하기위해 존재한느 컴포넌트들은 function 컴포넌트로 교체 될수있다.
아래 컴포너트는 실제로 poster를 받아서 랜더링하고 그것만 리턴해준다
이 컴포넌트는 state가 필요없기에
Function으로 제작되어도 무관함.
function MoivePoster({poster}){
return (
<img src={poster} alt="poster"/>
)
}
class MoviePoster extends Component{
render(){
return(
<img src={this.props.poster}/>
)
}
}
로 변할수있음 다음과같이 바꿔볼수있음
function Movie( {title, poster}){
return(
<div>
<h1>{title}</h1>
<MoviePoster poster={poster}></MoviePoster>
</div>
)
}
function MoviePoster ({poster}) {
return(
<img src={poster} alt="poster" />
)
}
MoviePoster.propsTypes = {
poster : PropTypes.string.isRequired,
}
Movie.propsTypes = {
title : PropTypes.string.isRequired ,
poster : PropTypes.string.isRequired ,
}
export default Movie;
Ajax fatch
fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')
console.log( fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating'))
Promise? 비동기+ 시나리오 라고하는데 잘모르겠다.
fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')
.then(data => console.log(data))
.catch(err => console.log(err))
Fetch url
Then 데이터 결과
Catch 에러처리
완성본
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | // JavaScript Codeimport React, { Component } from 'react'; import PropTypes from 'prop-types'; import './movie.css';
function Movie( {title, poster , genres , synopsis}){ return( <div className="Movie"> <div className="Movie_Colums Movie_Poster"> <MoviePoster poster={poster} alt={title}></MoviePoster> </div> <div className="Movie_Colums"> <h1>{title}</h1> <div className="Movie_Genres"> {genres.map( (genre ,index) => <MoiveGenres genre={genre} key={index} /> ) }
{/*genres.map( (genre , index) => { <MoiveGenres genre={genre} key={index} /> } )*/} </div> <p className="Movie_Synopsis"> {synopsis} </p> </div> </div> ) }
function MoviePoster ({poster , alt}) { return( <img src={poster} alt={alt} title={alt} className="Movie_Poster"/> ) } function MoiveGenres ({genre }) { return( <span className="Movie_Genres">{genre} </span> ) } /* Movie.propsTypes = { title : PropTypes.string.isRequired , poster : PropTypes.string.isRequired , genres : PropTypes.array.isRequired , synopsis : PropTypes.string.isRequired ,
}
MoviePoster.propsTypes = { poster : PropTypes.string.isRequired,import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import Movie from './Movie'
class App extends Component { //reder 순서는 will-> render -> did //update 순서는 will recevies(변경 getDerivedStateFromProps()) -> should -> true ->willupdqte - render state = { greeting : "Hello", page : 1 , scrollH : 0, next : false,
}
componentDidMount(){ window.addEventListener('scroll', this._handleScroll); this._getMoives(); } _handleScroll = () => { //console.log(window.innerHeight + window.scrollY); let heightScroll = window.innerHeight + window.scrollY
if(heightScroll == document.body.offsetHeight && this.state.next === false){ alert("뻐엉"); this.setState ({ next : true, }) this._getMoives();
}
} _callApi = () =>{ const pageNum = this.state.page; this.setState({ page : pageNum+1 }) //console.log( fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')) //fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating&page=1') console.log("호출페이지"+pageNum) return fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating&page='+pageNum) .then(data => data.json()) .then(json => json.data.movies) .catch(err => console.log(err)) }
_renderMovies = () => { const movies = this.state.movies.map( (movie , index) => { return <Movie title={movie.title_english} poster={movie.medium_cover_image} genres={movie.genres} synopsis={movie.synopsis} key={index} ></Movie> }) //console.log(movies) return movies } _getMoives = async () => { //await이유? //callAPi의 기능이 끝나길 기다리고 그 후에 무비 리스트를 받아오겠다.라는 의미인덧 //console.log("getMoives call") let addMovies = await this._callApi(); let oddMovies = this.state.movies; if (!(oddMovies === undefined)) { addMovies = [...oddMovies , ...addMovies]; } this.setState ({ // moives : moives 같은 효과 movies : addMovies , next : false,
})
}
render() {
return ( <div className="App">
{this.state.movies ? this._renderMovies() : 'Loding'} {/* 위와 같은 문법은 다음과같다. <Movie title={moives[0].title} poster={moives[0].poster}/> 0~2까지 배열길이 만큼 돌려줌 이터레이터와 비슷한 개념일듯 이와 같은문법임 */}
</div> ); }
}
export default App;
alt : PropTypes.string.isRequired, } MoiveGenres.propsTypes = { genres : PropTypes.string.isRequired, } */ export default Movie;
|
Moive.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | // JavaScript Codeimport React, { Component } from 'react'; import PropTypes from 'prop-types'; import './movie.css';
function Movie( {title, poster , genres , synopsis}){ return( <div className="Movie"> <div className="Movie_Colums Movie_Poster"> <MoviePoster poster={poster} alt={title}></MoviePoster> </div> <div className="Movie_Colums"> <h1>{title}</h1> <div className="Movie_Genres"> {genres.map( (genre ,index) => <MoiveGenres genre={genre} key={index} /> ) }
{/*genres.map( (genre , index) => { <MoiveGenres genre={genre} key={index} /> } )*/} </div> <p className="Movie_Synopsis"> {synopsis} </p> </div> </div> ) }
function MoviePoster ({poster , alt}) { return( <img src={poster} alt={alt} title={alt} className="Movie_Poster"/> ) } function MoiveGenres ({genre }) { return( <span className="Movie_Genres">{genre} </span> ) } /* Movie.propsTypes = { title : PropTypes.string.isRequired , poster : PropTypes.string.isRequired , genres : PropTypes.array.isRequired , synopsis : PropTypes.string.isRequired ,
}
MoviePoster.propsTypes = { poster : PropTypes.string.isRequired, alt : PropTypes.string.isRequired, } MoiveGenres.propsTypes = { genres : PropTypes.string.isRequired, } */ export default Movie;
|
Css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | .Movie{ background-color: white; width : 40%; display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; text-overflow: ellipsis; margin-bottom: 50px; padding: 0 20px; box-shadow: 0 8px 38px rgba(133, 133, 133, 0.3), 0 5px 12px rgba(133, 133, 133,0.22); }
.Movie_Colums{ width:30%; background-color: white; box-sizing: border-box; }
.Movie_Colums:last-child{ padding:10px 0; width: 60%; }
.Movie h1{ font-size:20px; font-weight: 600; }
.Movie .Movie_Genres{ display: flex; flex-wrap: wrap; margin-bottom: 5px; } .Movie_Genres .Movie_Genres{ margin-right: 10px; color : #B4B5BD; } .Movie .Movie_Synopsis{
color:#B4B5BD; text-overflow: ellipsis; line-height: 1em; height: 10em; overflow: hidden;
}
.Movie .Movie_Poster{ max-width: 100%; position: relative; top:-20px; box-shadow: -10px 19px 38px rgba(83, 83, 83, 0.3), 10px 15px 12px rgba(80,80,80,0.22); }
@media screen and (min-width:320px) and (max-width:667px){ .Movie{ width:100%; } }
@media screen and (min-width:320px) and (max-width:667px) and (orientation: portrait){ .Movie{ width:100%; flex-direction: column; } .Movie_Poster{ top:0; left:0; width:100%; } .Movie_Colums{ width:100%!important; } }
|