이번 블로그에서는 React와 OMDB API를 사용하여 Movie Finder 웹사이트를 구축하는 과정을 살펴보겠습니다. 이 웹사이트를 통해 사용자는 어벤져스, 스타워즈, 시리즈 등의 카테고리별로 영화를 탐색하고 특정 검색어를 사용하여 영화를 검색할 수 있습니다. 각 영화에는 세부 정보 페이지가 있어 좋아하는 영화에 대해 더 쉽게 탐색할 수 있습니다.
Movie Finder 웹사이트를 통해 사용자는 다음을 수행할 수 있습니다.
프로젝트의 디렉토리 구조는 다음과 같습니다.
movie-finder/ ├── public/ ├── src/ │ ├── components/ │ │ └── Navbar.js │ │ └── Footer.js │ ├── pages/ │ │ └── Home.js │ │ └── Movies.js │ │ └── Series.js │ │ └── SearchResults.js │ │ └── MovieDetail.js │ └── App.js │ └── App.css └── package.json
저장소 복제:
git clone https://github.com/abhishekgurjar-in/movie-finder.git cd movie-finder
종속성 설치:
npm install
OMDB API에서 API 키를 받으세요.
프로젝트 루트에 .env 파일을 생성하고 API 키를 추가하세요.
REACT_APP_OMDB_API_KEY=yourapikey
프로젝트 실행:
npm start
홈페이지에서는 어벤져스와 스타워즈라는 두 가지 카테고리의 영화를 선보입니다. 사용자가 영화 카드를 클릭하면 영화 상세 페이지로 리디렉션됩니다.
Home.js의 코드 조각:
import React, { useEffect, useState } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; import Movies from "./Movies"; import Series from "./Series"; const Home = () => { const [avengersMovies, setAvengersMovies] = useState([]); const [starWarsMovies, setStarWarsMovies] = useState([]); const [loadingAvengers, setLoadingAvengers] = useState(true); const [loadingStarWars, setLoadingStarWars] = useState(true); const navigate = useNavigate(); useEffect(() => { fetchMovies("Avengers", setAvengersMovies, setLoadingAvengers); fetchMovies("Star Wars", setStarWarsMovies, setLoadingStarWars); }, []); const fetchMovies = (query, setMovies, setLoading) => { axios .get(`http://www.omdbapi.com/?s=${query}&apikey=you_api_key`) .then((response) => { if (response.data.Search) { setMovies(response.data.Search); } else { setMovies([]); // Clear movies if no results } }) .catch((error) => { console.error(`There was an error fetching the ${query} movies!`, error); setMovies([]); // Clear movies if there is an error }) .finally(() => { setLoading(false); }); }; const handleCardClick = (id) => { navigate(`/movie/${id}`); }; const renderMovies = (movies, loading) => ( loading ? ( <div className="loader"><div className="load"></div></div> ) : ( <div className="cards"> {movies.length > 0 ? ( movies.map((movie) => ( <div key={movie.imdbID} className="card" onClick={() => handleCardClick(movie.imdbID)}> <img src={movie.Poster} alt={movie.Title} /> <h2>{movie.Title}</h2> </div> )) ) : ( <p>No movies found.</p> )} </div> ) ); return ( <> <div className="home"> <div className="movie-category"> <h4>Avengers Movies</h4> {renderMovies(avengersMovies, loadingAvengers)} </div> <br /> <br /> <div className="movie-category"> <h4>Star Wars Movies</h4> {renderMovies(starWarsMovies, loadingStarWars)} </div> </div> <Movies /> <Series /> </> ); }; export default Home;
사용자는 웹사이트 상단의 검색창을 사용하여 모든 영화를 검색할 수 있습니다. 사용자의 쿼리를 기반으로 OMDB API에서 검색 결과를 가져옵니다.
SearchResults.js의 코드 조각:
import React, { useEffect, useState } from "react"; import axios from "axios"; import { useParams, useNavigate } from "react-router-dom"; const SearchResults = () => { const [movies, setMovies] = useState([]); const [loading, setLoading] = useState(false); const { query } = useParams(); const navigate = useNavigate(); // Add this line to use navigate useEffect(() => { if (query) { setLoading(true); // Set loading to true before starting the fetch axios .get(`http://www.omdbapi.com/?s=${query}&apikey=your_api_key`) .then((response) => { if (response.data.Search) { setMovies(response.data.Search); } else { setMovies([]); // Clear movies if no results } }) .catch((error) => { console.error("There was an error fetching the movie data!", error); }) .finally(() => { setLoading(false); // Set loading to false once fetch is complete }); } }, [query]); const handleCardClick = (id) => { navigate(`/movie/${id}`); // Navigate to movie detail page }; return ( <div className="search-results"> <h4>Search Results for "{query}"</h4> {loading ? ( <div className="loader"><div className="load"></div></div> // Loader ) : ( <div className="cards"> {movies.length > 0 ? ( movies.map((movie) => ( <div key={movie.imdbID} className="card" onClick={() => handleCardClick(movie.imdbID)}> <img src={movie.Poster} alt={movie.Title} /> <h2>{movie.Title}</h2> </div> )) ) : ( <p>No results found.</p> )} </div> )} </div> ); }; export default SearchResults;
사용자가 영화를 클릭하면 영화 세부정보 페이지로 리디렉션됩니다. 이 페이지에는 영화 제목, 포스터, 줄거리, 배우 등이 표시됩니다.
MovieDetail.js의 코드 조각:
import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { useParams } from 'react-router-dom'; const MovieDetail = () => { const [movie, setMovie] = useState(null); const [loading, setLoading] = useState(true); const { id } = useParams(); useEffect(() => { axios.get(`http://www.omdbapi.com/?i=${id}&apikey=your_api_key`) .then((response) => { setMovie(response.data); }) .catch((error) => { console.error("There was an error fetching the movie details!", error); }) .finally(() => { setLoading(false); }); }, [id]); if (loading) return <div className="loader"> <div className="load"></div> </div>; if (!movie) return <div className='loader'>No movie data found!</div>; return ( <div className="movie-detail"> <div className="detail-box"> <h1>{movie.Title}</h1> <p><strong>Year:</strong> {movie.Year}</p> <p><strong>Rating:</strong> {movie.imdbRating}</p> <p><strong>Genre:</strong> {movie.Genre}</p> <p><strong>Director:</strong> {movie.Director}</p> <p><strong>Actors:</strong> {movie.Actors}</p> <p><strong>Plot:</strong> {movie.Plot}</p> <p><strong>Runtime:</strong> {movie.Runtime}</p> <p><strong>Language:</strong> {movie.Language}</p> <p><strong>Country:</strong> {movie.Country}</p> <p><strong>Awards:</strong> {movie.Awards}</p> </div> <div className="img-box"> <img src={movie.Poster} alt={movie.Title} /> </div> </div> ); }; export default MovieDetail;
Movies.js 및 Series.js 페이지에는 각각 영화와 TV 시리즈가 나열됩니다.
Movies.js의 코드 조각:
import React, { useEffect, useState } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; const Movies = () => { const [movies, setMovies] = useState([]); const navigate = useNavigate(); useEffect(() => { axios .get(`http://www.omdbapi.com/?s=Avengers&type=movie&apikey=${process.env.REACT_APP_OMDB_API_KEY}`) .then(response => setMovies(response.data.Search || [])) .catch(error => console.error(error)); }, []); const handleCardClick = (id) => { navigate(`/detail/${id}`); }; return ( <div className="movies"> <h2>Movies</h2> <div className="cards"> {movies.map(movie => ( <div key={movie.imdbID} className="card" onClick={() => handleCardClick(movie.imdbID)}> <img src={movie.Poster} alt={movie.Title} /> <h3>{movie.Title}</h3> </div> ))} </div> </div> ); }; export default Movies;
Series.js의 코드 조각:
import React, { useEffect, useState } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; const Series = () => { const [series, setSeries] = useState([]); const navigate = useNavigate(); useEffect(() => { axios .get(`http://www.omdbapi.com/?s=Star Wars&type=series&apikey=${process.env.REACT_APP_OMDB_API_KEY}`) .then(response => setSeries(response.data.Search || [])) .catch(error => console.error(error)); }, []); const handleCardClick = (id) => { navigate(`/detail/${id}`); }; return ( <div className="series"> <h2>TV Series</h2> <div className="cards"> {series.map(show => ( <div key={show.imdbID} className="card" onClick={() => handleCardClick(show.imdbID)}> <img src={show.Poster} alt={show.Title} /> <h3>{show.Title}</h3> </div> ))} </div> </div> ); }; export default Series;
Navbar 구성요소를 사용하면 사용자가 여러 페이지 사이를 탐색하고 검색을 수행할 수 있습니다.
import React, { useState } from "react"; import { NavLink, Link } from "react-router-dom"; const Navbar = () => { const [searchQuery, setSearchQuery] = useState(""); const handleSearch = (event) => { if (event.key === 'Enter' && searchQuery.trim()) { document.getElementById('search-link').click(); } }; return ( <div className="navbar"> <div className="logo"> <h1>Movie Finder</h1> </div> <div className="page-list"> <NavLink to="/"> <h4>Home</h4> </NavLink> <NavLink to="/movies"> <h4>Movies</h4> </NavLink> <NavLink to="/series"> <h4>TV Series</h4> </NavLink> </div> <div className="search-box"> <input type="text" placeholder="Search for movies or series" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} onKeyDown={handleSearch} /> <Link to={`/search/${searchQuery}`} id="search-link"> <button>Search</button> </Link> </div> </div> ); }; export default Navbar;
바닥글 구성요소는 간단한 바닥글 메시지를 제공합니다.
import React from 'react'; const Footer = () => { return ( <div className='footer'> Made with <span>❤️</span> by Abhishek Gurjar </div> ); }; export default Footer;
*{ box-sizing: border-box; } body{ font-family: sans-serif; margin: 0; padding: 0; } .navbar { padding-inline: 100px; display: flex; align-items: center; justify-content: space-between; background-color: red; } .search-btn{ background-color: red; } .logo h1{ font-size: 25px; color:black; } .page-list { display: flex; align-items: center; gap: 40px; } .page-list a{ color: white; text-decoration: none; font-size: 20px; } .page-list a:hover{ color: black; } .page-list a.active{ color: black; } .search-box{ box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; background-color:white; color: gray; width: 250px; height: 40px; border-radius: 50px; overflow: hidden; } .search-box input{ width: 200px; height: 40px; margin-left: 10px; border: none; outline: none; } .home{ margin-block: 40px; margin-inline: 60px; } .home h4{ font-size: 16px; } .movies{ margin-block: 40px; margin-inline: 60px; } .movies h4{ font-size: 16px; } .cards{ display: flex; flex-wrap: wrap; align-items:center ; justify-content: space-between; gap: 10px; } .card{ width:200px; height:360px; border-radius: 10px; overflow: hidden; box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; } .card img{ width: 200px; height: 290px; object-fit: cover; } .card h2{ margin: 10px; font-size: 16px; text-align: center; } .series{ margin-block: 40px; margin-inline: 60px; } .series h4{ font-size: 16px; } .home{ margin-block: 40px; margin-inline: 60px; } .search-results{ margin-block: 40px; margin-inline: 60px; } .search-results h4{ font-size: 16px; } .loader{ min-height: 90vh; display: flex; align-items: center; justify-content: center; } /* HTML: <div class="loader"></div> */ .load { width: 50px; padding: 8px; aspect-ratio: 1; border-radius: 50%; background: #ff1900; --_m: conic-gradient(#0000 10%,#000), linear-gradient(#000 0 0) content-box; -webkit-mask: var(--_m); mask: var(--_m); -webkit-mask-composite: source-out; mask-composite: subtract; animation: l3 1s infinite linear; } @keyframes l3 {to{transform: rotate(1turn)}} .movie-detail { margin-block: 40px; margin-inline: 60px; display: flex; align-items: flex-start; justify-content: space-between; } img-box{ width: 50%; } .movie-detail img { border-radius: 10px; width: 330px; height: auto; object-fit: cover; box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; } .detail-box{ width: 50%; } .movie-detail p { font-size: 18px; margin: 10px 0; } .movie-detail a { display: inline-block; margin-top: 20px; color: #007bff; text-decoration: none; } .movie-detail a:hover { text-decoration: underline; } .footer{ width: 100%; background-color: red; text-align: center; color: white; padding: 20px; }
여기에서 Movie Finder 웹사이트의 라이브 데모를 확인하실 수 있습니다.
이번 블로그에서는 React, React Router, Axios를 사용하여 Movie Finder 웹사이트를 만드는 방법을 배웠습니다. 이 프로젝트는 공개 API와 상호 작용하고, React에서 상태를 관리하고, 간단하면서도 기능적인 영화 탐색 경험을 만드는 방법을 보여줍니다.
디자인을 자유롭게 맞춤화하고 사용자 리뷰나 영화 평가와 같은 기능을 추가하여 더욱 역동적으로 만들어 보세요!
Abhishek Gurjar는 실용적이고 기능적인 웹 애플리케이션 제작에 열정을 쏟는 헌신적인 웹 개발자입니다. GitHub에서 더 많은 프로젝트를 확인해 보세요.
위 내용은 React를 사용하여 Movie Finder 웹사이트 구축하기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!