내일배움캠프 React_7기 TIL - 13.[JavaScript 개인과제] 영화 검색 사이트 만들기 (2)

·2024년 10월 22일
0


사진제공 : 림졍님

저번에 영화 정보 API를 사용하여 화면에 뿌리고, 페이지네이션 기능까지 구현하였다.

이번에 기록할 구현 기능은
1. 제목 검색 기능(실시간 검색) 2. 최근 검색어 히스토리 남기기 기능

제목 검색 기능

사용자가 인풋에 제목을 입력하면, 검색 제안 목록 리스트를 인풋 밑에 띄워주도록 구현하고자 했다.

fetchMovieByTitle 함수로 제목을 query 인자로 줘서 검색 결과를 받아온다.

const fetchMovieByTitle = async (query, page) => {
  try {
    const response = await fetch(
      `https://api.themoviedb.org/3/search/movie?query=${query}&include_adult=false&language=ko-KR&page=${page}`,
      options
    );

    const data = await response.json();
    console.log(data);
    if (data.results) {
      filteredMovies = data.results;
      filterdTotalPages = data.total_pages;
      return filteredMovies; // 검색된 영화 목록 반환
    } else {
      console.error("영화 데이터를 가져오는 데 실패했습니다.", data);
    }
  } catch (err) {
    console.error(err);
  }
};

사용자가 값을 입력 또는 수정할 때, fetchMovieByTitle함수를 실행하게 하되, debounce 함수를 적용하여 일정 시간 동안 텀을 두고 함수가 실행되도록 했다.
결과가 있으면 각 영화를 제안 목록으로 추가하고, 검색 결과가 없으면 '검색 결과가 없습니다' 메시지를 표시하고, 검색 결과가 있으면 각 검색 결과를 제안 목록에 표시한다.

// debounce 함수
function debounce(func, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer); // 타이머 초기화
    timer = setTimeout(() => func.apply(this, args), delay); // delay 후에 함수 실행
  };
}

function handleInputChange(e) {
  const searchSuggestions = document.querySelector(".search-suggestions");

  if (e.target.value) {
    fetchMovieByTitle(e.target.value, 1).then((searchedMovies) => {
      searchSuggestions.innerHTML = ""; // 이전 제안 제거

      if (searchedMovies.length === 0) {
        // 검색 결과가 없을 경우
        searchSuggestions.innerHTML = `<div class="no-suggestions">검색 결과가 없습니다.</div>`;
      } else {
        // 검색 결과가 있을 경우
        searchedMovies.forEach((movie) => {
          const suggestionItem = document.createElement("div");
          suggestionItem.className = "suggestion-item";
          suggestionItem.textContent = movie.title;

          suggestionItem.addEventListener("click", () => {
            titleSearchInput.value = movie.title; // 클릭 시 제목 설정
            searchSuggestions.innerHTML = ""; // 제안 목록 비우기
          });

          searchSuggestions.appendChild(suggestionItem); // 제안 목록에 추가
        });
      }
    });
  }
  if (e.target.value === "") {
    showHistoryList();
  }
}

// debounce 적용된 함수
const debouncedInputChange = debounce(handleInputChange, 300);
titleSearchInput.addEventListener("input", debouncedInputChange);

최근 검색어 히스토리

const showHistoryList = () => {
  const searchSuggestions = document.querySelector(".search-suggestions");
  const removeHistory = document.createElement("div");
  removeHistory.innerHTML = "<span>모두 삭제</span>";

  searchSuggestions.innerHTML =
    '<div class="recent-searhced"><div>최근 검색어</div><span id="remove-history">모두 삭제</span></div>';
  const searchHistory = getSearchHistory();
  searchHistory.forEach((history) => {
    console.log(history);
    const suggestionItem = document.createElement("div");
    suggestionItem.className = "suggestion-item";
    suggestionItem.textContent = history;
    suggestionItem.addEventListener("click", () => {
      titleSearchInput.value = history; // 클릭 시 제목 설정
      searchSuggestions.style.visibility = "hidden";
      // searchSuggestions.innerHTML = ""; // 제안 목록 비우기
      // 검색 제안 목록에 아이템 추가
    });
    document.getElementById("remove-history").addEventListener("click", () => {
      localStorage.clear();
      searchSuggestions.innerHTML = "";
    });
    searchSuggestions.appendChild(suggestionItem);
  });
};

function addSearchQuery(searchInput) {
  let searchHistory = JSON.parse(localStorage.getItem("searchHistory")) || [];
  searchHistory = searchHistory.filter((value) => value !== searchInput);
  // 검색어 추가
  searchHistory.push(searchInput);

  // 로컬 스토리지에 업데이트
  localStorage.setItem("searchHistory", JSON.stringify(searchHistory));
}

// 검색어 기록 불러오기
function getSearchHistory() {
  return JSON.parse(localStorage.getItem("searchHistory")) || [];
}

SearchButton.addEventListener("click", function (e) {
  e.preventDefault();
  // console.log(titleSearchInput.value);
  const searchInput = titleSearchInput.value;
  if (searchInput.trim() === "") {
    // 검색어가 없을 때 알림을 표시
    alert("검색어를 입력해 주세요.");
  } else {
    //세션에 검색어 저장
    sessionStorage.setItem("searchQuery", searchInput);
    // console.log(sessionStorage.getItem("searchQuery"));

    //검색히스토리 추가
    addSearchQuery(searchInput);
    // search.html로 이동
    window.location.href = "search.html";
  }
});

addSearchQuery 로 검색어를 로컬 스토리지에 넣는 기능을 구현했다. 만약 이미 있는 기록이면 중복해서 들어가지 않도록 했다.
세션에도 검색어를 저장하도록 했는데, 이건 search.html 페이지로 query를 넘기는데 사용했다.

사실 오늘 중간 과정을 TIL로 쓰기엔 좀 코드가 더럽고 부끄러워서.... 쓰기를 고민했지만 기록을 해야 정리가 될 것 같아서 용기내어 쓴다..^^

이제 남은건 영화 디테일 모달(구현 완료), 북마크 기능과 css다.

profile
내배캠 React_7기 이수중

1개의 댓글

comment-user-thumbnail
2024년 10월 22일

wow 누군진 모르겠지만 짤 하나는 최고네요! ^-^b

답글 달기

관련 채용 정보