From 4761f6835675b9311c216e8224c80fcc1d99e455 Mon Sep 17 00:00:00 2001 From: cys4585 Date: Thu, 3 Oct 2024 18:12:47 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=98=81=ED=99=94=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?-=20CSR=EA=B3=BC=20=EB=8F=99=EC=9D=BC=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81=EB=90=98=EB=8F=84=EB=A1=9D=20API=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20-=20API=EB=A1=9C=20=EC=96=BB=EC=96=B4?= =?UTF-8?q?=EC=98=A8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=A5=BC=20index.html?= =?UTF-8?q?=EC=9D=98=20=ED=85=A1=ED=94=8C=EB=A6=BF=20=EB=B3=80=EC=88=98(?= =?UTF-8?q?=3F)=EC=97=90=20=EB=B0=94=EC=9D=B8=EB=94=A9=20-=20router.get?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=8B=A4=EB=A5=B8=20=EC=A3=BC=EC=86=8C?= =?UTF-8?q?=EB=8F=84=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81,=20=EB=8B=A4=EB=A5=B8=20API=20=EC=97=B0=EA=B2=B0=20-?= =?UTF-8?q?=20=EC=84=A0=ED=83=9D=ED=95=9C=20=ED=83=AD=20=ED=99=9C=EC=84=B1?= =?UTF-8?q?=ED=99=94=EB=90=98=EB=8F=84=EB=A1=9D=20index.html=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ssr/server/routes/index.js | 164 ++++++++++++++++++++++++++++++++++++- ssr/views/index.html | 19 +++-- 2 files changed, 173 insertions(+), 10 deletions(-) diff --git a/ssr/server/routes/index.js b/ssr/server/routes/index.js index 84d32f2..950c172 100644 --- a/ssr/server/routes/index.js +++ b/ssr/server/routes/index.js @@ -8,14 +8,172 @@ const __dirname = path.dirname(__filename); const router = Router(); -router.get("/", (_, res) => { +router.get("/", async (_, res) => { const templatePath = path.join(__dirname, "../../views", "index.html"); - const moviesHTML = "

들어갈 본문 작성

"; + const template = fs.readFileSync(templatePath, "utf-8"); + + const movies = await loadMovies(TMDB_MOVIE_LISTS.nowPlaying); + const movie = movies[0] ?? { + id: -1, + title: "", + bannerUrl: "", + vote_average: 0, + }; + + const renderedHTML = template + .replace("${MOVIE_ITEMS_PLACEHOLDER}", getMovieItemHTML(movies)) + .replace("${background-container}", TMDB_BANNER_URL + movie.backdrop_path) + .replace("${bestMovie.rate}", round(movie.vote_average, 1)) + .replace("${bestMovie.title}", movie.title) + .replace("${isSelectedNowPlaying}", "selected"); + + res.send(renderedHTML); +}); +router.get("/now-playing", async (_, res) => { + const templatePath = path.join(__dirname, "../../views", "index.html"); const template = fs.readFileSync(templatePath, "utf-8"); - const renderedHTML = template.replace("", moviesHTML); + + const movies = await loadMovies(TMDB_MOVIE_LISTS.nowPlaying); + const movie = movies[0] ?? { + id: -1, + title: "", + bannerUrl: "", + vote_average: 0, + }; + + const renderedHTML = template + .replace("${MOVIE_ITEMS_PLACEHOLDER}", getMovieItemHTML(movies)) + .replace("${background-container}", TMDB_BANNER_URL + movie.backdrop_path) + .replace("${bestMovie.rate}", round(movie.vote_average, 1)) + .replace("${bestMovie.title}", movie.title) + .replace("${isSelectedNowPlaying}", "selected"); res.send(renderedHTML); }); +router.get("/popular", async (_, res) => { + const templatePath = path.join(__dirname, "../../views", "index.html"); + const template = fs.readFileSync(templatePath, "utf-8"); + + const movies = await loadMovies(TMDB_MOVIE_LISTS.popular); + const movie = movies[0] ?? { + id: -1, + title: "", + bannerUrl: "", + vote_average: 0, + }; + + const renderedHTML = template + .replace("${MOVIE_ITEMS_PLACEHOLDER}", getMovieItemHTML(movies)) + .replace("${background-container}", TMDB_BANNER_URL + movie.backdrop_path) + .replace("${bestMovie.rate}", round(movie.vote_average, 1)) + .replace("${bestMovie.title}", movie.title) + .replace("${isSelectedPopular}", "selected"); + + res.send(renderedHTML); +}); + +router.get("/top-rated", async (_, res) => { + const templatePath = path.join(__dirname, "../../views", "index.html"); + const template = fs.readFileSync(templatePath, "utf-8"); + + const movies = await loadMovies(TMDB_MOVIE_LISTS.topRated); + const movie = movies[0] ?? { + id: -1, + title: "", + bannerUrl: "", + vote_average: 0, + }; + + const renderedHTML = template + .replace("${MOVIE_ITEMS_PLACEHOLDER}", getMovieItemHTML(movies)) + .replace("${background-container}", TMDB_BANNER_URL + movie.backdrop_path) + .replace("${bestMovie.rate}", round(movie.vote_average, 1)) + .replace("${bestMovie.title}", movie.title) + .replace("${isSelectedTopRated}", "selected"); + + res.send(renderedHTML); +}); + +router.get("/upcoming", async (_, res) => { + const templatePath = path.join(__dirname, "../../views", "index.html"); + const template = fs.readFileSync(templatePath, "utf-8"); + + const movies = await loadMovies(TMDB_MOVIE_LISTS.upcoming); + const movie = movies[0] ?? { + id: -1, + title: "", + bannerUrl: "", + vote_average: 0, + }; + + const renderedHTML = template + .replace("${MOVIE_ITEMS_PLACEHOLDER}", getMovieItemHTML(movies)) + .replace("${background-container}", TMDB_BANNER_URL + movie.backdrop_path) + .replace("${bestMovie.rate}", round(movie.vote_average, 1)) + .replace("${bestMovie.title}", movie.title) + .replace("${isSelectedUpcoming}", "selected"); + + res.send(renderedHTML); +}); + +const getMovieItemHTML = (movies) => { + return movies + .map( + ({ id, title, vote_average, poster_path }) => + `
  • + +
    + ${title} +
    +

    + + ${round(vote_average, 1)} +

    + ${title} +
    +
    +
    +
  • ` + ) + .join(""); +}; + +const BASE_URL = "https://api.themoviedb.org/3/movie"; +const TMDB_MOVIE_LISTS = { + popular: BASE_URL + "/popular?language=ko-KR&page=1", + nowPlaying: BASE_URL + "/now_playing?language=ko-KR&page=1", + topRated: BASE_URL + "/top_rated?language=ko-KR&page=1", + upcoming: BASE_URL + "/upcoming?language=ko-KR&page=1", +}; + +const TMDB_THUMBNAIL_URL = + "https://media.themoviedb.org/t/p/w440_and_h660_face/"; +const TMDB_BANNER_URL = + "https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/"; + +const FETCH_OPTIONS = { + method: "GET", + headers: { + accept: "application/json", + Authorization: "Bearer " + process.env.TMDB_TOKEN, + }, +}; + +const loadMovies = async (url) => { + const response = await fetch(url, FETCH_OPTIONS); + const data = await response.json(); + + return data.results; +}; + +const round = (value, decimals = 0) => { + const factor = 10 ** decimals; + + return Math.round(value * factor) / factor; +}; + export default router; diff --git a/ssr/views/index.html b/ssr/views/index.html index a052396..cebc703 100644 --- a/ssr/views/index.html +++ b/ssr/views/index.html @@ -14,10 +14,15 @@
    -
    +
    -

    MovieList

    +

    + MovieList +

    @@ -33,28 +38,28 @@

    MovieList

    • -
    • + >

      상영 예정

      @@ -64,7 +69,7 @@

      상영 예정

      지금 인기 있는 영화

        - + ${MOVIE_ITEMS_PLACEHOLDER}
      From 27df9ee25a928c69d0712648d870bf96d491e221 Mon Sep 17 00:00:00 2001 From: cys4585 Date: Thu, 3 Oct 2024 20:08:25 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EC=83=81=EC=84=B8=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EB=AA=A8=EB=8B=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ssr/server/routes/index.js | 86 +++++++++++++++++++++++++++++++++++++- ssr/views/index.html | 2 +- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/ssr/server/routes/index.js b/ssr/server/routes/index.js index 950c172..9818391 100644 --- a/ssr/server/routes/index.js +++ b/ssr/server/routes/index.js @@ -118,6 +118,34 @@ router.get("/upcoming", async (_, res) => { res.send(renderedHTML); }); +router.get("/detail/:movieId", async (req, res) => { + const { movieId } = req.params; + + const templatePath = path.join(__dirname, "../../views", "index.html"); + const template = fs.readFileSync(templatePath, "utf-8"); + + const movies = await loadMovies(TMDB_MOVIE_LISTS.upcoming); + const movie = movies[0] ?? { + id: -1, + title: "", + bannerUrl: "", + vote_average: 0, + }; + const movieDetail = await loadMovieDetail(movieId); + + const renderedHTML = template + .replace("${MOVIE_ITEMS_PLACEHOLDER}", getMovieItemHTML(movies)) + .replace("${background-container}", TMDB_BANNER_URL + movie.backdrop_path) + .replace("${bestMovie.rate}", round(movie.vote_average, 1)) + .replace("${bestMovie.title}", movie.title) + .replace("${MODAL_AREA}", getMovieDetailModalHTML(movieDetail)) + .replace("${isSelectedNowPlaying}", "selected"); + + res.send(renderedHTML); +}); + +// ---------------------------- HTML ------------------------------------------------- + const getMovieItemHTML = (movies) => { return movies .map( @@ -142,18 +170,61 @@ const getMovieItemHTML = (movies) => { .join(""); }; +const getMovieDetailModalHTML = (movieDetail) => { + const { title, bannerUrl, releaseYear, description, genres, rate } = { + title: movieDetail.title, + bannerUrl: TMDB_ORIGINAL_URL + movieDetail.poster_path, + releaseYear: movieDetail.release_date.split("-")[0], + description: movieDetail.overview, + genres: movieDetail.genres.map(({ name }) => name), + rate: round(movieDetail.vote_average, 1), + }; + + const handleCloseButtonClick = + "document.getElementById('modalBackground').classList.remove('active')"; + + return ` + + `; +}; + +// ---------------------------- API ------------------------------------------------- + const BASE_URL = "https://api.themoviedb.org/3/movie"; + const TMDB_MOVIE_LISTS = { popular: BASE_URL + "/popular?language=ko-KR&page=1", nowPlaying: BASE_URL + "/now_playing?language=ko-KR&page=1", topRated: BASE_URL + "/top_rated?language=ko-KR&page=1", upcoming: BASE_URL + "/upcoming?language=ko-KR&page=1", }; - const TMDB_THUMBNAIL_URL = "https://media.themoviedb.org/t/p/w440_and_h660_face/"; +const TMDB_ORIGINAL_URL = "https://image.tmdb.org/t/p/original/"; const TMDB_BANNER_URL = "https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/"; +const TMDB_MOVIE_DETAIL_URL = "https://api.themoviedb.org/3/movie/"; const FETCH_OPTIONS = { method: "GET", @@ -170,6 +241,19 @@ const loadMovies = async (url) => { return data.results; }; +const loadMovieDetail = async (id) => { + const url = TMDB_MOVIE_DETAIL_URL + id; + console.log(url); + const params = new URLSearchParams({ + language: "ko-KR", + }); + const response = await fetch(url + "?" + params, FETCH_OPTIONS); + + return await response.json(); +}; + +// ---------------------------- UTILS ------------------------------------------------- + const round = (value, decimals = 0) => { const factor = 10 ** decimals; diff --git a/ssr/views/index.html b/ssr/views/index.html index cebc703..f238193 100644 --- a/ssr/views/index.html +++ b/ssr/views/index.html @@ -80,6 +80,6 @@

      지금 인기 있는 영화

      - + ${MODAL_AREA}