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
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
node_modules
.env
.env
.prettierrc.json
babel.config.js
package-lock.json
package.json
README.md
tsconfig.json
webpack.config.js
7 changes: 4 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<title>MovieList</title>
</head>
<body>
여기에 기본 html 작성
<div id="app"></div>
<script src="src/index.js" type="module"></script>
</body>
</html>
29 changes: 29 additions & 0 deletions my-vue-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

assets
vite.config.js
eslint.config.js
vite.svg
16 changes: 16 additions & 0 deletions my-vue-app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Movie</title>
</head>
<body>
<div id="root"></div>
<div id="portal-root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

<!--
npm,pnpm,yarn -->
41 changes: 41 additions & 0 deletions my-vue-app/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
background-color: black;
}

#root {
height: 100%;
width: 100%;
margin: 0%;
display: grid;
grid-template-rows: 40px 30px 1780fr 129fr;
grid-template-columns: 100fr 650fr 100fr;
}

.section {
grid-column: 2/3;
grid-row: 3/4;
display: grid;
grid-template-rows: 60fr 750fr 108fr;

display: flex;
flex-direction: column;
}

@media (max-width: 1024px) {
#root {
height: 100%;
width: 100%;
margin: 0%;
display: grid;
grid-template-rows: 40px 30px 1780fr 129fr;
grid-template-columns: 10px 650fr 10px;
}
}

.loading {
color: white;
}
49 changes: 49 additions & 0 deletions my-vue-app/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useEffect } from 'react';
import Header from './header/Header';
import Title from './section/Title';
import Button from './section/Button';
import ShowMovie from './section/ShowMovie';
import useStore from './store/useStore';
import './App.css';
import { Suspense } from 'react';

function App() {
const { button, loadPopularMovies, search, loadSearchMovies } = useStore();

useEffect(() => {
loadPopularMovies(1);
}, [loadPopularMovies]);

useEffect(() => {
if (search === '') {
loadPopularMovies(1);
} else {
loadSearchMovies(1, search);
}
}, [search, loadPopularMovies, loadSearchMovies]);

const handleLoadMore = () => {
const nextPage = useStore.getState().page + 1;
if (search === '') loadPopularMovies(nextPage);
else loadSearchMovies(nextPage, search);
};

return (
<>
<Suspense fallback={<Loading />}>
<Header />
<div className="section">
<Title />
<ShowMovie />
{button && <Button onClick={handleLoadMore} />}
</div>
</Suspense>
</>
);
}

function Loading() {
return <h2 className="loading">🌀 Loading... </h2>;
}

export default App;
26 changes: 26 additions & 0 deletions my-vue-app/src/Config.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const BASE_URL = import.meta.env.VITE_BASE_URL;
export const IMAGE_URL = import.meta.env.VITE_IMAGE_URL;
export const DETAIL_URL = import.meta.env.VITE_DETAIL_URL;
export const options = {
method: 'GET',
headers: {
accept: 'application/json',
Authorization: `Bearer ${import.meta.env.VITE_AUTH_TOKEN_1}`,
},
};

export const options2 = {
method: 'GET',
headers: {
accept: 'application/json',
Authorization: `Bearer ${import.meta.env.VITE_AUTH_TOKEN_2}`,
},
};

export const options3 = {
method: 'GET',
headers: {
accept: 'application/json',
Authorization: `Bearer ${import.meta.env.VITE_AUTH_TOKEN_3}`,
},
};
20 changes: 20 additions & 0 deletions my-vue-app/src/Modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import ReactDOM from 'react-dom';
import './modal.css';

const Modal = ({ isOpen, onClose, children }) => {
if (!isOpen) return null;

return ReactDOM.createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<button className="modal-close" onClick={onClose}>
x
</button>
{children}
</div>
</div>,
document.getElementById('portal-root')
);
};

export default Modal;
44 changes: 44 additions & 0 deletions my-vue-app/src/api/API.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { BASE_URL, DETAIL_URL, options, options2, options3 } from '../Config.jsx';

async function fetchApi(endpoint, options) {
try {
const response = await fetch(`${BASE_URL}${endpoint}`, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.results;
} catch (error) {
console.error('API 호출 중 오류:', error);
return null;
}
}

async function fetchApi2(endpoint, options) {
try {
const response = await fetch(`${DETAIL_URL}${endpoint}`, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('API 호출 중 오류:222', error);
return null;
}
}

export function getPopularMovies(page) {
const endpoint = `/movie/popular?language=ko-KR&page=${page}`;
return fetchApi(endpoint, options);
}

export function getSearchMovies(page, input) {
const endpoint = `/search/movie?include_adult=false&language=ko-KR&page=${page}&query=${input}`;
return fetchApi(endpoint, options2);
}

export function getDetailMovie(id) {
const endpoint = `${id}?language=ko-KR`;
return fetchApi2(endpoint, options3);
}
25 changes: 25 additions & 0 deletions my-vue-app/src/header/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import './header.css';
import useStore from '../store/useStore';

function Header() {
const { setSearch } = useStore();

const handleKeyDown = (e) => {
if (e.key === 'Enter') {
setSearch(e.target.value);
}
};

const reloadPage = () => {
location.reload();
};

return (
<header className="header">
<img className="header__logo" src="/src/assets/logo.png" onClick={reloadPage} />
<input className="header__search" placeholder="검색" onKeyDown={handleKeyDown} />
</header>
);
}

export default Header;
38 changes: 38 additions & 0 deletions my-vue-app/src/header/header.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.header {
width: 100%;
display: flex;
grid-column: 1/4;
grid-row: 1/2;
align-items: center;
justify-content: space-between;
border-bottom: 1px rgb(172, 165, 165) solid;
box-shadow: 0px 2px 4px rgba(255, 255, 255, 0.5);
}

.header__logo {
width: 123px;
height: 20px;
padding-left: 20px;
}

.header__search {
padding: 5px;
margin-right: 15px;
border-radius: 8px;
border: 1px 0px 0px 0px;
background-image: url(/src/assets/search_button.png);
background-position: right 5px center;
background-repeat: no-repeat;
background-size: 20px;
}
@media (max-width: 480px) {
.header__search {
width: 20px;
background-image: url(/src/assets/search_button.png);
background-position: right 5px center;
background-size: 20px;
}
.header__search::placeholder {
opacity: 0;
}
}
1 change: 1 addition & 0 deletions my-vue-app/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

10 changes: 10 additions & 0 deletions my-vue-app/src/main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.jsx";

createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>
);
57 changes: 57 additions & 0 deletions my-vue-app/src/modal.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#portal-root {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1000;
pointer-events: none;
}

.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
pointer-events: auto;
display: flex;
justify-content: center;
align-items: center;
}

.modal-content {
height: 500px;
width: 900px;
border-radius: 15px;
position: relative;
pointer-events: auto;
background-color: #212122;
}

.modal-close {
position: absolute;
top: 10px;
right: 10px;
background: none;
border: none;
font-size: 18px;
cursor: pointer;
background-color: #383839;
border-radius: 100%;
color: white;
width: 25px;
}

@media (max-width: 868px) {
.modal-content {
height: 400px;
width: 480px;
}
}
@media (max-width: 480px) {
.modal-overlay {
top: 220px;
}
}
Loading