Vanilla JS로 사진 리스트 관리 Web Application 만들기
<이번 플젝의 미션>
- 스크롤이 가능한 리스팅 페이지 만들기
- 전체 페이지 데스크탑-모바일 반응형 페이지로 개발
- 프로필 페이지 (상세 페이지) 만들기
- 데이터 및 사진을 등록, 수정, 삭제가 가능하게 만들기
- 로딩 애니메이션 넣기
- 검색 기능 넣기
- 무한 스크롤 넣기
- LocalStorage 사용하기
- CSS : 상대수치(rem, em) 사용, 애니메이션 구현
- DOM event 조작
- 유저플로우 제작하기
기술 스택 : HTML, CSS, JavaScript, Firebase, .env, Vercel, Git
그리고 결과물은 아래와 같다. 나는 디즈니 캐릭터 관리 페이지를 만들었다.
https://disney-character-teal.vercel.app/
디즈니 캐릭터 리스트
disney-character-teal.vercel.app
아쉬움이 좀 남긴 하지만 디자인 빼고는 전체적으로 만족스럽다.
가장 힘들었던 기능
의외로 로딩 애니메이션. 사실 진짜 별거 아니라고 생각해서 가장 마지막으로 넣은 기능인데 복병이었다 ㅠㅠ 처음에 display: grid로 저 캐릭터 리스트를 뿌려줬는데 로딩애니메이션을 넣으려니 테이블이 무너져 내렸다.... 멘토님께 자문을 구하니 flex로 바꾸는걸 추천하신다 하여 막판에 grid 에서 flex로 바꿔주는 대참사 발생. 그렇게 바꿔주고 난 후에는 리스트 표시 전은 flex-direction: column 으로 하고 리스트를 뿌려줄때는 flex-direction: row 로 변경을 해서 테이블 무너짐 없이 잘 나오게 할 수있었다. 넥플릭스 같은 곳을 보면 무한 스크롤할때 다음 데이터가 나올 곳에 로딩애니메이션이 나오던데 그걸 구현 하고 싶었지만.... 어떻게 해야 하는지 전혀 감이 오질 않았다.
가장 재밌었던 부분
Firebase 조작 하는 부분. Firebase는 처음 사용을 해봤는데, 뭐 이런게 다 있지? 할 정도로 조작이 너무 쉬워서 놀랐다 ㅋㅋ 공식문서만 보고 따라 하면 아무 이슈없이 원하는 대로 동작을 하니 불안할 정도였다 ㅋㅋ 처음에 연결 할때만 좀 애먹은 거 빼곤 실제 사용하는 부분은 코드가 술술 써져서 너무 재밌었다 ㅋㅋ 이번 플젝에서는 유저가 사진을 업로드 하면 Firebase Storage 에 먼저 사진을 업로드 -> URL 을 받아서 DB에 저장 하는 방법으로 했는데 그닥 어렵지 않게 구현할 수 있었다. 구글 최고!
가장 무릎팍 쳤던 부분
무한 스크롤. 시간을 많이 잡아먹었지만 한번 해보니 그렇게 어려운것도 아닌것 같았다. IntersectionObserver를 통해서 구현을 했는데 잘 모르는 부분이라 구글링을 하며 코드를 마구 써넣을땐 모르다가, 나중에 찬찬히 읽어보니 아~ 하는 순간이 왔다.
// infinite scroll 제어
const io = new IntersectionObserver( (entries) => {
entries.forEach(async entry => {
//출력할 데이터가 있는지 확인하고 없으면 disconnect 하고 종료
if(!characterStore.state.searchText) {
if (totalViewCount === characterStore.state.totalCount) {
console.log(
"총 데이터 수 :", characterStore.state.totalCount,
" Page 수 :", characterStore.state.page ,
" 모든 캐릭터 출력 완료!");
io.disconnect();
return;
}
} else {
if (totalViewCount === characterStore.state.searchedCount) {
console.log(
"총 데이터 수 :", characterStore.state.searchedCount,
" Page 수 :", characterStore.state.page ,
" 모든 캐릭터 출력 완료!");
io.disconnect();
return;
}
}
if (entry.isIntersecting) {
// 다음 타겟이 뷰포트에 들어왔으므로 처음 타겟은 unobserve
io.unobserve(last);
if (!characterStore.state.searchText) {
try {
await getNextList();
} catch (error) {
console.log("다음페이지 로딩 실패 : ", error);
}
} else {
characterStore.state.page += 1
}
// 페이지별로 리스트를 배열로 갖고 있으므로 인덱스는 page-1
const listIndex = characterStore.state.page - 1;
const charNextList = characterStore.state.characters[listIndex];
charNextList?.forEach(character => {
this.el.innerHTML += getHtml(character);
totalViewCount++;
});
const nodes = this.el.querySelectorAll(".card");
const newLast = nodes[nodes.length- 1];
// 마지막 div 요소를 감시한다
io.observe(newLast);
}
})
}, {threshold: 0.7});
내가 구현한 무한 스크롤 부분인데, 한가지 의문점이 드는것은 리스트를 뿌려줄때 어떻게 현재 이미 뿌려진 리스트를 구분을 하는 걸까?
나는 리스트를 이중 배열로 넣었다 예를 들면 이런식이다.
const 캐릭터리스트 = [[{캐릭터1},{캐릭터2},{캐릭터3},{캐릭터4}],[{캐릭터5},{캐릭터6},{캐릭터7},{캐릭터8}]];
그러면 현재 페이지수를 보고 페이지 수 -1 을 한 인덱스 번호의 요소를 화면에 뿌려주는 식으로 했는데 이런 방법이 맞는지 모르겠다. 이 부분은 멘토님께 여쭤볼 예정이다.
무한 스크롤 관련하여 또 무릎팍 쳤던 순간이 있는데 바로 버블링을 이용한 이벤트 위임 부분이다. 처음에 데이터가 뿌려질때는 없었던 리스트들이 나중에 스크롤을 하면 나타나는 형식이라 addEventListener 를 해도 자꾸 없는 요소라는 에러가 떴다. 이럴때 바로 부모요소에 addEventListener를 해주면 되는거다. 그리고 target 을 통해 어떤 요소를 클릭했는지 알아내서 동작을 하도록 해주었다. 이건 정말 생각지도 못한 부분이어서 한편으로는 재미있었다 ㅋㅋ 수업시간에 버블링이라는게 있구나 하고 듣기만 하고 실제로 어떻게 쓰는지 몰랐는데 바로 이거라니! 하는 마음이었다 ㅎㅎ
개발을 끝내고 Vercel을 통해 Deploy 하면서 강의 때 배운 환경변수도 잊지 않고 설정해 주었다. 정보는 소중하니까...
이것저것 뭔가 새로운걸 많이 시도해서 재밌었던 플젝이었던거 같다.