[TIL] 231023

·2023년 10월 23일
1

TIL series

목록 보기
3/28

첫번째 개인 프로젝트 완료

첫번째 개인 프로젝트는 TMDB 오픈 API를 이용해 인기 영화 데이터를 불러오는 것이었다.
jQuery라이브러리 없이 순수 바닐라 js로 구현해야하고 몇 가지 조건들이 있었다.

↓ 과제 예시 화면

  • 구현해야 하는 주요 기능
    1) TMDB 오픈 API에서 인기영화 20개의 포스터/제목/소개/평점 불러오기
    2) 영화 제목 검색 기능
    3) 각각 영화 카드 클릭 시 TMDB에서 제공하는 영화 id 출력



구현 결과


구현 해야하는 요소와 조건은 모두 만족하는 것 같다. 그렇지만 전체적인 코드가 지저분하고 무식(?)하게 구현한 부분이 많아 개운하지는 않다. 그래도 첫 개인 프로젝트를 무사히 제출했다는데에 의의를 두기로 했다ㅎㅎ.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <script src="app.js" defer></script>

    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Black+Han+Sans&display=swap"
      rel="stylesheet"
    />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Nanum+Gothic&display=swap"
      rel="stylesheet"
    />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Fjalla+One&display=swap"
      rel="stylesheet"
    />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Heebo&family=Nanum+Gothic&display=swap"
      rel="stylesheet"
    />

    <link rel="stylesheet" href="styles.css" />

    <title>내배캠 인기 영화</title>
  </head>
  <body>
    <header>
      <div id="title_box">
        <a href="./index.html">내배캠 인기 영화</a>
      </div>
      <div id="search_box">
        <input
          onkeyup="enterKey();"
          id="search_input"
          type="text"
          placeholder="제목으로 영화를 검색할 수 있어요:)"
        />
        <button id="search_btn">검색</button>
      </div>
    </header>

    <main>
      <section>
        <h2>TOP 20</h2>
      </section>

      <section>
        <ul id="grid" class="poster_grid"></ul>
      </section>
    </main>

    <footer>
      <button id="top_btn">Top</button>
    </footer>
  </body>
</html>

body {
  background: #dce8e3;
}

header {
  position: static;
  display: flex;
  justify-content: space-between;
  border-bottom: 2px solid #61b798;
  background: #111d18;
  box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  padding: 30px 60px;
}

header a {
  color: #0ecd88;
  font-family: "Black Han Sans", sans-serif;
  font-size: 60px;
  text-decoration: none;
  text-align: center;
}

#search_box {
  padding: 20px 0;
}

#search_input {
  border-style: solid;
  border-radius: 50px;
  border: 1.5px solid #0ecd88;
  background: #000;
  box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
  width: 250px;
  height: 35px;
  text-align: center;
  font-family: "Nanum Gothic", sans-serif;
  color: #fff;
}

#search_btn {
  width: 70px;
  height: 35px;
  border-style: solid;
  border-radius: 50px;
  border: 1.5px solid #0ecd88;
  background: #0ecd88;

  color: #fff;
  text-align: center;
  font-family: "Nanum Gothic", sans-serif;
  font-size: 16px;

  cursor: pointer;
}

#search_btn:hover {
  background-color: #0d9161;
  border-color: #0d9161;
}

main h2 {
  margin: 30px 150px;
  text-align: right;
  font-size: 80px;
  font-style: italic;
  font-family: "Fjalla One", sans-serif;
}

main ul {
  width: 1200px;
  position: static;
  margin: 0 auto 48px auto;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 20px 20px;
}

main li {
  list-style: none;
  overflow: hidden;
}

.movie_card img {
  width: 400px;
  height: 656px;
  object-fit: cover;
}

.movie_card {
  border-radius: 20px;
  background-color: #fff;
}

.movie_card:hover {
  cursor: pointer;
}

.text_area {
  margin: 10px 20px 40px 20px;
}

.text_area h3 {
  font-family: "Heebo", sans-serif;
}

.text_area p {
  font-family: "Nanum Gothic", sans-serif;
  line-height: 23px;
}

#top_btn {
  display: block;
  float: right;
  margin-right: 50px;
  margin-bottom: 100px;

  width: 70px;
  height: 35px;
  border-style: solid;
  border-radius: 50px;
  border: 1.5px solid #0ecd88;
  background: #0ecd88;

  color: #fff;
  text-align: center;
  font-family: "Nanum Gothic", sans-serif;
  font-size: 16px;

  cursor: pointer;
}

const options = {
  method: "GET",
  headers: {
    accept: "application/json",
    Authorization:
      "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI5YTM1ZTk2ZGQzNTBiYWRhZTQ5ZDVjNTU0M2QyZjI2YSIsInN1YiI6IjY1MzBhMWIwNTFhNjRlMDBhYmEwMGU3NCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.FRFQf3AHUFL0D0CZ7g8GOQP-J-y3b4_G84drHnBjtIE",
  },
};

let img_url = [20];
let titles = [20];
let overviews = [20];
let averages = [20];
let movie_ids = [20];

fetch(
  "https://api.themoviedb.org/3/movie/popular?language=en-US&page=1",
  options
)
  .then((response) => response.json())
  .then((response) => {
    console.log(response);
    const obj = response.results;
    //초기화면 영화 카드 구성
    init(obj);
  })
  .catch((err) => console.error(err));

const grid = document.getElementById("grid");
const search_input = document.querySelector("#search_box input");
const search_btn = document.querySelector("#search_box button");
const top_btn = document.getElementById("top_btn");

const isAllFalse = (value) => value === false;

//initialize
function init(obj) {
  for (let i = 0; i < 20; i++) {
    img_url[i] = "https://image.tmdb.org/t/p/original/" + obj[i].poster_path;
    titles[i] = obj[i].title;
    overviews[i] = obj[i].overview;
    averages[i] = "Rate: " + obj[i].vote_average;
    movie_ids[i] = obj[i].id;

    add_card(i);
  }
}

//영화카드 생성&추가
function add_card(i) {
  const new_card = `
  <li class="movie_card" id="${movie_ids[i]}">
    <img class="main_img" src= ${img_url[i]} alt="movie-poster" />
    <div class="text_area">
      <h3>${titles[i]}</h3>
      <p>${overviews[i]}</p>
      <p>${averages[i]}</p>
    </div>
  </li>`;

  grid.insertAdjacentHTML("beforeend", new_card);
}

//검색 버튼 이벤트 핸둘러
function btn_click() {
  //영화 카드 전체 삭제
  while (grid.firstChild) {
    grid.firstChild.remove();
  }

  //입력 값 영화 제목 포함 여부 확인
  let check = titles.map(function (title) {
    let tmp = title.toUpperCase();
    return tmp.includes(search_input.value.toUpperCase());
  });

  //일치하는 결과 없을 경우
  if (check.every(isAllFalse)) {
    alert("검색 실패");
    location.reload();
    return;
  }

  //일치하는 결과 화면에 출력
  check.forEach(function (TorF, index) {
    if (TorF) {
      add_card(index);
    }
  });
}

//input 요소에서 enter 누르면 검색 버튼 클릭한 것처럼
function enterKey() {
  if (window.event.keyCode == 13) {
    btn_click();
  }
}

//검색 버튼 이벤트 리스너
search_btn.addEventListener("click", btn_click);

//영화 카드 클릭 시 해당 영화 id alert
grid.addEventListener("click", (target) => {
  let click_element = target.target;
  let target_card = click_element.closest(".movie_card");
  alert("영화 id: " + target_card.id);
});

//상단 이동 버튼
top_btn.addEventListener("click", () => {
  let body = document.getElementsByTagName("body")[0];
  //창의 스크롤을 본문 최상단으로 부드럽게 이동시킵니다.
  window.scroll({
    behavior: "smooth",
    top: body.offsetTop,
  });
});

0개의 댓글