[TIL] React로 Movie app 만들기

신재욱·2023년 6월 26일
0
post-thumbnail

📒 오늘 공부한 내용

🔍 수업목차

[8-1] ReactJS로 Movie app 만들기

✅ ReactJS로 Movie app 만들기

  • 영화 목록을 불러와서 화면에 표시하고, 각 영화를 클릭하면 모달 창에서 상세 정보를 확인할 수 있도록 구현할 것이다.

  • 필요한 패키지를 import하고 useState, useEffect를 사용하여 상태와 데이터를 관리한다. 그리고 CSS 스타일링을 위해 styled-components(@emotion/styled)를 사용한다.

// 필요한 패키지 import
import { useState, useEffect } from "react";
import styled from "@emotion/styled";
import { Modal } from "antd";
  • 상태와 API 키를 설정하고, useEffect를 사용하여 컴포넌트가 마운트될 때 영화 데이터를 가져오도록 한다.
// 상태와 API 키 설정
const [movies, setMovies] = useState([]);
const [modalVisible, setModalVisible] = useState(false); // 모달의 가시성 상태
const [selectedMovie, setSelectedMovie] = useState(null); // 선택한 영화
const API_KEY = "15f8fbf5168d6da001f1e3c2c4b76277";

// 영화 데이터 가져오기
useEffect(() => {
  fetchMoviesData();
}, []);

const fetchMoviesData = async () => {
  try {
    const response = await fetch(
      `https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year&api_key=${API_KEY}`
    );
    const data = await response.json();
    setMovies(data.data.movies);
  } catch (error) {
    console.log(error);
  }
};
  • 이제 UI를 구성하기 위해 필요한 스타일과 컴포넌트를 작성한다. Main, Item, Img, Tie, TextTie 등의 스타일을 정의하고, fetchMoviesData에서 가져온 영화 데이터를 map 함수를 사용하여 그리드 형태로 화면에 표시한다. 클릭한 영화에 대한 상세 정보는 Modal 컴포넌트로 모달 창에 표시된다.
// UI 스타일과 컴포넌트 정의
const Main = styled.div`
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  width: 80%;
  margin: 0 auto;
  padding: 40px 0px;
`;

const Item = styled.div`
  width: calc(25% - 20px);
  text-align: center;
`;

const Img = styled.img`
  cursor: pointer;
  background-color: gray;
  object-fit: cover;
  transition: border-color 0.3s, transform 0.3s;
  border: 2px solid transparent;

  :hover {
    border: 5px solid gold;
    transform: translateY(-10px);
  }
`;

const Tie = styled.div`
  display: flex;
`;

const TextTie = styled.div`
  display: flex;
  flex-direction: column;
  margin-left: 10px;
`;

// 영화 목록 UI 구성
return (
  <Main className="grid-container">
    {movies.map((movie) => (
      <Item key={movie.id} className="grid-item">
        <Img
          src={movie.medium_cover_image}
          alt={movie.title}
          onError={(event) => {
            event.target.style.display = "none";
          }}
          onClick={() => handleMovieClick(movie)}
        />
        <p>제목: {movie.title}</p>
        <p>연도: {movie.year}</p>
      </Item>
    ))}
    {selectedMovie && (
      <Modal
        visible={modalVisible}
        onCancel={handleModalClose}
        footer={null}
        destroyOnClose

        <Tie>
          <img
            src={selectedMovie.medium_cover_image}
            alt={selectedMovie.title}
          />
          <TextTie>
            <p>제목: {selectedMovie.title}</p>
            <p>연도: {selectedMovie.year}</p>
            <p>내용: {truncateSummary(selectedMovie.summary, 200)}</p>
            <Button onClick={handleMoreButtonClick}>더보기</Button>
          </TextTie>
        </Tie>
      </Modal>
    )}
  </Main>
);
  • 마지막으로, 영화 요약(summary) 내용이 너무 길 경우 일정 길이까지만 표시하고 "더보기" 버튼을 클릭하면 해당 영화의 URL로 이동할 수 있도록 한다.
// 영화 요약 내용 요약 함수
const truncateSummary = (summary, maxLength) => {
  if (summary.length > maxLength) {
    return summary.substring(0, maxLength) + "...";
  }
  return summary;
};

// 더보기 버튼 클릭 핸들러
const handleMoreButtonClick = () => {
  window.open(selectedMovie.url, "_blank");
};
  • React 영화 목록 앱은 API를 통해 영화 데이터를 가져와 화면에 그리드 형태로 표시하고, 선택한 영화의 상세 정보를 모달 창에서 확인할 수 있다. 더보기 버튼을 클릭하면 해당 영화의 URL로 이동하여 더 많은 정보를 얻을 수 있다.

✍ 알게된 점 / 과제

📝 실습

import { useState, useEffect } from "react";
import styled from "@emotion/styled";
import { Modal } from "antd";

export default function Weather() {
  const [movies, setMovies] = useState([]);
  const [modalVisible, setModalVisible] = useState(false); // 모달의 가시성 상태
  const [selectedMovie, setSelectedMovie] = useState(null); // 선택한 영화

  const API_KEY = "15f8fbf5168d6da001f1e3c2c4b76277";

  useEffect(() => {
    fetchMoviesData();
  }, []);

  const Main = styled.div`
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    width: 80%;
    margin: 0 auto;
    padding: 40px 0px;
  `;

  const Item = styled.div`
    width: calc(25% - 20px);
    text-align: center;
  `;

  const Img = styled.img`
    cursor: pointer;
    background-color: white;
    object-fit: cover;
    transition: border-color 0.3s, transform 0.3s; /* 테두리 색상 및 변화에 애니메이션 효과 추가 */
    border: 2px solid transparent; /* 초기 테두리 설정 */
    margin-top: 20px;
    :hover {
      transform: translateY(-10px); /* 위로 10px 이동하는 애니메이션 효과 */
    }
  `;

  const Tie = styled.div`
    display: flex;
  `;

  const TextTie = styled.div`
    display: flex;
    flex-direction: column;
    margin-left: 10px;
  `;

  const P1 = styled.p`
    margin-top: 10px;
  `;

  const Button = styled.button`
    margin-top: 10px;
    cursor: pointer;
    background-color: #1890ff;
    color: white;
    border: none;
    padding: 5px 10px;
    border-radius: 4px;
    transition: background-color 0.3s;
    margin-top: 30px;
    :hover {
      background-color: #40a9ff;
    }
  `;

  const fetchMoviesData = async () => {
    try {
      const response = await fetch(
        `https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year&api_key=${API_KEY}`
      );
      const data = await response.json();
      console.log(data.data.page_number);
      setMovies(data.data.movies);
    } catch (error) {
      console.log(error);
    }
  };

  const handleMovieClick = (movie) => {
    setSelectedMovie(movie);
    setModalVisible(true);
  };

  const handleModalClose = () => {
    setModalVisible(false);
  };

  const truncateSummary = (summary, maxLength) => {
    if (summary.length > maxLength) {
      return summary.substring(0, maxLength) + "...";
    }
    return summary;
  };

  const handleMoreButtonClick = () => {
    if (selectedMovie && selectedMovie.url) {
      window.open(selectedMovie.url, "_blank");
    }
  };

  return (
    <Main className="grid-container">
      {movies.map((movie) => (
        <Item key={movie.id} className="grid-item">
          <Img
            src={movie.medium_cover_image}
            alt={movie.title}
            onError={(event) => {
              event.target.style.display = "none";
            }}
            onClick={() => handleMovieClick(movie)}
          />
        </Item>
      ))}
      {selectedMovie && (
        <Modal
          visible={modalVisible}
          onCancel={handleModalClose}
          footer={null}
          destroyOnClose

          <Tie>
            <img
              src={selectedMovie.medium_cover_image}
              alt={selectedMovie.title}
            />
            <TextTie>
              <P1>제목: {selectedMovie.title}</P1>
              <P1>연도: {selectedMovie.year}</P1>
              <P1>내용: {truncateSummary(selectedMovie.summary, 200)}</P1>
              <Button onClick={handleMoreButtonClick}>더보기</Button>
            </TextTie>
          </Tie>
        </Modal>
      )}
    </Main>);}

💫 느낀점

  • 이번 프로젝트를 통해 React의 기본 개념과 주요 기능들을 익히는 동시에, 외부 API와의 데이터 통신, 컴포넌트 간의 상태 및 이벤트 처리 등 다양한 경험을 할 수 있었다.

📥 Reference

  • 없음
profile
1년차 프론트엔드 개발자

0개의 댓글