Open API를 활용하여 async/await으로 비동기처리하는 SPA 웹 영화검색사이트를 만들었습니다.
// 검색
const handleSubmit = (e) => {
e.preventDefault();
// 키보드 gif 숨기기, 로딩 스피너 보이기, 검색결과 새로 출력
beforeTypeEl.classList.add('hide');
loadingEl && loadingEl.classList.add('show');
// 검색 api 호출 및 렌더
countPages = selectCountEl.value;
searchData(searchInputEl.value, selectYearEl.value, countPages);
};
searchFormEl.addEventListener('submit', handleSubmit);
searchData
함수에 입력된 값을 인자로 넣습니다.searchData
함수는 매개변수로 받은 값으로 api를 호출하고, response 값을 후처리하여 render 함수로 보내주는 역할을 합니다.document.querySelector
를 사용하여 선택한 요소에.value
를 붙여서 값을 선택했습니다.beforeTypeEl
)를 숨기고, 로딩 요소를 보여줍니다.searchData
함수에서 response 값을 받고, 렌더링 직전에 제거됩니다.영화 상세페이지에서는 고화질의 포스터를 출력합니다.
https://m.media-amazon.com/images/M/MV5BYTdiOTIyZTQtNmQ1OS00NjZlLWIyMTgtYzk5Y2M3ZDVmMDk1XkEyXkFqcGdeQXVyMTAzMDg4NzU0._V1_SX300.jpg
${
detailPoster !== 'N/A'
? `<img class="detailPoster-img" src=${detailPoster.replace('SX300','SX450')} alt="${detailTitle} poster"/>`
: `<div class="no-image"></div>`
}
📦src
┣ 📂css
┃ ┃ ┣ 📜main.scss
┃ ┃ ┗ 📜reset-css.css
┣ 📂js
┃ ┃ ┣ 📜api.js
┃ ┃ ┗ 📜handlePushstate.js
┣ 📂pages
┃ ┣ 📂Detail
┃ ┃ ┣ 📜detailMarkup.js
┃ ┃ ┗ 📜index.js
┃ ┗ 📂Search
┃ ┃ ┗ 📜index.js
┃ ┃ ┣ 📜searchData.js
┃ ┃ ┣ 📜searchMarkup.js
┃ ┃ ┣ 📜searchResultsRender.js
┗ ┗ ┗ 📜searchYearOption.js
// api.js
export async function getMovies(title, year, page) {
try {
const res = await fetch(
`https://omdbapi.com/?apikey=7035c60c&s=${title}&page=${page}&y=${year}`
);
const json = await res.json();
return json;
} catch (error) {
console.log(error);
}
}
export async function getMovieDetails(movieId) {
try {
const res = await fetch(
`https://omdbapi.com/?apikey=7035c60c&i=${movieId}&plot=full`
);
const json = await res.json();
return json;
} catch (error) {
console.log(error);
}
}
// searchData.js
import { getMovies } from '/src/js/api';
export const searchData = async (keyword, year, countPages) => {
const moviesData = [];
for (let i = 1; i <= countPages; i++) {
const response = await getMovies(keyword, year, i);
response.Response === 'True'
? moviesData.push(...response.Search)
: moviesData.push(...[]);
}
searchResultsRender(moviesData);
};
// src/pages/Detail/index.js
import { getMovieDetails } from '/src/js/api';
const renderDetail = async (id = 'tt0114709') => {
const response = await getMovieDetails(id);
생략...
};
export default renderDetail;
제일 큰 수확은 아무래도 API를 사용해서 만든 첫 프로젝트이기에 비동기 함수 처리에 대해 알게 된 부분이 제일 큰 수확이었습니다. 더불어 처음으로 번들러를 적용해봤다는 것도 굉장히 의미있었습니다. 그 이외에는 아래와 같습니다.
- HTML는 input 태그를 활용하며 유효성 검사하는 법, innerHTML을 활용하는 법, select option 태그의 value를 가져오는 법
- CSS는 input cursor 크기를 조절하는 법과, CSS 요소의 import 방식.
- Git은 branch 병합하는 것과 삭제하는 것,
.gitignore
와.env
- JavaScript는 나머지 매개변수, AJAX, SPA 라우터 구현, 커스텀 이벤트, DOMContentLoaded 이벤트, match 메서드, dataset 속성
- bundler를 NPM,
package.json
과package-lock.json
의 역할 및 차이점, dependencies, SEMVER(Semantic Versioning)
알게된 것 중 더 자세히 알고 싶은 것에 대해서는 위에서도 언급하였듯이 포스팅으로 남겨뒀습니다.
// 무한스크롤
const infinite = async () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
page += 1;
getDataAndRender();
}
};
window.addEventListener('scroll', infinite);
몇시간 내내 씨름한 결과 package.json
에 라이브러리들을 잘못 설치하고 scripts를 잘못 써서 그런 것이었습니다! 여러 에러들이 동시에 떴기에 당시에는 정확히 뭐가 문젠지 몰랐는데, 정리하면서 잘 알게 됐습니다.
"build": "parcel index.html”
에서 "build": "parcel build index.html"
로 수정했습니다."@types/autoprefixer"
를 삭제했습니다. "parcel"
이 없었습니다. commit을 뒤져보니 다른 라이브러리 삭제 후 재설치하는 과정에서 삭제됐었기에 다시 설치했습니다.제일 뿌듯하고 안도감 드는 빌드완료 문구!
그리고 자꾸 경고메시지가 떠서 찾아보니 현재 parcel은 autoprefixer
라이브러리를 별도로 설치할 필요가 없다고 합니다. package.json
의 browserslist 설정에 따라 자동으로 해당 기능을 활성화하여, 자동으로 CSS에 벤더 프리픽스를 추가해준다고 했습니다.
npm uninstall autoprefixer
도 진행해주었습니다.비동기 처리를 처음 해보아서 api를 호출하여 response값을 받아 render하고, 더보기 버튼으로 영화 목록을 더 불러오는 것만 구현하는 것도 어려웠다. 추가 작업을 진행할 때는 익숙해져있던 때라 크게 어렵지 않게 기능 구현했다. 다만 PJAX 방식의 SPA를 구현하는 것은 정말 어려웠다. 가장 오래걸렸던 작업이었던 것 같다.
좋았거나 잘했다고 생각하는 점
아쉬웠기에 다음에 시도해볼 점
로그인 기능, day&night 기능, 최근 검색어, 최근 본 영화, 좋아요 기능
등등을 추가로 작업하며 사이트를 더 멋지게 만들어나가보고 싶다.