오락기/react

노마드 코더님 영화 사이트 클론코딩

문방구앞오락기 2018. 8. 17. 13:15

노마드 코더님의 강좌를 보고 제작하였습니다.(+ 몇가지는 제가 기능적으로 추가함) 

해당 자료를 클론코딩했으며 이후 한국영화 서비스로 만들려고함. 

 

 

 

Reactjs 영화  서비스 만들기 클론 코딩 

 

 

class App extends Component { 

render() { 

return ( 

<div className="App"> 

<header className="App-header"> 

<h1 className="App-title">Test</h1> 

</header> 

{movies.mapmovie=> { 

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( {titleposter}){ 

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.logfetch('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.mapmovie=> { 

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( {titleposter}){ 

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.logfetch('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; 

} 

}