본문 바로가기

응용SW 개발 (React)/필기일지

231027 [응용SW개발] - API (TMDB 실습) - antd(Ant-design) 활용 (Grid, Col, Row, Card) - useState(), useEffet(), fetch(), .then 등

반응형

[23.10.27] 59일차

 

<<진도>>

[DB 연동 응용SW 개발]

- API (TMDB 실습)

- antd(Ant-design) 활용 (Grid, Col, Row)

- useState(), useEffet(), fetch(), .then

 

<<과제>>

. useEffect() 인수에 따라 어느 타이밍이 되는지 스터디

. 왜 두번씩 출력되는 지 스터디

. 메뉴 짝수 홀수 마다 세팅왜다른지 찾기 (개인)

 

<<10/27과제>>

. url 없어 깨진 배우사진 안보이게

. GridCard대신 AntCard변경

. md일 때 4, lg: 6

. 카드하단에 (영화목록)영화 제목, (배우목록)배우이름 보이게

 

* 1027 LandingPage

 

import React, { useEffect, useState } from 'react';

import { API_KEY, API_URL, IMAGE_BASE_URL } from '../../Config';

import MainImage from '../MainImage/MainImage';

import { Row, Button } from 'antd';

import GridCard from '../Commons/GridCard';

 

function LandingPage() {

 

  const [Movies, setMovies] = useState([]); // 빈배열 저장해두기

  console.log('Movies : ', Movies);

 

  // [페이지의 첫 번째 영화 정보저장] =========================================

  const [MainMovieImage, setMainMovieImage] = useState(null);

  console.log('MainMovieImage : ', MainMovieImage);

 

  useEffect(() => {

    const endpoint = `${API_URL}popular?api_key=${API_KEY}&language=en-US&page=1`

    fetch(endpoint)

      .then(response => response.json())

      .then(response => {

        setMovies(response.results);

        setMainMovieImage(response.results[0]);

      });

  }, []);

 

  // [Button] ===================================================

  const loadMoreItems = () => {

    const endpoint = `${API_URL}popular?api_key=${API_KEY}&language=en-US&page=2`

    // console.log(endpoint);

    fetch(endpoint)

      .then(response => response.json())

      .then(response => {

        setMovies(response.results);

        setMainMovieImage(response.results[0]);

      });

  };

 

  return (

    <>

      <div style={{ width: '100%' }}>

        {/* Main Image */}

        {MainMovieImage &&

          <MainImage

            image={`${IMAGE_BASE_URL}w1280${MainMovieImage.poster_path}`}

            title={MainMovieImage.title}

            overview={MainMovieImage.overview}

          />}

 

        <div style={{ width: '85%', margin: '1rem auto' }}>

          <h2>화제의 영화</h2>

          <hr />

 

          {/* Movie Grid Card */}

          <Row gutter={[10, 10]}>

            {Movies.map((movie, index) => {

              return (

                <React.Fragment key={index}>

                  <GridCard

                    path={`${IMAGE_BASE_URL}w400${movie.poster_path}`}

                    title={movie.title}

                  />

                </React.Fragment>

              );

            })}

          </Row>

 

        </div>

        <div style={{ display: 'flex', justifyContent: 'center' }}>

          <Button onClick={loadMoreItems}> 더보기 </Button>

        </div>

      </div>

    </>

 

  );

};

export default LandingPage;

 

 

*** 위 코드는 더보기 클릭 시 페이지가 교체되고 endpoint, fetch()가 반복됨.

=> 함수로 refactoring 필요

 

1. 블록 지정 후

2. ctrl + shift + R : innerfunction (Vscode 자체기능)

 

 

3. return안에 inner function

 

  function fetchMovies(page) {

    const endpoint = `${API_URL}popular?api_key=${API_KEY}&language=en-US&page=${page}`;

 

    fetch(endpoint)

      .then(response => response.json())

      .then(response => {

        setMovies(response.results);

        setMainMovieImage(response.results[0]);

      });

}

 

 

4. 코드 정리

 

 useEffect(() => {

    const page = 1;

    fetchMovies(page);

  }, []);

 

  // [Button] ===================================================

  const loadMoreItems = () => {

    fetchMovies(2);

};

 

 

** 기존 페이지도 유지하고 [더보기] 시 아래 페이지 추가

기존

 

  function fetchMovies(page) {

    const endpoint = `${API_URL}popular?api_key=${API_KEY}&language=en-US&page=${page}`;

 

    fetch(endpoint)

      .then(response => response.json())

      .then(response => {

        console.log(response);

        setMovies(response.results);

        setMainMovieImage(response.results[0]);

      });

}

 

더보기클릭 시

새로운 페이지링크 정보가 setMovies되면서 기존 Movies의 배열 데이터가 사라짐

 

따라서 기존 Movies의 데이터도 Movies안에 누적되어 들어가게,

전개연산자를 추가하고 하나의 배열로 넣어준다.

 

    fetch(endpoint)

      .then(response => response.json())

      .then(response => {

        console.log(response);

        setMovies([...Movies, ...response.resultss]);

        setMainMovieImage(response.results[0]);

      });

 

 

- 계속해서 다음페이지가 로드되게 하기

 

const [CurrentPagea, setCurrentPagea] = useState(0);

 

현재 상태값을 만들고

 

    fetch(endpoint)

      .then(response => response.json())

      .then(response => {

        console.log(response);

        setMovies([...Movies, ...response.results]);

        setMainMovieImage(response.results[0]);

        setCurrentPage(response.page);

      });

 

fetch안에 응답 데이터의 .page값을 입력 후

 

 

  const loadMoreItems = () => {

    fetchMovies(CurrentPage + 1);

};

 

loadMoreItems (더보기버튼 클릭 시)  현재 페이지 값이 1씩 증가하도록

 

** 포스터 이미지 클릭 시 영화 상세정보페이지(다른 컴포넌트) 연결

App.jsRouter 설정

 

  <div style={{ minHeight: 'calc(100vh - 80px)' }}>

        <Router>

          <Routes>

            {/* 요청된 경로로 페이지 이동 : 특정 컴포넌트 실행 */}

            <Route path="/" element={<LandingPage />} />

            <Route path="/movie" element={<Detail />} />

            <Route path="/Items" element={<Items />} />

          </Routes>

        </Router>

      </div>

 

 

GridCard.js

 

const GridCard = (props) => {

  return (

    <Col xs={24} sm={12} md={8} lg={6}>

      <div>

        <a href={`/movie/${props.id}`}>

          <img

            style={{ width: '100%' }}

            src={props.path}

            alt={props.title}

          />

        </a>

      </div>

    </Col>

  )

}

 

a태그의 링크를 /movie/{특정영화의 id}url 지정해주고

클릭 시

url 이동

 

** 위에서 설정한 url Detail 컴포넌트로 연결

App.js

 

<Route path="/movie/:movieId" element={<Detail />} />

 

[리액트 문법]

“/:변수명” -> :(콜론) 뒤에오는 이름은 변수 취급하여 모두 받아준다

 

Detail.js 컴포넌트 페이지로 연결

 

** Detail.js 상세정보 페이지로 만들기

LandingPage의 변수(데이터) movieId를 가져와 Detail에서 사용하기

 

1. react-router 설치 필요

client > yarn add react-router

2. useParams 사용 : LandingPage movieId 가져오기

(Params : Parameters)

import { useParams } from "react-router"

 

const Detail = () => {

const { movieId } = useParams();  // 다른 컴포넌트의 변수(데이터) 가져오기

 

 

3. state 상태값과 useEffect() 설정

 

  // [state] ============================================================

  const [Movie, setMovie] = useState({}) // 상세정보는 {객체}

 

  useEffect(() => {

    // [영화 정보]

https://api.themoviedb.org/3/movie/{영화아이디}?api_key={}&language=en-US

    const endpointInfo = `${API_URL}${movieId}?api_key=${API_KEY}`;

    console.log(endpointInfo);

 

    fetch(endpointInfo)

      .then(response => response.json())

      .then(response => {

        console.log(response)

        setMovie(response)

      })

}, []);

 

 

** Detail page element 생성

 

MovieInfo.js (영화정보 테이블) : Description

 

import React from 'react'

import { Descriptions } from 'antd';

 

const MovieInfo = (props) => {

  const { movie } = props ================= Info

  console.log("movie : ", movie);

 

  const items = [

    { key: '1', label: 'Title', children: `${movie.title}` },

    { key: '2', label: 'Release Date', children: `${movie.release_date}` },

    { key: '3', label: 'Revenue', children: `${movie.revenue}` },

    { key: '4', label: 'Runtime', children: `${movie.runtime}` },

    { key: '5', label: 'Vote Average', children: `${movie.vote_average}` },

    { key: '6', label: 'Vote Count', children: `${movie.vote_count}` },

    { key: '7', label: 'Status', children: `${movie.status}` },

    { key: '8', label: 'Popularity', children: `${movie.popularity}}` },

    { key: '9', label: 'Budget', children: `${movie.budget}` },

 

  ]

 

  return (

    <Descriptions title="Movie Info" bordered items={items} />

  )

}

 

export default MovieInfo

 

 

Detail에서 Movie 정보 MovieInfo로 가져오기

        {/* Movie Info */}

        <MovieInfo movie = {Movie} /> =============== Info

 

** 영화목록 Button 클릭으로 이전페이지로 연결

 

import { useNavigate } from 'react-router';

 

 

 

 

const Detail = () => {

const navigate = useNavigate(); // 이동할 url입력함수

 

  // [영화목록 버튼클릭] =========

  function handleUseHistory() {

    navigate("/") // ‘/경로

  };

 

  return (

      <div style={{ textAlign: 'center', marginTop: '20px', }}>

        <Button onClick={handleUseHistory}>영화 목록</Button>

      </div>

 

 

** 배우목록 나오게하기

 

배우 정보 API

https://api.themoviedb.org/3/movie/{영화아이디}/credits?api_key={}

 

useEffect(() => {

 

    // [출연진] https://api.themoviedb.org/3/movie/{영화아이디}/credits?api_key={}

 

    const endpointCrew = `${API_URL}${movieId}/credits?api_key=${API_KEY}`;

    // console.log('endpointCrew', endpointCrew);

    fetch(endpointCrew)

      .then(response => response.json())

      .then(response => {

        console.log(response)

      })

 

}, []);

 

 

cast: 배우정보, crew: 스태프정보, id

state 설정후 값 설정

 

// [state] =====================================================

const [Cast, setCast] = useState([]) // cast 배우정보는 [배열]

 

  useEffect(() => {

    fetch(endpointCrew)

      .then(response => response.json())

      .then(response => {

        console.log(response)

        setCast(response.cast) // 배우정보만 필요하므로 .cast

      })

  }, []);

 

 

** Grid Card (Detail.js) (Row, GridCard import)

 

        {/* Cast Grid Card */}

        <Row gutter={[10, 10]}>

            {Casts.map((cast, index) => {

              return (

                <React.Fragment key={index}>

                  <GridCard

                    path={`${IMAGE_BASE_URL}w400${cast.profile_path}`}

                    castName={cast.name}

                  />

                </React.Fragment>

              );

            })}

          </Row>

 

 

** Grid Card를 사용위치에 따라 나누기

 

const GridCard = (props) => {

  if (props.landingPage) {

    //// [LandigPage] 처리 ================

    return (

      <Col xs={24} sm={12} md={8} lg={6}>

        <div>

          <a href={`/movie/${props.movieId}`}>

            <img

              style={{ width: '100%' }}

              src={props.path}

              alt={props.title}

            />

          </a>

        </div>

      </Col>

    )

  } else {

    //// [Detail] 처리 ================

    return (

      <Col xs={24} sm={12} md={8} lg={6}>

        <div>

          <img

            style={{ width: '100%' }}

            src={props.path}

            alt={props.castName}

          />

        </div>

      </Col>

    )

  }

}

 

** Detail에서 Casts용은 <a> 태그도 삭제 props로 받아주는 요소도 변경

 

*** 배우목록 Button 토글Toggle 상태로 만들기

[state]

 

  const [ActorToggle, setActorToggle] = useState(false) // 초기값 false

 

 

[handler 함수 사용]

 

  // [배우목록 버튼 클릭] ========

  function toggleActorView() {

    setActorToggle(!ActorToggle); // !저장되어 있는 값을 반전

    // false <-> true

  };

 

 

[Cast Grid Card에 조건 지정]

 

        {/* Cast Grid Card */}

        {ActorToggle && // ActorToggle true여야 실행(element 생성)

          <Row gutter={[10, 10]}>

            {Casts.map((cast, index) => {

              return (

                <React.Fragment key={index}>

                  <GridCard

                    path={`${IMAGE_BASE_URL}w400${cast.profile_path}`}

                    castName={cast.name}

                  />

                </React.Fragment>

              );

            })}

          </Row>

        }

 

 

 

 

 

 

 

 

 

 

 

 

                                                                    

반응형