영화 정보 api

 

API Documentation - YTS YIFY

Official YTS YIFY API documentation. YTS offers free API - an easy way to access the YIFY movies details.

yts.mx

아래 조건을 사용한 api를 사용함

https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year

import { useEffect, useState } from "react";

function App() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const getMovie = async () => {
    const response = await fetch(
      "https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
    );
    const json = await response.json();
    // 아래 같이 한줄로 작성해도 됨
    // const json = await (
    //   await fetch(
    //     "https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
    //   )
    // ).json();
    setMovies(json.data.movies);
    setLoading(false);
  };
  useEffect(() => {
    getMovie();
  }, []);
  return (
    <div>
      {loading ? (
        <h1>Loading...</h1>
      ) : (
        <div>
          {movies.map((movie) => (
            <div key={movie.id}>
              <h2>{movie.title}</h2>
              <img src={movie.medium_cover_image}></img>
              <p>{movie.summary}</p>
              <ul>
                {/* hasOwnProperty를 사용하여 movie 내에 genres 프로퍼티가 있는지 없는지 확인할 수 있음 */}
                {movie.hasOwnProperty("genres")
                  ? movie.genres.map((genre) => <li key={genre}>{genre}</li>)
                  : null}
              </ul>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

export default App;

컴포넌트 분리

Movie.js 파일 생성
// Movie.js
function Movie({ title, coverImg, summary, genres }) {
  return (
    <div>
      <h2>{title}</h2>
      <img src={coverImg} alt={title}></img>
      <p>{summary}</p>
      <ul>
        {genres.map((genre) => (
          <li key={genre}>{genre}</li>
        ))}
      </ul>
    </div>
  );
}

export default Movie;
App.js에서 Movie 컴포넌트 사용
// App.js
import { useEffect, useState } from "react";
import Movie from "./Movie";

function App() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const getMovie = async () => {
    const response = await fetch(
      "https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
    );
    const json = await response.json();
    setMovies(json.data.movies);
    setLoading(false);
  };
  useEffect(() => {
    getMovie();
  }, []);
  return (
    <div>
      {loading ? (
        <h1>Loading...</h1>
      ) : (
        <div>
          {movies.map((movie) => (
            <Movie
              key={movie.id}
              title={movie.title}
              coverImg={movie.medium_cover_image}
              summary={movie.summary}
              genres={movie.genres}
            />
          ))}
        </div>
      )}
    </div>
  );
}

export default App;

입력한 props의 타입 검증을 위한 PropTypes 적용

// Movie.js
import PropTypes from "prop-types";

Movie.propTypes = {
  title: PropTypes.string.isRequired,
  coverImg: PropTypes.string.isRequired,
  summary: PropTypes.string,
  genres: PropTypes.arrayOf(PropTypes.string),
};

React Router

5.3.0 버전을 사용
react router 설치
npm i react-router-dom@5.3.0

이제 스크린 (Router) 단위로 생각

App.js는 route를 받아서 스크린을 불러옴

스크린(Router) 단위로 파일 생성

src 경로 안의 파일 정리

파일 경로 새로 생성

  • components/Movie.js (경로를 옮겼기 때문에 import의 경로 수정)
  • routes/Home.js
  • routes/Detail.js
기존 App.js에 있던 내용을 Home.js로 옮겨줌
// Home.js
import { useEffect, useState } from "react";
import Movie from "./components/Movie";

function Home() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const getMovie = async () => {
    const response = await fetch(
      "https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
    );
    const json = await response.json();
    setMovies(json.data.movies);
    setLoading(false);
  };
  useEffect(() => {
    getMovie();
  }, []);
  return (
    <div>
      {loading ? (
        <h1>Loading...</h1>
      ) : (
        <div>
          {movies.map((movie) => (
            <Movie
              key={movie.id}
              title={movie.title}
              coverImg={movie.medium_cover_image}
              summary={movie.summary}
              genres={movie.genres}
            />
          ))}
        </div>
      )}
    </div>
  );
}

export default Home;
App에서 router 적용
// App.js
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./routes/Home";
import Movie from "./routes/Detail";

function App() {
  return (
    <Router>
      <Switch>
        <Route path="/movie">
          <Movie />
        </Route>
        <Route path="/">
          <Home />
        </Route>
      </Switch>
    </Router>
  );
}

export default App;
  • 위 코드에서 "/moive" 가 "/" Route보다 아래에 있으면 "/movie" 주소를 입력해도 "/" Route가 먼저 render된다. 위의 코드처럼 router의 위치를 조종하거나 exact={true}를 적용
  • Switch 태그는 Route를 한 번에 하나만 render할 때 사용

페이지 이동

<a> 태그 사용하기

  • 사용 가능
  • 다만 해당 페이지로 이동했을 경우 전체 페이지가 다시 rendering
<h2>
  <a href="/movie">{title}</a>
</h2>

<Link> 사용하기

  • 브라우저의 새로고침 없이 다른 페이지로 이동시켜주는 컴포넌트
// Movie.js
import { Link } from "react-router-dom";

<h2>
  <Link to="/movie">{title}</Link>
</h2>;

Dynamic URL

  • 변화하는 주소를 받을 수 있음
  • 변화하는 부분에 ":" 를 사용
// App.js
<Route path="/movie/:id">
  <Detail />
</Route>
id 값을 사용하기 위해서 props에서 id 전달
// Home.js
{
  movies.map((movie) => (
    <Movie
      key={movie.id}
      id={movie.id}
      title={movie.title}
      coverImg={movie.medium_cover_image}
      summary={movie.summary}
      genres={movie.genres}
    />
  ));
}
전달 받은 props 값 Link에 적용
// Movie.js
function Movie({ title, id, coverImg, summary, genres }) {
  return (
    <div>
      <h2>
        <Link to={`/movie/${id}`}>{title}</Link>
      </h2>
      <img src={coverImg} alt={title}></img>
      <p>{summary}</p>
      <ul>
        {genres.map((genre) => (
          <li key={genre}>{genre}</li>
        ))}
      </ul>
    </div>
  );
}

useParams

url에 있는 값을 반환해주는 함수

// Detail.js
import { useParams } from "react-router-dom";

function Detail() {
  const x = useParams();
  console.log(x);
  // {id: '53703'}
  // id: "53703"
  // [[Prototype]]: Object
  return <h1>Detail</h1>;
}

export default Detail;

Detail.js 내에서 api 받아오기

// Detail.js
import { useParams } from "react-router-dom";
import { useEffect } from "react";

function Detail() {
  const { id } = useParams();
  const getMovie = async () => {
    const json = await (
      await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
    ).json();
    // console.log(json);
  };
  useEffect(() => {
    getMovie();
  }, []);
  return <h1>Detail</h1>;
}

export default Detail;

최종 코드

// App.js
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";

function App() {
  return (
    <Router>
      <Switch>
        <Route path="/movie/:id">
          <Detail />
        </Route>
        <Route path="/">
          <Home />
        </Route>
      </Switch>
    </Router>
  );
}

export default App;

 

// Detail.js
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import MovieDetail from "../components/MovieDetail";

function Detail() {
  const { id } = useParams();
  const [loading, setLoading] = useState(true);
  const [movie, setMovie] = useState("");
  const getMovie = async () => {
    const json = await (
      await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
    ).json();
    // console.log(json);
    setLoading(false);
    setMovie(json.data.movie);
  };
  useEffect(() => {
    getMovie();
  }, []);

  return (
    <div>
      <h1>Detail</h1>
      {loading ? (
        <h1>loading</h1>
      ) : (
        <MovieDetail
          date_uploaded={movie.date_uploaded}
          download_count={movie.download_count}
          genres={movie.genres}
          language={movie.language}
          like_count={movie.like_count}
          large_cover_image={movie.large_cover_image}
          rating={movie.rating}
          runtime={movie.runtime}
          title={movie.title}
          torrents={movie.torrents}
          year={movie.year}
          description={movie.description_full}
        />
      )}
    </div>
  );
}

export default Detail;
// MovieDetail.js
import PropTypes from "prop-types";

function MovieDetail({
  date_uploaded,
  download_count,
  genres,
  language,
  like_count,
  large_cover_image,
  rating,
  runtime,
  title,
  year,
  description,
  torrents,
}) {
  return (
    <div>
      <h2>{title}</h2>
      <img src={large_cover_image} alt={title}></img>
      <p>Release: {year}</p>
      <p>Language: {language}</p>
      <p>Runtime: {runtime}</p>
      <p>Rating: {rating}</p>
      <p>Like Count: {like_count}</p>
      <p>Genre</p>
      <ul>
        {genres.map((genre) => (
          <li key={genre}>{genre}</li>
        ))}
      </ul>
      <p>{description}</p>
      <hr />
      <h3>Download info</h3>
      <p>File Uploaded: {date_uploaded}</p>
      <p>Download Count: {download_count}</p>
      <ul>
        {torrents.map((torrent, i) => (
          <li key={torrent.hash}>
            <a href={torrent.url}>다운로드 링크 {i + 1}</a>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default MovieDetail;
// Home.js

import { useEffect, useState } from "react";
import Movie from "../components/Movie";

function Home() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const getMovie = async () => {
    const response = await fetch(
      "https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
    );
    const json = await response.json();
    setMovies(json.data.movies);
    setLoading(false);
  };
  useEffect(() => {
    getMovie();
  }, []);
  return (
    <div>
      {loading ? (
        <h1>Loading...</h1>
      ) : (
        <div>
          {movies.map((movie) => (
            <Movie
              key={movie.id}
              id={movie.id}
              title={movie.title}
              coverImg={movie.medium_cover_image}
              summary={movie.summary}
              genres={movie.genres}
            />
          ))}
        </div>
      )}
    </div>
  );
}

export default Home;
// Movie.js

import PropTypes from "prop-types";
import { Link } from "react-router-dom";

function Movie({ title, id, coverImg, summary, genres }) {
  return (
    <div>
      <h2>
        <Link to={`/movie/${id}`}>{title}</Link>
      </h2>
      <img src={coverImg} alt={title}></img>
      {/* summary 길이에 따른 처리 */}
      <p>{summary.length > 235 ? `${summary.slice(0, 235)} ...` : summary}</p>
      <ul>
        {genres.map((genre) => (
          <li key={genre}>{genre}</li>
        ))}
      </ul>
    </div>
  );
}

Movie.propTypes = {
  id: PropTypes.number.isRequired,
  title: PropTypes.string.isRequired,
  coverImg: PropTypes.string.isRequired,
  summary: PropTypes.string,
  genres: PropTypes.arrayOf(PropTypes.string),
};

export default Movie;

'React' 카테고리의 다른 글

[React] TypeScript  (0) 2023.08.17
[React] styled-components  (0) 2023.08.16
[React 기초] 07. 예제 - Coin tracker  (0) 2023.08.10
[React 기초] 06. 예제 - Todo List  (0) 2023.08.10
[React 기초] 05. effects  (0) 2023.08.10

+ Recent posts