필수과제였던 메인 기능들을 완성하고서 거의 마지막 단계로 모듈화를 수행하려고 했다.
이번 단계에 할 일은
- 모듈화 및 코드 분리
- API 요청과 관련된 코드와 DOM 조작 코드를 별도의 모듈로 분리하여, 코드의 재사용성과 유지보수성을 높이세요.
- 예를 들어, API 요청은
api.js
에서, UI 업데이트는ui.js
에서 처리하는 식으로 각각 분리합니다.- 중요 포인트: 함수로 모듈을 분리할 때 매개변수와 반환값을 명확히 정의하여 재사용성을 높이는 데 중점을 두세요.
그동안main.js
에 차곡차곡 쌓아뒀던... 나의 213줄짜리 코드를 나눠줄 차례다.
워낙 길다 보니까, 이게 서로 종속적인 상태로 묶여있었는데.. 시작하려니 참 감이 안 왔다.
접근은 그냥 로직이 복잡하지 않은 기능 순서대로, 차근차근 옮겨보려 했다. 헤메이다가 깨달은 결론 먼저 스포하자면,
코드분리 작업은 작은 단위로 쪼개어 옮겨 테스트 해보는 게 중요하다!
card.js
, modal.js
분리 및 디버깅 완료main.js
코드길이 213줄 -> 158줄...목표한 최종 Structure
/PROJECT └── src ├── main.js # 메인 로직 구현 (아직 158줄) ├── api.js # fetch 기능 ├── ui.js # UI 조작 관련 기능 ├── card.js # 영화 카드 생성 기능 (CLR) ├── modal.js # 모달 관련 기능 (CLR) └── bookmark.js # 북마크 주요 기능 (추후 개발 예정)
현재 주어진 달성 사항은 API 요청과 관련된 코드와 DOM 조작 코드를 별도의 모듈로 분리해야 한다.
나는 조금 더 욕심을 부려서.. 현재 목표한 프로젝트의 주요 기능들 (영화 카드 보여주기 / 영화 상세 모달화 / 북마크 추가 및 보여주기 기능)을 따로 모듈화하여 관리하고자 했다.
아니, 근데 문제는 API였다!!! 하라고 한 게 안되는 상황 ㅋㅋ 아니 모달이랑 카드는 잘 옮겨졌는데 말이지...
let dataset = []; // 데이터셋을 담을 변수
fetch("https://api.themoviedb.org/3/trending/movie/day?language=ko", {
method: "GET",
headers: {
accept: "application/json",
Authorization:"API_KEY",
},
})
.then((res) => res.json()) // JSON으로 받아오기
.then((res) => {
let rows = res["results"]; // Result만 불러오기
dataset = rows; // 굳이 한 코드
// 평가한 사람이 많은 순으로 정렬 (내림차순)
rows.sort((a, b) => b.vote_count - a.vote_count);
// 각 영화에 대해 카드 생성
rows.forEach((movie) => {
const card = createMovieCard(movie);
showCardsContainer(card);
// 카드 클릭 시 openModal
card.addEventListener("click", () => {
openModal(movie.id, dataset);
});
});
})
.catch((err) => console.error(err));
2. [모듈화 전] 검색 기능 - Search/Movie
searchBtn.addEventListener("click", async function () {
const searchQuery = searchTitle.value.trim().toLowerCase();
if (!searchQuery) {
alert(`[오류] 키워드를 입력해주세요!\n검색어가 입력되지 않았습니다.`);
return;
}
try {
const response = await fetch(
`https://api.themoviedb.org/3/search/movie?query=${searchQuery}&include_adult=false&language=ko`,
{
method: "GET",
headers: {
accept: "application/json",
Authorization:
"API_KEY",
},
}
);
if (!response.ok) {
throw new Error("API 요청 실패");
}
const data = await response.json();
const sortedSearchResult = data.results.sort(
(a, b) => b.vote_count - a.vote_count
);
dataset = sortedSearchResult;
movieList.innerHTML = ""; // 기존 목록 초기화
if (sortedSearchResult.length > 0) {
sortedSearchResult.forEach((movie) => {
const card = createMovieCard(movie);
showCardsContainer(card);
card.addEventListener("click", () => {
openModal(movie.id, dataset);
});
});
}
// 검색결과가 없을 때
else {
const noResultContainer = document.createElement("div");
noResultContainer.classList.add("no-result-container");
const noResultMessage = document.createElement("p");
noResultMessage.textContent = "검색 결과가 없습니다.";
noResultContainer.appendChild(noResultMessage);
movieList.appendChild(noResultContainer);
}
} catch (error) {
console.error(error); // 오류 발생 시 콘솔에 출력
}
backToMain();
});
코드 안에 이벤트 리스너부터 DOM 조작 명령어 등등이 혼재되어 있어서 API를 건들기가 어려웠던 것도 사실...
리팩토링도 너무 필요한 상황이라, 일단 main화면 내 trending movie 데이터를 불러와 Card List 표출하는 것부터 손을 댔다.
api.js
파일을 생성해주고, 그 안에 fetch
구문을 길게 만들었던 이 녀석을 변수에 담아주었다.
const options = {
method: "GET",
headers: {
accept: "application/json",
Authorization:
"API_KEY",
},
};
async function getTrendingMovies(url) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`error! status : ${response.status}`);
}
const data = await response.json();
return data; // 데이터를 반환
} catch (err) {
console.error(err);
throw new Error(`error! status : ${err.message}`);
}
}
export { getTrendingMovies };
그래도 위의 코드보다는 조금 줄어든 상황이라, 여기까진 좋았다.
main.js
에서는 results 값만 받아오고, 동시에 정렬도 수행하고 오면 좋겠다!"는 생각을 했던 것..async function getTrendingMovies(url) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`error! status : ${response.status}`);
}
const data = await response.json();
return data; // 데이터를 반환
} catch (err) {
console.error(err);
throw new Error(`status : ${err.message}`);
}
}
내가 붙이고 싶었던
const data = await response.json();
const sortedSearchResult = data.results.sort(
(a, b) => b.vote_count - a.vote_count
);
이런 코드를 조합했다. 그러니까 아무리 main.js
에서 getTrendingMovies
를 불러와도 안되는 거다.. API 응답에 에러 발생.
data.results.sort()
를 수행하기 전에, data["results"]
를 먼저 콘솔에 찍어보자.sort()
메소드는 배열에만 작동한다.await
가 제대로 동작하지 않으면, 데이터가 아직 로드되지 않았는데 .sort()
를 호출하려고 할 수도 있어서..(?)[api.js]
import { API_KEY } from "./api-key.js";
const options = {
method: "GET",
headers: {
accept: "application/json",
Authorization: API_KEY,
},
};
// [main] trending movie
async function getMovieData(url) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`status : ${response.status}`);
}
const data = await response.json();
return data;
} catch (err) {
throw new Error(`status : ${err.message}`);
}
}
async/await
으로 불러온 데이터를 다시 비동기로 JSON화 하여 바로 return하는 구조.[main.js]
let dataset = [];
async function main() {
try {
dataset = await getMovieData(urlTrending);
return dataset["results"].sort((a, b) => b.vote_count - a.vote_count);
} catch (err) {
throw new Error(`error! status : ${err}`);
}
}
dataset = await main();
createCardNEvent(dataset);
그리고 main()을 또다시 비동기로 실행해준다는 점. 왜냐면 main.js
내에서도 main이라는 비동기 함수 안에 구성을 해 주어서..!
그래도 웹페이지 로드하는 데에는 큰 문제점이 없는 상황.
console.log()
찍어보고 테스트 하는 거. 그래도 스스로 이것저것 시도해보고 생소한 개념에 한 걸음 가까워진 경험이었다고 생각함.