[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.js의 Router 설정
<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>
}
'응용SW 개발 (React) > 필기일지' 카테고리의 다른 글
231031 [응용SW개발] - React SPA 미니 프로젝트 점검 / DB 연동 기초 개념 / mongo DB / express (1) | 2023.10.31 |
---|---|
231030 [응용SW개발] 리액트 SPA 실습 (0) | 2023.10.31 |
231026 [응용SW개발] - API (TMDB 실습) / React API 활용 (useEffect, useState, grid, Fragments 등) (0) | 2023.10.26 |
231025 [응용SW개발] - 컴포넌트 component / 외부 컴포넌트 사이트 (디자인요소) / API (0) | 2023.10.25 |
231024 [응용SW 개발] -React 관련 기반 설치 (0) | 2023.10.25 |