영화 평점 TOP20 데이터를 open API로 불러와서 간단하게 보여주는 개인 프로젝트를 진행 중이였다.
필요한 필수 기능을 만들었지만 같은 구문을 다시 사용하는 과정이 많아 리팩토링을 해야했다.
그 중 검색기능 부분이 문제가 많아서 수정중이였다. 아래처럼 데이터 card들을 리스팅하는 부분을 재사용 했기 때문이었다.
fetch(
"https://api.themoviedb.org/3/movie/top_rated?language=en-US&page=1",
options
)
.then((response) => response.json())
.then((response) => {
response["results"].forEach((i) => {
let title = i["title"];
let rate = i["vote_average"];
let desc = i["overview"];
let img = i["poster_path"];
let id = i["id"];
let url = `https://image.tmdb.org/t/p/w500/${img}`;
let temp_html = ` <img class="poster" src="${url}" alt="movie">
<h3 class="movie-title">${title}</h3>
<p>${desc}
</p>
<footer class="rate"><p>Ration : ${rate}</p></footer>
`;
// cardList 하위로 card 요소들 붙여넣기
const $cardList = document.getElementById("card-list");
const $card = document.createElement("div");
$card.classList.add("card");
$card.innerHTML = temp_html;
$cardList.appendChild($card);
// click 이벤트 적용
$card.addEventListener("click", () => {
alert(`영화 id : ${id}`);
});
});
})
data를 다시 다 불러서 title
값으로 필터링 한 배열을 다시 temp_html
로 붙여넣기를 수행 하는 방식이었다.
리팩토링을 위해 키워드를 검색했을 때 필터링하는 데이터와 처음 접속시에 데이터를 따로 받아서 innerHTML
을 적용하는 부분을 공통으로 쓸 수 있게 계획했다.
그렇게 진행하다 보니 너무 비효율적이라는 생각이 들었다.
굳이 다시 데이터를 받아가면서 지우고 다시 리스팅하는 것은 아닌 것 같다.
예전 Vanilla JS
로 토이 프로젝트를 했을 때 class 속성 display :none;
을 부여해서 데이터를 숨겼던 방식이 기억났다. 그래서 그 방식을 적용하기로 하고 검색기능을 싹 갈아 엎었다.
그러던 중 문제가 발생했다.
const $card = document.querySelectorAll(".card");
console.log($card);
const sortList = $card.filter((i) => {
return $card[i]
.querySelector(".movie-title")
.textContent.toLowerCase()
.includes($value);
});
console.log(sortList);
모든 card 요소를 가져와서 필터링 한 뒤에 class="show"
를 넣고 싶었다.
그러나
filter
메서드가 안써진다.
type을 확인해보니 $card
는 Object.
찾아보니 전개구문을 사용하여 배열로 만들면 해결이 가능하다.
그 이유는 querySelectorAll의 리턴 값은 Dom Tree의 Node List이기 때문이다.
Node List의 간단한 개념을 설명하자면, 먼저 Node는 HTML의 부모요소와 자식요소, 그리고 자기자신의 관한 정보를 담고 있는 객체라고 할 수 있다.
그럼 Node List는 그런 Node의 집합이다.
따라서, querySelectorAll
을 쓰면 여러개의 Node를 가져오는 메서드이기 때문에 Node List가 반환된다.
Node List의 메서드는 따로 있다.
NodeList.item()
: 리스트 내 항목(item)의 인덱스를 반환하고, 인덱스가 범위 외의 경우일 땐 null을 반환.
NodeList.entries()
: iterator 를 반환하여 코드가 콜렉션에 포함된 모든 키/값 쌍을 순회 (이 경우 키는 0부터 시작하는 숫자이고, 값은 각 노드.)
NodeList.keys()
: iterator를 반환하여 콜렉션에 포함된 키/값 쌍의 모든 키를 코드가 순회 (즉, Key는 0부터 시작한다.)
NodeList.values()
: 콜렉션에 포함된 키/값 쌍의 모든 값(nodes)을 코드가 순회할 수 있게 해주는 iterator를 반환합니다.
NodeList.forEach()
: 요소(element)마다 한 번씩, 인자로 전달 받은 함수를 실행하여 요소를 인수(argument)로 함수에 전달 (배열 메서드와 동일)
Node의 개념과 메서드는 아래 링크에서 확인할 수 있음. https://developer.mozilla.org/ko/docs/Web/API/Node
결론적으론, filter 메서드를 사용할 수 없기 때문에 아래와 같이 전개구문으로 형변환하여 사용해야 한다.(형변환 하는 메서드는 많기 때문에 편한 것을 쓰면 됨)
const $card = document.querySelectorAll(".card");
console.log($card);
const sortList = [...$card].filter((i) => {
return !i
.querySelector(".movie-title")
.textContent.toLowerCase()
.includes($value);
});
console.log(sortList);
이런 문제를 하나하나 해결하고 정리할 때마다 지식이 쌓인다.
학습하는 것보다 이렇게 마주치는게 더 기억에 잘 남는 것 같고 개념이해가 된다.