Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";

import Layout from "./Layout.jsx";
import MainPage from "./page/MainPage.jsx";
import DetailPage from "./page/DetailPage.jsx";
import MainPage from "./pages/MainPage.jsx";
import DetailPage from "./pages/DetailPage.jsx";

import { createGlobalStyle } from "styled-components";

Expand Down
2 changes: 0 additions & 2 deletions src/Layout.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { Outlet } from "react-router-dom";
import NavigationBar from "./component/NavigationBarComponent";

export default function Layout() {
return (
<div>
<NavigationBar />
<div>
<Outlet />
</div>
Expand Down
File renamed without changes
37 changes: 21 additions & 16 deletions src/Page/DetailPage.jsx → src/pages/DetailPage.jsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
import styled from "styled-components";

import NavigationBar from "./components/NavigationBar";

import { useParams } from "react-router-dom";

import useDetailApi from "../Api/DetailApi";
import useTmdbDetailData from "./data/hooks/useTmdbDetailData";

// <-------------------- function -------------------->

export default function DetailPage() {
const { id } = useParams();

const detailApi = useDetailApi(id);
const tmdbDetail = useTmdbDetailData(id);

// <-------------------- return -------------------->

return (
<Detail>
<Poster
className="poster"
src={`https://image.tmdb.org/t/p/w500${detailApi.backdrop_path}`}
alt={detailApi.title}
/>
<Info>
<TitleRating>
<Title>{detailApi.title}</Title>
<Rating>{detailApi.vote_average}</Rating>
</TitleRating>
<Overview>{detailApi.overview}</Overview>
</Info>
</Detail>
<>
<NavigationBar />
<Detail>
<Poster
className="poster"
src={`https://image.tmdb.org/t/p/w500${tmdbDetail.backdrop_path}`}
alt={tmdbDetail.title}
/>
<Info>
<TitleRating>
<Title>{tmdbDetail.title}</Title>
<Rating>{tmdbDetail.vote_average}</Rating>
</TitleRating>
<Overview>{tmdbDetail.overview}</Overview>
</Info>
</Detail>
</>
);
}

Expand Down
39 changes: 25 additions & 14 deletions src/Page/MainPage.jsx → src/pages/MainPage.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
import styled from "styled-components";
import Card from "./components/Card.jsx";

import CardComponent from "../Component/CardComponent.jsx";
import useTopApi from "../Api/TopApi.js";
import useMainApi from "../Api/MainApi";
import useTmdbTopData from "./data/hooks/useTmdbTopData.js";
import useTmdbMainData from "./data/hooks/useTmdbMainData.js";
import useTmdbKeywordData from "./data/hooks/useTmdbKeywordData.js";

import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation, Pagination, Autoplay } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";

import { useSearchParams } from "react-router-dom";
import NavigationBar from "./components/NavigationBar.jsx";

// <-------------------- function -------------------->

export default function MainPage() {
const topApi = useTopApi();
const mainApi = useMainApi();
const [searchParams] = useSearchParams();
const query = searchParams.get("keyword")?.trim();

const tmdbTop = useTmdbTopData();
const tmdbMainData = useTmdbMainData();
const tmdbKeywordData = useTmdbKeywordData(query);

const tmdbData = query ? tmdbKeywordData : tmdbMainData;

// <-------------------- return -------------------->

return (
<>
<NavigationBar />
<Container>
<Top10>TOP 10 🏆</Top10>
<Top10>🏆 TOP 10</Top10>
<Swiper
modules={[Navigation, Pagination, Autoplay]}
navigation
Expand All @@ -33,19 +44,19 @@ export default function MainPage() {
speed={600}
className="topSwiper"
>
{topApi.slice(0, 10).map((api) => (
{tmdbTop.slice(0, 10).map((api) => (
<SwiperSlide>
<CardComponent movie={api} key={api.id} />
<Card movie={api} key={api.id} />
</SwiperSlide>
))}
</Swiper>

<Popular>Popular ✨</Popular>
<Mainapi>
{mainApi.map((api) => (
<CardComponent movie={api} key={api.id} />
<Popular>✨ Popular</Popular>
<MainList>
{tmdbData.map((movie) => (
<Card movie={movie} key={movie.id} />
))}
</Mainapi>
</MainList>
</Container>
</>
);
Expand All @@ -69,7 +80,7 @@ const Popular = styled.p`
padding-left: 100px;
`;

const Mainapi = styled.div`
const MainList = styled.div`
display: flex;
flex-wrap: wrap;
justify-content: center;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { Link } from "react-router-dom";

// <-------------------- function, return -------------------->

export default function CardComponent({ movie }) {
export default function Card({ movie }) {
return (
<Card>
<Container>
<Link to={`/details/${movie.id}`}>
<Poster
src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
Expand All @@ -16,13 +16,13 @@ export default function CardComponent({ movie }) {
<Title>{movie.title}</Title>
<Rating>{movie.vote_average}</Rating>
</Info>
</Card>
</Container>
);
}

// <-------------------- styled-components -------------------->

const Card = styled.div`
const Container = styled.div`
width: 18vw;
max-width: 200px;
min-width: 150px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
import styled from "styled-components";
import { Link } from "react-router-dom";
import { Link, useSearchParams } from "react-router-dom";
import { useEffect, useState } from "react";

import useDebounce from "./hooks/useDebounce.js";
import useTmdbKeywordData from "../data/hooks/useTmdbKeywordData.js";

// <-------------------- function -------------------->

export default function NavigationBar() {
const [searchParams, setSearchParams] = useSearchParams();
const keywordParam = searchParams.get("keyword") || "";
const [keyword, setKeyword] = useState(keywordParam);

const debouncedKeyword = useDebounce(keyword, 100);
useTmdbKeywordData(debouncedKeyword);

useEffect(() => {
if (keyword.trim() === "") {
const params = new URLSearchParams(searchParams);
params.delete("keyword");
setSearchParams(params);
} else {
setSearchParams({ keyword });
}
}, [keyword, searchParams, setSearchParams]);

// <-------------------- return -------------------->

return (
<Navigationbar>
<Link to="/" style={{ textDecoration: "none", color: "white" }}>
<Logo>🎬 • WISH MOVIE</Logo>
</Link>

<SearchBox>
<input type="text" placeholder="tell me your wish 🧞‍♂️" />
<input
type="text"
placeholder="tell me your wish 🧞‍♂️"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
/>
</SearchBox>
<Buttons>
<LoginBtn>로그인</LoginBtn>
Expand All @@ -18,6 +49,8 @@ export default function NavigationBar() {
);
}

// <-------------------- styled-components -------------------->

const Navigationbar = styled.nav`
width: 100%;
height: 60px;
Expand Down Expand Up @@ -55,6 +88,18 @@ const SearchBox = styled.div`
}
`;

const SearchResults = styled.div`
position: absolute;
top: 70px;
background-color: #111;
width: 300px;
max-height: 400px;
overflow-y: auto;
color: white;
border-radius: 8px;
padding: 10px;
`;

const Buttons = styled.div`
display: flex;
align-items: center;
Expand Down
17 changes: 17 additions & 0 deletions src/pages/components/hooks/useDebounce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useEffect, useState } from "react";

export default function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
clearTimeout(handler);
};
}, [value, delay]);

return debouncedValue;
}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useEffect, useState } from "react";
import axios from "axios";

const apiToken = import.meta.env.VITE_TMDB_ACCESS_TOKEN;
const apiToken = import.meta.env.VITE_TMDB_API_KEY;

// <-------------------- function -------------------->

export default function useDetailApi(id) {
const [detailApi, setDetailApi] = useState([]);
// console.log(movieId);
export default function useTmdbDetailData(id) {
const [tmdbDetailData, setTmdbDetailData] = useState([]);

// <-------------------- API : Details

Expand All @@ -25,12 +24,12 @@ export default function useDetailApi(id) {
axios
.request(options)
.then((res) => {
setDetailApi(res.data);
setTmdbDetailData(res.data);
})
.catch((err) => console.error(err));
}, []);

// <-------------------- return -------------------->

return detailApi;
return tmdbDetailData;
}
35 changes: 35 additions & 0 deletions src/pages/data/hooks/useTmdbKeywordData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useEffect, useState } from "react";
import axios from "axios";

const apiToken = import.meta.env.VITE_TMDB_API_KEY;

export default function useTmdbKeywordData(keyword) {
const [search, setSearch] = useState([]);

useEffect(() => {
console.log(keyword);

if (!keyword) return;

const options = {
method: "GET",
url: `https://api.themoviedb.org/3/search/movie`,
params: {
query: keyword,
language: "ko-KR",
page: 1,
},
headers: {
accept: "application/json",
Authorization: `Bearer ${apiToken}`,
},
};

axios
.request(options)
.then((res) => setSearch(res.data.results))
.catch((err) => console.error(err));
}, [keyword]);

return search;
}
12 changes: 6 additions & 6 deletions src/Api/MainApi.js → src/pages/data/hooks/useTmdbMainData.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { useEffect, useState } from "react";

// <-------------------- function -------------------->

export default function useMainApi() {
const [mainApi, setMainApi] = useState([]);
export default function useTmdbMainData(keyword) {
const [tmdbMainData, setTmdbMainData] = useState([]);

// <-------------------- API : popular

useEffect(() => {
const apiToken = import.meta.env.VITE_TMDB_ACCESS_TOKEN;
const apiToken = import.meta.env.VITE_TMDB_API_KEY;
const options = {
method: "GET",
headers: {
Expand All @@ -27,12 +27,12 @@ export default function useMainApi() {
(resData) => resData.adult === false
);
// console.log("✅ TMDB 응답 데이터:", filteredTmdbMovies);
setMainApi(noAdultResData);
setTmdbMainData(noAdultResData);
})
.catch((err) => console.error(err));
}, []);
}, [keyword]);

// <-------------------- return -------------------->

return mainApi;
return tmdbMainData;
}
Loading