Skip to content
Merged
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
25 changes: 25 additions & 0 deletions judy/Week3/Movie/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local
.env

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
54 changes: 54 additions & 0 deletions judy/Week3/Movie/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

```js
export default tseslint.config({
extends: [
// Remove ...tseslint.configs.recommended and replace with this
...tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
...tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
...tseslint.configs.stylisticTypeChecked,
],
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```

You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default tseslint.config({
plugins: {
// Add the react-x and react-dom plugins
'react-x': reactX,
'react-dom': reactDom,
},
rules: {
// other rules...
// Enable its recommended typescript rules
...reactX.configs['recommended-typescript'].rules,
...reactDom.configs.recommended.rules,
},
})
```
28 changes: 28 additions & 0 deletions judy/Week3/Movie/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
13 changes: 13 additions & 0 deletions judy/Week3/Movie/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
34 changes: 34 additions & 0 deletions judy/Week3/Movie/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "movie",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.0.17",
"axios": "^1.8.4",
"clsx": "^2.1.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-router-dom": "^7.4.1",
"tailwindcss": "^4.0.17"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react-swc": "^3.8.0",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.2.0"
}
}
1 change: 1 addition & 0 deletions judy/Week3/Movie/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions judy/Week3/Movie/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { RootLayout } from "./layout/root-layout";
import { HomePage } from "./page/HomePage";
import { MovieListPage } from "./page/MovieListPage";
import { MovieDetailPage } from "./page/MovieDetailPage";
Comment on lines +2 to +5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 default export를 안 쓴 이유가 있는지 궁금해용

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 이거 리팩토링하면서 default가 빠진것같아요...ㅎㅎ


const movieListTypes = ["popular", "upcoming", "top_rated", "now_playing"];

const movieListRoutes = movieListTypes.map((type) => ({
path: `movies/${type}`,
element: <MovieListPage />,
}));
Comment on lines +7 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무 깔끔한 방법이네요!
movieListTypes는 const 폴더 만들어서 넣어놔도 좋을 것 같아용


const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
children: [
{
index: true,
element: <HomePage />,
},
...movieListRoutes,
],
},
{
path: "movie/detail/:id",
element: <MovieDetailPage />,
},
]);

function App() {
return <RouterProvider router={router} />;
}

export default App;
8 changes: 8 additions & 0 deletions judy/Week3/Movie/src/api/apiClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import axios from "axios";

export const apiClient = axios.create({
baseURL: "https://api.themoviedb.org/3/movie",
headers: {
Authorization: `Bearer ${import.meta.env.VITE_MOVIE_TOKEN}`,
},
});
7 changes: 7 additions & 0 deletions judy/Week3/Movie/src/components/common/ErrorPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const ErrorPage = () => {
return (
<div className="w-[100vw] h-[100vh] flex items-center justify-center">
<div className="text-4xl">영화를 불러올 수 없습니다🥹</div>
</div>
);
};
7 changes: 7 additions & 0 deletions judy/Week3/Movie/src/components/common/LoadingPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const LoadingPage = () => {
return (
<div className="w-[100vw] h-[100vh] flex items-center justify-center">
<span className="w-20 h-20 border-6 border-lime-400 border-b-transparent rounded-full animate-spin"></span>
</div>
);
};
36 changes: 36 additions & 0 deletions judy/Week3/Movie/src/components/movieList/MovieCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useNavigate } from "react-router-dom";
import { Movie } from "../../types/movieType";

interface Props {
movie: Movie;
}

export const MovieCard = ({ movie }: Props) => {
const navigate = useNavigate();
const onClickMovie = (movieId: number) => {
navigate(`/movie/detail/${movieId}`);
};

return (
<div
className={
"w-60 h-90 rounded-md hover:scale-110 transition-all duration-300 ease-in overflow-hidden relative group cursor-pointer"
}
onClick={() => onClickMovie(movie.id)}
>
<img
src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
alt={movie.title}
className="group-hover:blur-[5px]"
/>
<div className="opacity-0 absolute top-0 left-0 w-full h-full px-3 flex flex-col items-center justify-center text-white group-hover:opacity-100 z-10">
<div className="font-semibold text-center">{movie.title}</div>
<div className="text-center text-sm">
{movie.overview.length > 50
? movie.overview.substring(0, 50) + "..."
: movie.overview}
</div>
</div>
</div>
);
};
42 changes: 42 additions & 0 deletions judy/Week3/Movie/src/components/movieList/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import clsx from "clsx";

interface Props {
page: number;
setPage: React.Dispatch<React.SetStateAction<number>>;
}

export const Pagination = ({ page, setPage }: Props) => {
const onNextPage = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
setPage((prev) => prev + 1);
};

const onPrevPage = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (page !== 1) {
setPage((prev) => prev - 1);
}
};
return (
<div className="flex items-center justify-center w-full h-40 gap-7">
<button
className={clsx(
"w-15 h-15 rounded-md text-white cursor-pointer",
page === 1
? "bg-gray-400 hover:bg-gray-400"
: "bg-lime-500 hover:bg-lime-400"
)}
onClick={onPrevPage}
>
{"<"}
</button>
<div>{page} 페이지</div>
<button
className="w-15 h-15 rounded-md bg-lime-500 hover:bg-lime-400 text-white cursor-pointer"
onClick={onNextPage}
>
{">"}
</button>
</div>
);
};
1 change: 1 addition & 0 deletions judy/Week3/Movie/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "tailwindcss";
57 changes: 57 additions & 0 deletions judy/Week3/Movie/src/layout/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import clsx from "clsx";
import { useNavigate } from "react-router-dom";

export const Navbar = () => {
const navigate = useNavigate();
const pathname = window.location.pathname;

return (
<nav className="flex h-15 items-center px-5 gap-4">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nav도 상수화하고 map으로 순회하며 중복 코드를 줄일 수 있을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 넵넵 따로 컴포넌트화 만들어보겠습니당

<div
onClick={() => navigate("/")}
className={clsx(
"cursor-pointer",
pathname === "/" ? "text-lime-600" : "text-black"
)}
>
</div>
<div
onClick={() => navigate("/movies/popular")}
className={clsx(
"cursor-pointer",
pathname === "/movies/popular" ? "text-lime-600" : "text-black"
)}
>
인기 영화
</div>
<div
onClick={() => navigate("/movies/upcoming")}
className={clsx(
"cursor-pointer",
pathname === "/movies/upcoming" ? "text-lime-600" : "text-black"
)}
>
상영 중
</div>
<div
onClick={() => navigate("/movies/top_rated")}
className={clsx(
"cursor-pointer",
pathname === "/movies/top_rated" ? "text-lime-600" : "text-black"
)}
>
평점 높은
</div>
<div
onClick={() => navigate("/movies/now_playing")}
className={clsx(
"cursor-pointer",
pathname === "/movies/now_playing" ? "text-lime-600" : "text-black"
)}
>
개봉 예정
</div>
</nav>
);
};
11 changes: 11 additions & 0 deletions judy/Week3/Movie/src/layout/root-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Outlet } from "react-router-dom";
import { Navbar } from "./Navbar";

export const RootLayout = () => {
return (
<>
<Navbar />
<Outlet />
</>
);
};
10 changes: 10 additions & 0 deletions judy/Week3/Movie/src/main.tsx
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.tsx'

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
3 changes: 3 additions & 0 deletions judy/Week3/Movie/src/page/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const HomePage = () => {
return <div>HomePage</div>;
};
Loading
Loading