diff --git a/ssr/api/movies.js b/ssr/api/movies.js new file mode 100644 index 0000000..468c892 --- /dev/null +++ b/ssr/api/movies.js @@ -0,0 +1,21 @@ +import { + FETCH_OPTIONS, + PATH_TO_API, + TMDB_MOVIE_DETAIL_URL, + TMDB_MOVIE_LISTS, +} from '../constant.js'; + +export const fetchMovies = async (endpoint) => { + const response = await fetch( + TMDB_MOVIE_LISTS[PATH_TO_API[endpoint]], + FETCH_OPTIONS + ); + + return await response.json(); +}; + +export const fetchMovieDetail = async (id) => { + const response = await fetch(`${TMDB_MOVIE_DETAIL_URL}${id}`, FETCH_OPTIONS); + + return await response.json(); +}; diff --git a/ssr/constant.js b/ssr/constant.js new file mode 100644 index 0000000..4fef897 --- /dev/null +++ b/ssr/constant.js @@ -0,0 +1,30 @@ +export const BASE_URL = 'https://api.themoviedb.org/3/movie'; + +export const TMDB_THUMBNAIL_URL = + 'https://media.themoviedb.org/t/p/w440_and_h660_face/'; +export const TMDB_ORIGINAL_URL = 'https://image.tmdb.org/t/p/original/'; +export const TMDB_BANNER_URL = + 'https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/'; +export const TMDB_MOVIE_LISTS = { + POPULAR: BASE_URL + '/popular?language=ko-KR&page=1', + NOW_PLAYING: BASE_URL + '/now_playing?language=ko-KR&page=1', + TOP_RATED: BASE_URL + '/top_rated?language=ko-KR&page=1', + UPCOMING: BASE_URL + '/upcoming?language=ko-KR&page=1', +}; +export const TMDB_MOVIE_DETAIL_URL = 'https://api.themoviedb.org/3/movie/'; + +export const FETCH_OPTIONS = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: 'Bearer ' + process.env.TMDB_TOKEN, + }, +}; + +export const PATH_TO_API = { + '/': 'NOW_PLAYING', + '/now-playing': 'NOW_PLAYING', + '/popular': 'POPULAR', + '/top-rated': 'TOP_RATED', + '/upcoming': 'UPCOMING', +}; diff --git a/ssr/server/routes/index.js b/ssr/server/routes/index.js index 84d32f2..23b0612 100644 --- a/ssr/server/routes/index.js +++ b/ssr/server/routes/index.js @@ -1,21 +1,50 @@ -import { Router } from "express"; -import fs from "fs"; -import path from "path"; -import { fileURLToPath } from "url"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import { Router } from 'express'; +import { renderMovieDetailModal, renderMovieList } from '../../views/render.js'; const router = Router(); -router.get("/", (_, res) => { - const templatePath = path.join(__dirname, "../../views", "index.html"); - const moviesHTML = "

들어갈 본문 작성

"; +router.get('/', async (req, res) => { + const endpoint = '/now-playing'; + const template = await renderMovieList(endpoint, res); + + res.send(template); +}); + +router.get('/now-playing', async (req, res) => { + const endpoint = req.path; + const template = await renderMovieList(endpoint, res); + + res.send(template); +}); + +router.get('/popular', async (req, res) => { + const endpoint = req.path; + const template = await renderMovieList(endpoint, res); + + res.send(template); +}); + +router.get('/top-rated', async (req, res) => { + const endpoint = req.path; + const template = await renderMovieList(endpoint, res); + + res.send(template); +}); + +router.get('/upcoming', async (req, res) => { + const endpoint = req.path; + const template = await renderMovieList(endpoint, res); + + res.send(template); +}); + +router.get('/detail/:id', async (req, res) => { + let template = await renderMovieList('/now-playing', res); + const modal = await renderMovieDetailModal(req, res); - const template = fs.readFileSync(templatePath, "utf-8"); - const renderedHTML = template.replace("", moviesHTML); + template = template.replace('', modal); - res.send(renderedHTML); + res.send(template); }); export default router; diff --git a/ssr/utils/round.js b/ssr/utils/round.js new file mode 100644 index 0000000..8e7f987 --- /dev/null +++ b/ssr/utils/round.js @@ -0,0 +1,5 @@ +export const round = (value, decimals = 0) => { + const factor = 10 ** decimals; + + return Math.round(value * factor) / factor; +}; diff --git a/ssr/views/getTemplates.js b/ssr/views/getTemplates.js new file mode 100644 index 0000000..79b98b4 --- /dev/null +++ b/ssr/views/getTemplates.js @@ -0,0 +1,61 @@ +import { round } from '../utils/round.js'; + +export const getMovieItemsHTML = (movieItems) => { + return movieItems.map((movie) => { + const thumbnailUrl = `https://image.tmdb.org/t/p/w200${movie.poster_path}`; + const roundedRate = round(movie.vote_average, 1); + + return /*html*/ ` +
  • + + ${movie.title} +
    +

    + + ${roundedRate} +

    + ${movie.title} +
    +
    +
  • + `; + }); +}; + +export const getMovieDetailModalHTML = (movieDetail) => { + return /*html*/ ` + + + + `; +}; diff --git a/ssr/views/index.html b/ssr/views/index.html index a052396..de529f5 100644 --- a/ssr/views/index.html +++ b/ssr/views/index.html @@ -14,10 +14,15 @@
    -
    +
    -

    MovieList

    +

    + MovieList +

    @@ -39,22 +44,22 @@

    상영 중

    >
  • -
  • -
  • -
    + +

    상영 예정

    diff --git a/ssr/views/render.js b/ssr/views/render.js new file mode 100644 index 0000000..4437cac --- /dev/null +++ b/ssr/views/render.js @@ -0,0 +1,57 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +import { fetchMovieDetail, fetchMovies } from '../api/movies.js'; +import { getMovieDetailModalHTML, getMovieItemsHTML } from './getTemplates.js'; +import { round } from '../utils/round.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export const renderMovieList = async (endpoint, res) => { + const movieItems = await fetchMovies(endpoint).then((data) => data.results); + const bestMovieItem = movieItems[0]; + const moviesHTML = getMovieItemsHTML(movieItems).join(''); + + const templatePath = path.join(__dirname, './', 'index.html'); + let template = fs.readFileSync(templatePath, 'utf-8'); + + template = template.replace('', moviesHTML); + template = template.replace( + '${background-container}', + 'https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/' + + bestMovieItem.backdrop_path + ); + template = template.replace( + '${bestMovie.rate}', + round(bestMovieItem.vote_average) + ); + template = template.replace('${bestMovie.title}', bestMovieItem.title); + + template = renderTabs(template, endpoint); + + return template; + ㅡ; +}; + +export const renderMovieDetailModal = async (req, res) => { + const { id: movieId } = req.params; + const movieDetailItem = await fetchMovieDetail(movieId).then((data) => data); + + const movieDetailModalHTML = getMovieDetailModalHTML(movieDetailItem); + + return movieDetailModalHTML; +}; + +export const renderTabs = (template, currentPath) => { + template = template.replace( + new RegExp( + `\\s*`, + 'g' + ), + `
    ` + ); + + return template; +};