HTML, CSS, JS만을 활용해 영화 검색 페이지를 구현하면서 기본적인 구현 과제를 제외하고 추가적인 기능으로 무한 스크롤을 구현하려고 했다. 기존에 React를 공부했을 때 연습해 본 경험이 있어서 쉽게 구현할 거라 생각했지만 생각 외로 너무 고민했던 부분들이 많았기에 정리하고 복습하려고 한다.
이벤트 등록
if (currentPage === "movieList.html") {
$searchResults.addEventListener("click", (e) => {
bookMarkCheck(e);
});
$searchInput.addEventListener("input", async (e) => {
inputSearch = e.target.value.toLowerCase().trim();
if (inputSearch) {
await handleSearch();
}
});
$searchInput.addEventListener("keydown", async (e) => {
if (e.key === "Enter") {
if (!inputSearch) {
alert("검색어를 입력해 주세요");
return;
}
await handleSearch();
}
});
$searchBtn.addEventListener("click", async () => {
inputSearch = $searchInput.value.toLowerCase().trim();
if (!inputSearch) {
alert("검색어를 입력해 주세요");
return;
}
await handleSearch();
});
}
구현 조건이 실시간 검색 및 엔터키, 클릭 버튼 모두 구현이라 각각의 이벤트 리스너를 통해 이벤트를 등록했다. 이벤트를 등록하고 이벤트 조건이 맞을 때마다 함수가 실행되도록 구현했다.
const handleSearch = async () => {
page = 1;
hasNextPage = true;
loading = true;
const { totalPages: searchPages, totalResults: searchResults } =
await displaySearchResultCards(page, inputSearch, $searchResults);
totalPages = searchPages;
totalResults = searchResults;
if (totalPages >= 1) {
initializeSearchObserver();
}
};
사용자들이 검색을 하고 지우고 다시 다른 값을 검색할 수 있기 때문에 무한 스크롤에서 사용한 상태들을 초기화해주고 데이터를 가져와 UI를 그려주는 함수를 사용했다. 그리고 넘어오는 데이터의 페이지가 한 페이지 이상일 때만 무한 스크롤을 위한 옵저버를 잡아주는 함수를 추가로 동작하도록 했다.
검색 UI를 그려주는 함수
// 영화 검색결과 UI 함수
export const displaySearchResultCards = async (page, title, element) => {
const moviesData = await getSearchMovies(page, title);
page === 1 ? (element.innerHTML = "") : null;
if (moviesData && moviesData.results.length > 0) {
try {
moviesData.results.forEach((movie) => {
const bookMarks = JSON.parse(localStorage.getItem("movies")) || [];
const isBookMarked = bookMarks.some(
(bookmark) => bookmark.id === String(movie.id)
);
const divEl = document.createElement("div");
divEl.setAttribute("key", movie.id);
divEl.classList.add("movie__card");
const movieItem = `
<a class='movie__link' href='./movieDetail.html?id=${movie.id}'>
<img class="movie__mark" src='./images/${
isBookMarked ? "fillstar.svg" : "star.svg"
}' />
<img class="movie__img" src=${
movie.backdrop_path
? `https://image.tmdb.org/t/p/w300${movie.backdrop_path}`
: "https://ekari.jp/wp-content/uploads/2020/12/noimage.png"
} alt="${movie.title}"/>
<div class="movie__text__container">
<h3 class="movie__title">${strCut(movie.title, 20)}</h3>
<p class="movie__date"> 개봉 : ${movie.release_date}</p>
<p class="movie__desc">${
movie.overview ? strCut(movie.overview, 35) : "설명이 없습니다."
}</p>
</div>
</a>
`;
divEl.innerHTML = movieItem;
element.append(divEl);
});
console.log("moviesData", moviesData);
return {
totalPages: moviesData.total_pages ? moviesData.total_pages : 0,
totalResults: moviesData.total_results ? moviesData.total_results : 0,
};
} catch (error) {
throw new Error(`이런 ${error} 가 발생했습니다.`);
}
} else {
element.innerHTML = `<p class="movie__no__result">${title} 에 대한 검색 결과가 없습니다.</p>`;
}
};
처음 검색을 하게 되면 아래와 같이 데이터가 출력되게 UI를 그려주면서 총 페이지 값과 총 결과 값을 객체의 형태로 리턴해주도록 했는데 검색 결과는 잘 출력되지만 자꾸 totalPages 값을 찾을 수 없다는 에러가 발생했다.
코드를 몇 번이고 다시 읽어봐도 코드의 동작 순서에 오류가 없는 것 같아 결국 튜터님께 문의를 요청했고 코드를 정상적으로 동작 시킬 수 있었다.
// 영화 검색 페이지 그리기
export const displaySearchResultCards = async (page, title, element) => {
const moviesData = await getSearchMovies(page, title);
const returnData = {
totalPages: moviesData.total_pages ? moviesData.total_pages : 0,
totalResults: moviesData.total_results ? moviesData.total_results : 0,
};
page === 1 ? (element.innerHTML = "") : null;
if (moviesData && moviesData.results.length > 0) {
try {
moviesData.results.forEach((movie) => {
const bookMarks = JSON.parse(localStorage.getItem("movies")) || [];
const isBookMarked = bookMarks.some(
(bookmark) => bookmark.id === String(movie.id)
);
const divEl = document.createElement("div");
divEl.setAttribute("key", movie.id);
divEl.classList.add("movie__card");
const movieItem = `
<a class='movie__link' href='./movieDetail.html?id=${movie.id}'>
<img class="movie__mark" src='./images/${
isBookMarked ? "fillstar.svg" : "star.svg"
}' />
<img class="movie__img" src=${
movie.backdrop_path
? `https://image.tmdb.org/t/p/w300${movie.backdrop_path}`
: "https://ekari.jp/wp-content/uploads/2020/12/noimage.png"
} alt="${movie.title}"/>
<div class="movie__text__container">
<h3 class="movie__title">${strCut(movie.title, 20)}</h3>
<p class="movie__date"> 개봉 : ${movie.release_date}</p>
<p class="movie__desc">${
movie.overview ? strCut(movie.overview, 35) : "설명이 없습니다."
}</p>
</div>
</a>
`;
divEl.innerHTML = movieItem;
element.append(divEl);
});
console.log("moviesData", moviesData);
} catch (error) {
throw new Error(`이런 ${error} 가 발생했습니다.`);
}
} else {
element.innerHTML = `<p class="movie__no__result">${title} 에 대한 검색 결과가 없습니다.</p>`;
}
return returnData;
};
수정된 코드는 위와 같은데 우선 데이터를 가져오면 앞에서 페이지와 결과값이 없을 때를 대비한 가드 역할을 해줄 수 있도록 리턴할 객체를 새로 만들어주었다. 기존에 if 문안에서 리턴되던 값도 가드 역할을 해주면서 함수 마지막에 리턴될 수 있도록 최종적으로 빼주었더니 정상적으로 오류 없이 동작하게 되었다.
원인
IntersectionObserver API를 사용해서 계속해서 페이지를 변경하면서 UI를 그리는 함수를 호출하는데 호출하면서 영화 제목에 없는 영화 데이터를 가져올 때는 총 페이지나 결과 값들이 모두 undefined였고, 콘솔로 확인해 본 결과 try 문에서 리턴되던 데이터들이 제대로 전달되지 않았다.
지금은 이 정도가 원인이라고 생각되는데 좀 더 정확한 원인을 코드를 읽어보면 생각해 봐야겠다.
'JavaScript' 카테고리의 다른 글
자바스크립트 얕은 복사와 깊은 복사 완벽 이해하기 (1) | 2024.10.17 |
---|---|
자바스크립트 클로저 완벽하게 이해하기 (0) | 2024.10.15 |
자바스크립트 함수 호출에 따른 this 와 명시적 바인딩 (0) | 2024.10.14 |
자바스크립트 실행 컨텍스트 복습 및 정리하기 (1) | 2024.10.11 |
자바스크립트 자료형 Set 학습 및 정리하기 (0) | 2024.10.10 |
댓글