[youtube] tailwind grid auto-fill, router, axios 리팩토링
tailwind에서 auto-fill쓰기
// tailwind.config.js
module.exports = {
content: ["./src/**/*.{html,js,jsx,ts,tsx}"],
theme: {
gridTemplateColumns: {
"fill-auto": "repeat(auto-fill, minmax(15rem, 1fr))",
},
},
plugins: [require("@tailwindcss/line-clamp")],
};
쓰고 적용할 곳에
https://github.com/tailwindlabs/tailwindcss/issues/1403
[Feature request] CSS Grid - Auto-Fit & Auto-Fill · Issue #1403 · tailwindlabs/tailwindcss
So happy that Tailwind finally have css grid support built-in 👍 The only thing i've been missing, when working with tailwinds grid utilities are Auto-Fit & Auto-Fill Any plans to add them i...
github.com
해야하는 것, 채널아이디로 채널 정보 가져와서 썸네일 보여주기
- header 태그 쓰기
- Home 이동시 검색창 비우기
- input에 type="text"쓰기
- 검색화면 뒤로가기: keyword가 있다면 text 업데이트 해주기
- body { 100vw 100vh }
- #root { @apply w-full max-w-screen-2xl } 하면 내가 약간 끙끙대던 문제 한 번에 해결...
- response를 받아서 json()한 뒤 거기서 data를 리턴하거나 data. ... 의 하위 아이템을 리턴하거나
- api는 분리하기
- # (private) 내부적으로 호출
c++때처럼 클래스 인스턴스 생성 후에 내장 함수 쓰는 방식...
강의를 들으면서 내가 react-router를 잘 못쓰고 있었다는 것을 깨달았다.
app.js에 해도 상관이 없는건진 모르겠는데 강의에서는 index.js에 router를 설정했다.
구조도 좀 이상하게 짜놨다.
videos 통일성을 주는 게 좋을 것 같다... 난 근데 주소도 여러개고 컴포넌트도 여러개
주소라도 좀 예쁘게 고쳐봐야겠다.
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement: (
<div className="mx-auto h-screen bg-slate-700 text-white">
미안해요🥲오류가 났어요
</div>
),
children: [
{
index: true,
element: <Home />,
},
{
path: "/videos",
element: <Home />,
},
{
path: "/videos/:keyword",
element: <SearchVideo />,
},
{
path: "/videos/watch/:id",
element: <VideoDetail />,
},
],
},
]);
이렇게 수정했다.
element <Videos /> 로 활용 못 하고 searchVideo로 나눠놓은게 아쉽다.
그리고 제일 이해할 수 없었던 부분,
나는 graphQL위주로..배우기도 했고...이건 변명이지만...
직접 fetch해오는 것에 낯설다. 근데 거기에 axios를 추가한... 그리고 Fake랑 찐 두 개로 나뉘는...
일단 컴포넌트에선 통신을 하지 않고 따로 분리하는 게 좋다는 건 알겠다.
일단 src 폴더 밑에 api 폴더를 만든다.
그 안에 youtube.js 파일을 생성
// src/api/youtube.js
import axios from "axios";
export async function search(keyword) {
return axios.get(`/videos/${keyword ? 'search' : 'popular'}.json`)
.then((res)=> res.data.items)
}
음...그리고 콜백함수로 전달해주면 되는데 나는 popular랑 search 쿼리를 다르게 쓰고 있는 것 같다.ㅎㅎ
아예 컴포넌트가 다르니까.... 으아아아앙아 ㅠㅠㅠ
쓰면서도 이상하긴 했어...
// Home.jsx
import React from "react";
import Video from "../video/Video";
import { useQuery } from "react-query";
import Loading from "../../commons/loading/Loading";
export default function Home() {
const apiKey = process.env.REACT_APP_YOUTUBE_API_KEY;
const {
isLoading,
error,
data: popularVideos,
} = useQuery(
["videos", "popular"],
async () => {
return fetch(
`https://youtube.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular®ionCode=KR&maxResults=25&key=${apiKey}`
).then((res) => {
return res.json();
});
},
{
staleTime: 1000 * 60 * 3,
}
);
// console.log("popular::::", popularVideos);
return (
<div className="w-full bg-neutral-900 text-white">
{isLoading && <Loading />}
<div className="grid grid-cols-fill-auto gap-2">
<Video category={popularVideos} />
</div>
</div>
);
}
// SearchVideo.jsx
import React, { useEffect } from "react";
import { useQuery } from "react-query";
import { useParams } from "react-router-dom";
import Loading from "../../commons/loading/Loading";
import Video from "../video/Video";
export default function SearchVideo() {
const { keyword } = useParams();
const apiKey = process.env.REACT_APP_YOUTUBE_API_KEY;
useEffect(() => {
searchRefetch();
}, [window.location.pathname]);
const {
isLoading,
error,
data: searchVideos,
refetch: searchRefetch,
} = useQuery(
["videos", keyword],
async () => {
return fetch(
`https://youtube.googleapis.com/youtube/v3/search?part=snippet&maxResults=25&q=${keyword}&key=${apiKey}`
).then((res) => {
return res.json();
});
},
{
staleTime: 1000 * 60 * 3,
enabled: false,
}
);
return (
<div className="w-full bg-neutral-900 text-white">
{isLoading && <Loading />}
<div className="grid grid-cols-fill-auto gap-2">
<Video category={searchVideos} />
</div>
</div>
);
}
지금 이렇게 되어있는데, 둘이 합쳐야할 것 같다.
오싹,,,,,,
기나오싹,,,
합치는 건 쉬웠다. 이름은... Videos로 바꾸면 좋을 것 같지만 일단 냅뒀다.
import React from "react";
import Video from "../video/Video";
import { useQuery } from "react-query";
import Loading from "../../commons/loading/Loading";
import SearchVideo from "../search_video/SearchVideo";
import { useParams } from "react-router-dom";
export default function Home() {
const { keyword } = useParams();
const {
isLoading,
error,
data: videos,
} = useQuery(["videos", keyword], async () => SearchVideo(keyword), {
staleTime: 1000 * 60 * 3,
});
// console.log("popular::::", popularVideos);
return (
<div className="w-full bg-neutral-900 text-white">
{isLoading && <Loading />}
<div className="grid grid-cols-fill-auto gap-2">
<Video category={videos} />
</div>
</div>
);
}
// src/api/Youtube.js
import axios from "axios";
const apiKey = process.env.REACT_APP_YOUTUBE_API_KEY;
export default class Youtube {
constructor() {
this.httpClient = axios.create({
baseURL: "https://www.googleapis.com/youtube/v3",
params: { key: apiKey },
});
}
async search(keyword) {
return keyword ? this.#searchByKeyword(keyword) : this.#mostPopular();
}
async #searchByKeyword(keyword) {
return this.httpClient
.get(`search`, {
params: {
part: "snippet",
maxResults: 25,
type: "video",
q: keyword,
},
})
.then((res) => res.data.items)
.then((items) => items.map((item) => ({ ...item, id: item.id.videoId })));
}
async #mostPopular() {
return this.httpClient
.get(`videos`, {
params: {
part: "snippet",
maxResults: 25,
chart: "mostPopular",
},
})
.then((res) => res.data.items);
}
}
axios.create()로 인스턴스 생성
axios.create([config])
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
headers: { 'X-Custom-Header': 'foobar' },
timeout: 1000,
});
인스턴스 메서드 .get, .post, .put 등을 쓸 수 있다.
API | Axios 러닝 가이드
API 구성(configuration) 설정을axios()에 전달하여 요청할 수 있습니다. axios(config) axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } }); 1 2 3 4 5 6 7 8 9 axios({ method:'get', url:'http://bit
yamoo9.github.io
// Home.jsx
const {
isLoading,
error,
data: videos,
} = useQuery(
["videos", keyword],
() => {
const youtube = new Youtube();
return youtube.search(keyword);
},
{
staleTime: 1000 * 60 * 3,
}
);