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
23 changes: 7 additions & 16 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
body {
background-color: #e2e8f0;
margin: 0;
font-family: "Noto Sans KR", sans-serif;
font-family: "Pretendard", "Noto Sans KR", sans-serif;
}

.App {
Expand All @@ -10,7 +10,7 @@ body {
}

.page-title {
font-size: 24px;
font-size: 30px;
font-weight: bold;
color: #8c02e2;
margin-bottom: 20px;
Expand All @@ -24,31 +24,22 @@ body {
}

.movie-card {
width: 120px;
background-color: #308cca;
height: auto;
width: 150px;
background-color: #9c47e7;
color: white;
border-radius: 6px;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 10px;
padding-bottom: 15px;
transition: transform 0.2s ease;
}

.movie-card:hover {
transform: scale(1.05);
}

.poster-box {
width: 100%;
height: 160px;
background-color: #1b6ca8;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}

.poster-box img {
width: 100%;
height: 100%;
Expand Down
70 changes: 46 additions & 24 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import "./App.css";
import React, { useState } from "react";
import MovieCard from "./component/MovieCard";
import { Link } from "react-router-dom";
import { Link, useSearchParams } from "react-router-dom";
import { useEffect } from "react";

function App() {
const [movies, setMovies] = useState([]);
const [loading, setLoading] = useState(false);
const [searchParams] = useSearchParams();
const query = searchParams.get("query");

useEffect(() => {
const fetchMovies = async () => {
setLoading(true);
try {
const options = {
method: "GET",
Expand All @@ -18,44 +22,62 @@ function App() {
},
};

const response = await fetch(
"https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc",
options
);
let url = "";

if (query && query.trim() !== "") {
url = `https://api.themoviedb.org/3/search/movie?query=${encodeURIComponent(
query
)}&include_adult=false&language=en-US&page=1`;
} else {
url =
"https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc";
}

const response = await fetch(url, options);

const data = await response.json();

const filteredMovies = data.results.filter(
(movie) => movie.adult === false
);
const filteredMovies = data.results
? data.results.filter((movie) => movie.adult === false)
: [];

setMovies(filteredMovies);
} catch (error) {
console.error("데이터 불러오는 중 오류가 발생했습니다", error);
} finally {
setLoading(false);
}
};
fetchMovies();
}, []);
}, [query]);

return (
<>
<div className="App">
<h1 className="page-title">MovieCard</h1>
<div className="movie-list">
{movies.map((movie) => (
<Link
to={`/details/${movie.id}`}
key={movie.id}
className="movie-link"
>
<MovieCard
title={movie.title}
posterPath={movie.poster_path}
voteAverage={movie.vote_average}
/>
</Link>
))}
</div>
{loading ? (
<p>로딩중</p>
) : (
<div className="movie-list">
{movies.length > 0 ? (
movies.map((movie) => (
<Link
to={`/details/${movie.id}`}
key={movie.id}
className="movie-link"
>
<MovieCard
title={movie.title}
posterPath={movie.poster_path}
voteAverage={movie.vote_average}
/>
</Link>
))
) : (
<p>검색 결과가 없습니다.</p>
)}
</div>
)}
</div>
</>
);
Expand Down
13 changes: 7 additions & 6 deletions src/component/MovieDetail.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,20 @@
/* 상세 내용 영역 */
.detail-content {
display: flex;
align-items: flex-start;
align-items: center;
justify-content: center;
gap: 40px;
padding: 40px;
background-color: #111;
flex-wrap: wrap; /* ✅ 반응형 정렬 */
flex-wrap: wrap;
}

.poster-box img {
width: 300px;
height: auto;
width: 150px;
height: 250px;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
margin: 10px;
justify-self: start;
align-items: flex-start;
}

.info-box {
Expand Down
9 changes: 9 additions & 0 deletions src/component/NavBar.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,12 @@
display: flex;
align-items: center;
}

.logo-link {
text-decoration: none;
color: inherit;
}

.logo-link:hover {
opacity: 0.8;
}
28 changes: 26 additions & 2 deletions src/component/NavBar.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
import { Link } from "react-router-dom";
import { Link, useSearchParams } from "react-router-dom";
import "./NavBar.css";
import { useState } from "react";
import useDebounce from "../hooks/useDebounce";
import { useEffect } from "react";

function NavBar() {
const [input, setInput] = useState("");
const [searchParams, setSearchParams] = useSearchParams();
const debouncedInput = useDebounce(input, 500);

useEffect(() => {
const q = searchParams.get("query") || "";
setInput(q);
}, []);

useEffect(() => {
if (debouncedInput && debouncedInput.trim() !== "") {
setSearchParams({ query: debouncedInput.trim() });
} else {
setSearchParams({});
}
}, [debouncedInput, setSearchParams]);

return (
<nav className="navbar">
<div className="navbar-left">
<h1 className="logo">OZ무비</h1>
<Link to="/" className="logo-link">
<h1 className="logo">🎬 OZ무비</h1>
</Link>
</div>

<div className="navbar-center">
<input
type="text"
placeholder="영화를 검색해보세요"
className="search-bar"
value={input}
onChange={(e) => setInput(e.target.value)}
/>
</div>

Expand Down
12 changes: 12 additions & 0 deletions src/hooks/useDebounce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useEffect, useState } from "react";

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

useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);

return debouncedValue;
}
35 changes: 35 additions & 0 deletions src/media.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@media (max-width: 600px) {
.movie-list {
grid-template-columns: repeat(2, 1fr);
}

.page-title {
font-size: 1.5rem;
}
}

@media (min-width: 601px) and (max-width: 1024px) {
.movie-list {
grid-template-columns: repeat(3, 1fr);
}
}

@media (min-width: 1025px) {
.movie-list {
grid-template-columns: repeat(5, 1fr);
}
}

.navbar {
display: flex;
justify-content: space-between;
padding: 10px 20px;
background: #1e293b;
}

@media (max-width: 600px) {
.navbar-right {
display: flex;
gap: 10px;
}
}