[React] Movie app clone coding : Async Await in React

5 minute read

6-3. Async Await in React

Async, Await

1componentDidMount(){
2  fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')
3  .then(response => response.json())
4  .then(json => console.log)
5  .catch(err => console.log('error'))
6}

AsyncAwait 는 위 코드들을 좀더 분명하게 작성해주는 도구이다.

영화들을 state에 올리려면 아래와 같은 작업을 해야 한다. 하지만 세련되지 않고, 어플리케니션으 크면 then 안에 then으로 이어지면서 callback hell 에 빠지게 된다. 그래서 Async , Await 를 쓸 것이다.

1.then(json =>{
2    this.setState({
3        movies: json.data.movies
4    })
5    .then(() => .then())    // ... callback hell
6})

Async, Await 적용

먼저 새로운 function을 두개 만든다.

1// App.js
2_getMovies = () => {}
3_callApi = () => {}

큰 사이즈의 componentDidMount 를 갖고 싶지 않으므로, 안에 있던 내용을 _callAPI 로 옮기고, this._getMovies() 를 작성한다. 작은 function들이 각기 다른 곳에 있는 것이 좋다.

 1// App.js
 2componentDidMount(){
 3  this._getMovies();    // 큰 사이즈의 componentDidMount를 갖고 싶지 않으므로
 4}
 5
 6_getMovies = () => {
 7
 8}
 9
10_callApi = () => {
11  fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')
12  .then(response => response.json())
13  .then(json => console.log)
14  .catch(err => console.log('error'))
15}

그다음 async 라는 것을 한다. (asynchronous )

1_getMovies = async() => {}

이 안에 await 라는 것을 작성한다.

1_getMovies = async() => {
2  const movies = await this._callApi()
3}

didMount 하고 나면, get movies 를 한다. 그리고 이것은 asynchronous function 인데 movie 라는 variable 을 갖고 있는 것이다. 그리고 moviecallApi 라는 function을 await 모드에서 value 를 갖고 있다.

1_getMovies = async() => {
2  const movies = await this._callApi()
3  this.setState({
4    movies
5  })
6}

await 하는 것은 callApi 기능이 끝나는 것을 기다리고(성공적인 수행이 아니라 끝나는 것을 기다린다), callApireturn value 가 무어이든 그 valuemovies 에 넣는다.

다시 정리하면, callApitreturn valuemoviesset 한다. 그리고 해당 컴포넌트의 setStatemovies 로 할 것이다. 이것은 this._callApireturn value 이다.

this.setState({ movies }) 부분은 setstatecallApi 작업이 완료되기 전까지 실행되지 않는다.

callApi 작업이 완료된 후에 this.setState({ movies }) 가 실행된다. 이를 하는 방법은 아래 코드와 같다.

fetch 라는 이름의 promise 를 return 한다.

1_callApi = () => {
2  return fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')
3    .then(response => response.json())
4    .then(json => json.data.movies)
5    .catch(err => console.log('error'))
6}

movie object 가 변경되었으므로 _renderMovies 의 코드를 아래와 같이 수정해준다.

1_renderMovies = () => {
2  const movies = this.state.movies.map((movie, index) => {
3    return <Movie title={movie.title} poster={movie.large_cover_image} key={movie.id} />       
4    // poster = movie.large_cover_image, key = movie.id 로 수정
5  })
6  return movies
7}

component의 key는 느리기 때문에 인덱스를 사용하는 것은 좋지않다. 그러므로 keymovie.id 를 사용한다.

정리

  1. fetchcallApi 로 변경
  2. getMovies 라는 asynchronous function 작성
  3. 그 안에 movies 라는 const variable 생성
  4. callApi 작업이 완료되고 return 하는 것을 await
  5. callApifetch promise 를 return하는데, 데이터는 Json
  6. 그러므로 json.data.movies 라는 value는 const movies 의 결괏값이 되는 것
  7. 그리고 component의 statemoviesset 한 것
  8. state 안에 movies 가 있으면 render movies 라는 fucntion 을 불러옴
  9. render moviesmovies 라는 const를 불러오는데 이는 타이틀, 포스터 순으로 맵핑 함

전체코드

 1// App.js
 2import React, { Component } from 'react';
 3import './App.css';
 4import Movie from './Movie';
 5
 6class App extends Component {
 7
 8  state = {}
 9
10  componentDidMount(){
11    this._getMovies();    // 큰 사이즈의 componentDidMount를 갖고 싶지 않으므로
12  }
13
14  _renderMovies = () => {
15    const movies = this.state.movies.map((movie, index) => {
16      return <Movie title={movie.title} poster={movie.large_cover_image} key={movie.id} />
17    })
18    return movies
19  }
20
21  _getMovies = async() => {
22    const movies = await this._callApi()
23    this.setState({
24      movies
25    })
26  }
27
28
29  _callApi = () => {
30    return fetch('https://yts.am/api/v2/list_movies.json?sort_by=rating')
31    .then(response => response.json())
32    .then(json => json.data.movies)
33    .catch(err => console.log('error'))
34  }
35
36
37  render() {
38    return (
39      <div className="App">
40        {this.state.movies ? this._renderMovies() : 'Loading'}
41      </div>
42    );
43  }
44}
45
46export default App;