새로 추가되는 멤버카드들을 가로로 넘겨볼 수 있도록 나는 캐러샐에 대해 공부해보기로 했다.
캐러샐? 솔직히 처음 들어봤다. 그게 뭔데.
캐러샐은 영어로 회전목마라는 뜻이다. 회전목마처럼 돌아가면서 하나의 컴포넌트가 돌아가면서 여러 요소를 보여주는 것을 의미하는데 대표적으로 우리가 웹사이트에서 흔하게 볼 수 있는 광고배너나 이벤트배너 같은 것들이다.
직접 드래그나 스왑을 해서 넘길 수도 있고 자동으로 넘어가기도 한다.
내가 구현하고 싶었던 것은 4개씩 진열된 멤버카드들이 옆으로 넘길 때마다 한 장씩 넘어가는 것이었다. 처음에 생각한 것은 부트스트랩을 사용하는 것이었는데 부트스트랩의 캐러샐은 한 칸에 이미지를 하나씩 넣고 넘기는 구성으로 그 안에 4개의 멤버카드들을 넣고 하나씩 넘기는 것을 구현하는 게 어려워보였다.
그렇게 캐러샐에 대해 구글에 서치를 하고 있던 때에 이 블로그를 발견했다.
https://velog.io/@grinding_hannah/JavaScript-자바스크립트로-캐러셀Carousel-구현하기
이 분은 간단하게 캐러샐을 구현하기 위해 숫자 박스 하나가 화면에 보이고 이전과 다음 버튼을 누르면 각각 뒤로 한 칸, 앞으로 한 칸 숫자박스가 이동하는 형식으로 재현하셨다.
[출처: https://velog.io/@grinding_hannah]
이 캐러샐을 딱 보자마자 '아! 저 숫자박스가 보이는 상자의 크기를 늘리고 그 안에 있는 숫자들을 카드로 대체하면 되지 않을까?' 라는 생각을 했다.
그렇다면 캐러샐을 어떤 식으로 구현하셨는지 어느 정도의 이해가 필요할 것 같았다.
이 분은 이런 식으로 캐러샐을 구현하신 것 같았다.
- 숫자박스가 가로로 나열된 리스트를 만들고 이를 '컨테이너'에 담는다.
- 숫자박스가 하나만 보일 수 있도록 나머지 부분을 가려주는 '윈도우'를 만든다.
- 버튼을 누르면 컨테이너를 숫자박스 하나만큼을 이동시킨다.
- 컨테이너를 제자리에 돌려놓고 컨테이너 안에 있는 숫자박스들의 배열을 바꿔준다.
1번 2번은 css의 영역인 것 같았고 기능적인 부분에서 중요해보이는 것은 역시 컨테이너를 움직이고, 안에 있는 내용물들의 순서를 바꿔주는 것이었다.
코드를 보니 컨테이너를 움직이는 부분은 이 부분 같았다.
const container = document.querySelector(".container");
container.style.transform = `translateX(${direction * (100 / 요소의 갯수)}%)`;
container.style.transitionDuration = '500ms';
컨테이너의 id를 가진 것을 컨테이너라고 지정해주고, 이를 노드.style.transfrom = translateX(이동거리)
함수를 이용해서 움직여주는 것이었다.
이 때, 가로로 이동하는 것이어서 translate뒤에 X가 붙는 것이라고 추측해보았고 그 아랫 줄은 속도를 조절해주는 것 같았다.
이동이 끝났을 때 무언가를 실행 :
ontransitionend
실행 취소 :removeAttribute
위의 컨테이너를 이동하는 구문을 적어준 뒤, ontransitionend을 사용하여 누른 버튼에 따라 다음 동작을 실행시키도록 한다.
그리고 그 동작은 removeAttribute을 통해 이동을 되돌리는 것이 될 것이다.
그냥 이동만 되돌리면 전과 바뀌는 것이 없기 때문의 내용물의 숫자박스를 이동시켜야 한다.
이때, 이전 버튼을 누르면 맨 앞에 있던 숫자박스가 뒤로 붙고,
다음 버튼을 누르면 맨 뒤의 있던 숫자박스가 앞으로 붙을 것이다.
맨 앞의 요소를 떼어서 맨 뒤에 붙이기 :
appendChild
맨 뒤의 요소를 떼어서 맨 앞으로 붙이기 :insertBefore
그렇게 완성하신 코드가 이렇게 되신 것 같다.
function translateContainer(direction) {
const selectedBtn = (direction === 1) ? 'prev' : 'next';
container.style.transitionDuration = '500ms';
container.style.transform = `translateX(${direction * (100 / 5)}%)`;
container.ontransitionend = () => reorganizeEl(selectedBtn);
}
function reorganizeEl(selectedBtn) {
container.removeAttribute('style');
(selectedBtn === 'prev') ? container.insertBefore(container.lastElementChild, container.firstElementChild) : container.appendChild(container.firstElementChild);
}
나는 이 캐러샐을 프로젝트에 넣기전에 연습용 html을 만들어 기능을 먼저 구현해보기로 마음 먹었다.
우선 css의 '윈도우'박스와 '컨테이너'박스의 크기를 조절해주었다.
그리고 블로그 주인장님이 리스트로 만든 숫자박스 대신에 조심스럽게 카드를 넣어보았다.
블로그 주인장님의 <body>
코드는 아래와 같은데,
<div class="window">
<ul class="container">
<li class="cell">5</li>
<li class="cell">1</li>
<li class="cell">2</l>
<li class="cell">3</li>
<li class="cell">4</li>
</ul>
</div>
이 <li class="cell">5</li>
의 숫자 대신에 붙일 카드의 형식(부트스트랩에서 따온)을 컨테이너 안에 넣었다.
<div class="window">
<ul class="container">
<li class="cell">
<div class="col">
<div class="card h-100">
<img src="https://pimg.mk.co.kr/news/cms/202304/29/20230429_01160101000003_M00.jpg"
class="card-img-top" alt="" />
<div class="card-body">
<h5 class="card-title">수제버거</h5>
<p class="card-text">말해뭐해</p>
</div>
</div>
</div>
</li>
</ul>
</div>
위는 하나의 카드만 넣었을 때고, 다른 카드들까지 오류가 나지 않도록 조심조심 넣었다.
(사실 넣었을 때 배열이 계속 일자로 뜨지 않고 카드 한장이 아래로 내려가서 윗줄의 카드와 겹친다던지 하는 문제가 계속 발생했는데 이는 컨테이너의 크기를 늘리고 카드의 크기를 줄이는 방법으로 해결했다.)
그리고 이 내용물을 또 다른 <div>
에 넣어 이 안에 버튼까지 위아래로 넣어서 css로 배열을 만져주었다.
물론 코드를 직접 쓴 게 아니라 이미 있는 구조를 수정해서 만든 것이고 디테일까지 완벽하게 이해한 건 아니지만
간단하게 원리를 이해하고 원하던 모양을 정확히 구현해냈다는 게 너무너무 기쁘다.
시간을 꽤 많이 들였는데 뭔가 그럴싸한 걸 만들기도 했고 만족스러운 노력이었다.
컨테이너의 내용을 움직이는 데에 사용하는 것 같은 코드 중간의 query에 대해 자세히 공부하지 못한 점, 그리고 캐러샐을 5번 넘기면 오른쪽에 밀림 현상이 발생하는 것에 대한 원인을 알아내지 못해서 해결하지 못한 점이 아쉬웠다.
(위 처럼 오른쪽에 다음 셀이 살짝 보이게 된다.)
그래도 언젠가는 알게 될 날이 오겠지. 화이팅!~