Redux는 props넘길때 자동으로 훨씬 편하게 만들어 준다.
Redux로 로그인 로그아웃 하기
uid를 로컬 스토리지에 담아주기
로그인 후 로그아웃도 구현한다.
그런데 로그인이나 로그아웃하고 페이지 새로고침 하고싶다.
원래는 useNavigate 훅을 사용하는게 좋다.
그런데 여기서는 안된다.
그래서 window.location.reload()로 처리했다.
컴포넌트 쪼개기 연습-youtubeAPI
https://developers.google.com/youtube/v3/getting-started?hl=ko
시작하기 | YouTube Data API | Google Developers
시작하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 소개 이 문서는 YouTube와 상호작용할 수 있는 애플리케이션을 개발하려는 개발자를 위해 작성되었
developers.google.com
Youtube 인기 동영상 25개 가져오기
import axios from "axios";
import { useEffect, useState } from "react";
import "./App.css";
import VideoList from "./components/VideoList";
const App =()=> {
console.log('App');
const [videos,setVideos]=useState([])//초기화에 []배열기호 넣어야한다. 배열들이 들어옴
const [params, setParams]=useState({
part:'snippet',
chart:'mostPopular',
maxResults:25,
regionCode:'US',
key:'비밀^^'
})
useEffect(()=>{
console.log('Effect');
axios.get('https://youtube.googleapis.com/youtube/v3/videos?',{params})
.then(result=>{//콜백...then
console.log(result.data.items);//배열
setVideos(result.data.items)//배열을 담아줌
}).catch(error=>console.log(error))//catch콜백함수로 오류 잡기
},[])//의존성 배열 안넣으면 무한루프 돈다!! useSate의 인자를 의존성 배열에 넣는다=>리렌더링이 일어나는 조건
console.log(videos);
return (
<>{/* 루트니까 fragment권장. 중첩 안된다. */}
<VideoList videos={videos}/>
</>
);
}
export default App;
import React from 'react'
import styled from 'styled-components';
import VideoItem from './VideoItem';
const UL=styled.ul`
display:flex;
flex-wrap:wrap;
list-style:none;
padding-left:0;
magin:0;
`
const VideoList = ({videos}) => {//videos를 받아와서 구조분해할당?
console.log(videos);
return (
<UL>
{//리액트문법
videos.map(video=>(//맵에서는 ()소괄호
<VideoItem key={video.id} video={video}/>
))
}
</UL>
)
}
export default VideoList
import React from 'react'
import styled from 'styled-components'
const LI=styled.li`
width:50%;
padding:0.2em;
`
const VIVDEODIV=styled.div`
width:100%;
height:100%;
display:flex;/* 비디오가 한줄에 나오게 함 */
align-items:center;
border:1px solid lightgray;
box-shadow:3px 3px 5px 0px rgba(191,191,191,0.55);/* 그림자 효과 */
cursor:pointer;
transition: transform 250ms easy-in;/*자연스럽게 처리 */
&:hover{
transform:scale(1.02);
}
`
const IMG=styled.img`
width:40%;
height:100%;
`
const DIV=styled.div`
margin-left:0.3em;
`
const PTITLE=styled.p`
margin:10;
font-size:0.8rem;
`
const PCHANNEL=styled.p`
margin:0;
font-size
`
const VideoItem = (props) => {//vidoe를 구조분해할당함
return (
<LI>
<VIVDEODIV>
<div>
<IMG src={props.video.snippet.thumbnails.medium.url} alt="video thumbnail"/>
<DIV>
<PTITLE>{props.video.snippet.title}</PTITLE>
<PCHANNEL>{props.video.snippet.channelTitle}</PCHANNEL>
</DIV>
</div>
</VIVDEODIV>
</LI>
)
}
export default VideoItem
유투브 페이지 분할하기
하나의 페이지로 구현을 해보자!
검색기능 넣기
이런 형식 너무 생소했다.
태그자리에 selectedVideo가 true이면 VideoDetail로 props를 보낸다는 뜻.
이 코드는 JSX로 작성되었으며 일반적으로 React와 함께 사용됩니다.
이 코드는 selectedVideo 변수의 진실성을 평가하여 선택된 비디오가 있는지 여부를 확인합니다. selectedVideo가 진실 값이면 (즉, null, undefined, false, 0 또는 빈 문자열이 아님), 선택한 비디오가 props로 전달되며 VideoDetail 컴포넌트가 렌더링됩니다.
selectedVideo가 거짓 값이면 아무것도 렌더링되지 않습니다.
전반적으로 이 코드는 선택한 비디오의 존재 여부에 따라 VideoDetail 컴포넌트를 조건부로 렌더링합니다.
import axios from 'axios'
import React, { useEffect, useState } from 'react'
import VideoList from '../VideoList'
import Form from 'react-bootstrap/Form';
import InputGroup from 'react-bootstrap/InputGroup';
import { Button } from 'react-bootstrap';
import VideoDetail from '../VideoDetail';
//기능 추가-사용자가 입력한 키워드를 관리-바뀔때마다 조회결과 달라져야
//1.검색기 추가
//2.비디오 선택시 상세페이지 처리
//
const YoutubePage = () => {
//사용자가 입력한 키워드 관리를 위해서 훅을 선언해야한다.
const [keyword, setKeyword]=useState("")
//상세화면 추가로 인한 훅 필요
const [selectedVideo, setSelectedVideo]=useState(null)
//비디오가 선택되면 상태값을 관리해야한다.
const videoSelect=(video)=>{//이벤트. List로넘기고 item으로 두단계 넘겨야...
setSelectedVideo(video)
}
const [videos,setVideos]=useState([])//초기화에 []배열기호 넣어야한다. 배열들이 들어옴
const [params, setParams]=useState({
part:'snippet',
chart:'mostPopular',
maxResults:25,
regionCode:'US',
key:'유투브API키'
})
const [params2, setParams2]=useState({//검색시의 데이터 가져오기 근데 이거 안쓴
part:'snippet',
type:'video',
maxResults:25,
// regionCode:'US',
q:`${"여행"}`,
key:'유투브API키'
})
useEffect(()=>{
console.log('Effect');
axios.get('https://youtube.googleapis.com/youtube/v3/videos?',{params})
.then(result=>{//콜백...then
console.log(result.data.items);//배열
setVideos(result.data.items)//배열을 담아줌
}).catch(error=>console.log(error))//catch콜백함수로 오류 잡기
},[params])//의존성 배열 안넣으면 무한루프 돈다!! useSate의 인자를 의존성 배열에 넣는다=>리렌더링이 일어나는 조건
const youtubeSearch=()=>{//함수 이름을 대문자로 하면 컴포넌트 취급을 받는다=화면이다/ 바벨번들러가... 주의하기!!
axios.get(`https://youtube.googleapis.com/youtube/v3/search?part=snippet&maxResults=25&q=${keyword}&type=video&key=AIzaSyCTPwHhUxcmNTC6iLNV1MdKqAaz535ltls`)
.then(result=>{//콜백...then
console.log(result.data.items);//배열
setVideos(result.data.items)//배열을 담아줌
}).catch(error=>console.log(error))//catch콜백함수로 오류 잡기
}
const changeKeyword=(event)=>{
console.log(event.target.value);
setKeyword(event.target.value)
}
return (
<div className='container'>
<div>
<h2>Youtube<small>도튜브</small></h2>
<hr/>
</div>
<InputGroup className="mb-3">
<Form.Control
placeholder="검색어"
aria-label="검색어"
aria-describedby="basic-addon2"
onChange={changeKeyword}
/>
<Button className='btn btn-danger' onClick={youtubeSearch}>Search</Button>
</InputGroup>
{
selectedVideo&&(
<div>
<VideoDetail video={selectedVideo}/>
</div>
)
}
<VideoList videos={videos} videoSelect={videoSelect}/>
</div>
)
}
export default YoutubePage
import React from 'react'
import styled from 'styled-components';
import VideoItem from './VideoItem';
const VideoListDiv=styled.div`
display:grid;
magin-top:10px;
grid-template-columns:repeat(auto-fill, minmax(320px, 1fr));
`
const VideoList = ({videos,videoSelect}) => {//videos를 받아와서 구조분해할당?
console.log(videos);
return (
<>
<VideoListDiv>
{//리액트문법
videos.map(video=>(//맵에서는 ()소괄호
<VideoItem key={video.id} video={video} videoSelect={videoSelect}/>
))
}
</VideoListDiv>
</>
)
}
export default VideoList
import React from 'react'
import styled from 'styled-components'
const VideoLi=styled.li`
width:50%;
padding:0.2em;
list-style:none;
`
const VideoCard=styled.div`
width:100%;
height:100%;
display:flex;/* 비디오가 한줄에 나오게 함 */
align-items:center;
border:1px solid lightgray;
box-shadow:3px 3px 5px 0px rgba(191,191,191,0.55);/* 그림자 효과 */
cursor:pointer;
transition: transform 250ms easy-in;/*자연스럽게 처리 */
&:hover{
transform:scale(1.02);
}
`
const VideoThumbnail=styled.img`
width:40%;
height:100%;
`
const VideoInfo=styled.div`
margin-left:0.3em;
`
const Ptitle=styled.p`
margin:10;
font-size:0.8rem;
`
const Pchannel=styled.p`
margin:0;
font-size
`
const VideoItem = (props) => {//vidoe를 구조분해할당함
const {video, videoSelect}=props
//첫번째 파라미터는 비디오 한건에 대한 정보
//두번째 파라미터는 선택된 비디오의 이벤트 처리 함수의 주소번지를 받아서
//VideoLi가 클릭 되었을때 파라미터로 video한건의 주소번지를 담아서
//부모에서 정의된 이벤트 처리 함수를 호출한다.
//VideoList에서 이벤트 처리를 마무리 하지 않고 props로 넘기는 이유가 뭔가요?
//VideoList에서는 n건을 가지고 있고 이 중에서 어떤 비디오 클립이 선택 되었는지 알 수 없으니까...
//이벤트 소스 클립은 리스트에 있지만 선택된 비디오 한건에 대한 정보는 VideoItem에서 결정 된다.
//그러니까 비디오 한건에 대한 정확한 정보를 알고 있는 자손 컴포넌트인 VideoItem에서
//부모가 가진 함수의 주소번지를 props로 받고 이벤트 호출은 VideoItem에서 처리 해야 함
return (
<VideoLi onClick={()=>videoSelect(video)}>
<VideoCard>
<div>
<VideoThumbnail src={props.video.snippet.thumbnails.medium.url} alt="video thumbnail"/>
<VideoInfo>
<Ptitle>{props.video.snippet.title}</Ptitle>
<Pchannel>{props.video.snippet.channelTitle}</Pchannel>
</VideoInfo>
</div>
</VideoCard>
</VideoLi>
)
}
export default VideoItem
import React from 'react'
const VideoDetail = ({video}) => {
console.log(video);
return (
<>
<section>
<iframe
title="video play" type="text/html" width="100%" height="500px"
src={`http://www.youtube.com/embed/${video.id.videoId}`}
frameborder="0" allowFullScreen></iframe>
</section>
</>
)
}
export default VideoDetail
'학원수업 > 4월' 카테고리의 다른 글
04/11 국비학원 개발자 과정 - (0) | 2023.04.11 |
---|---|
04/07 국비학원 개발자과정 91회차수업 - Redux, CSS 로 네임카드 만들기 (0) | 2023.04.07 |
04/05 국비과정 개발자 89회차 수업- QnA게시판 글 상세보기, 이미지 다운, 글수정 (0) | 2023.04.05 |
04/04 국비학원 개발자과정 - 88회차 React, Spring QnA게시판 (0) | 2023.04.04 |
04/03 국비학원 개발자과정 87회차- React와 Spring연결 (0) | 2023.04.03 |
댓글