NAVER에서 제공하는 라이브러리로 인터랙티브한 컨텐츠를 만드는데 특화된 라이브러리다. 사실 공식 릴리즈 된지는 꽤 되어서 이미 많은 사람들이 사용하는듯 하다. 해당 라이브러리를 vanilla js 로 바로 사용해보자!
태초 공개할 때는 Jindo
프레임워크(과거 NAVER에서 제공하던 js 라이브러리)부터 쌓인 노하우를 녹였다고 한다! naver jindo 는 이제 역사의 뒤안길로 사양된 웹 FE 프레임워크 이며, 이걸 사용 했다거나 기억한다면,, 당신은,,,,!! NHN의 안과 밖: Jindo와 jQuery 을 읽어 보셔야 합니다 ㅎ 나의 시대는 아니지만 가끔 이렇게 유물을 가져오는 재미가 쏠쏠합니다. ㅎ 🫡
egjs
는 "네이버의 FE 오픈소스 컴포넌트 그룹" 이며 미디엄 naver-fe-platform 에서도 소식을 볼 수 있다. 그리고 gihub 와 official docs 에서 확인할 수 있 듯, 되게 일관되게 정리가 잘 되어 있고 잘 관리되는 중인 것을 볼 수 있다.
egjs
의 공식 공개는 [ jQuery 기반의 egjs 라이브러리를 공개합니다. ] 글에서 맛볼 수 있다. [ egjs 2023년 4분기 릴리즈 소식을 전해드립니다. ] 글에서 보면 21, 22, 23년도 가파른 상승세를 보여주는 것을 볼 수 있다.
docs가 굉장히 잘되어있어서 특별한 가이드 없이 바로 따라서 사용할 수 있다!
flicking
사용하기캐러셀(Carousel)은 사전적으로는 "회전목마" 라고 하며, 이미지나 텍스트의 슬라이드를 가로로 슬라이드시켜 여러 개를 표시하는 컴포넌트를 말한다.
요즘 이런 캐러셀을 0 부터 직접 만드는 일은 "흔하지 않는 것" 같다. 특정 니즈에 의해서나, 브랜딩 관점에서의 디자인 템플릿이 완전하게 정해지지 않으면 거의 라이브러리를 활용하게 된다.
Carousel
컴포넌트를 만드는 library 이며 egjs 에서는 간판인 프로젝트 이다. "Everyday 30 million people experience" 라고 소개 되어 있다.타 라이브러리 대비 강점은 "크로스 프레임워크" 이다.
기본적으로 vanilla javascript (물론 typscript 는 사용하는 듯 하다.) 로 짜여져 있고, 이를 다른 framework 에서 적용하기 위해 Adapter pattern
으로 배포해 프레임워크 전용 컴포넌트와 동일한 사용성과 동작방식을 가지게 한 것이다.
일단 도입이 굉장히 편하다. 데모 페이지 에서 보고 바로 쉽게 활용할 수 있다.
<!-- EGJS flicking CDN -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/egjs-flicking/4.11.2/flicking.css"
crossorigin="anonymous"
/>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/egjs-flicking/4.11.2/flicking.pkgd.min.js"
crossorigin="anonymous"
></script>
<div id="flick" class="flicking-viewport">
<div class="flicking-camera">
<div class="flicking-panel">1</div>
<div class="flicking-panel">2</div>
<div class="flicking-panel">3</div>
<div class="flicking-panel">4</div>
<div class="flicking-panel">5</div>
</div>
</div>
<div class="block is-flex is-justify-content-center">
<span id="btn-prepend" class="button mr-2 is-info is-outlined">Prepend</span>
<span id="btn-append" class="button mr-2 is-info is-outlined">Append</span>
</div>
const flicking = new Flicking("#flick", {
renderOnlyVisible: true
});
let minIdx = 1;
let maxIdx = 5;
const prependBtn = document.querySelector("#btn-prepend");
const appendBtn = document.querySelector("#btn-append");
prependBtn.addEventListener("click", () => {
flicking.prepend(`<div class="flicking-panel">${--minIdx}</div>`)
});
appendBtn.addEventListener("click", () => {
flicking.append(`<div class="flicking-panel">${++maxIdx}</div>`)
});
class 의 instance 변수로 편하게 작동 방식이나 전체적인 특징을 컨트롤하기 편하다. 심플하게 위와 같이 세팅하면 아래와 같다! css 도 세팅해야 보기 편한데, 이는 github - repo 로 대체 한다.
<div class="flicking-panel">
들은 dynamic rendering 이 된다. flicking
안에 다시 flicking
을 배치하는데 아주 안정감(?)이 있다고 생각이 된다.<div id="flick1" class="flicking-viewport">
<div class="flicking-camera">
<div class="flicking-panel">1</div>
<div class="flicking-panel nested-wide">
<div id="flick2" class="flicking-viewport">
<div class="flicking-camera">
<div class="flicking-panel">2.1</div>
<div class="flicking-panel">2.2</div>
<div class="flicking-panel">2.3</div>
</div>
</div>
</div>
<div class="flicking-panel nested-wide vertical">
<div id="flick3" class="flicking-viewport vertical">
<div class="flicking-camera">
<div class="flicking-panel">3.1</div>
<div class="flicking-panel">3.2</div>
<div class="flicking-panel">3.3</div>
</div>
</div>
</div>
<div class="flicking-panel">4</div>
<div class="flicking-panel">5</div>
</div>
</div>
const flicking1 = new Flicking("#flick1");
const flicking2 = new Flicking("#flick2", {
bounce: 0,
bound: true,
nested: true,
});
const flicking3 = new Flicking("#flick3", {
bounce: 0,
// bound: true,
nested: true,
horizontal: false,
});
실제 예제에는 <div class="flicking-camera">...</div>
이 빠져있는데, 공식 홈페이지에는 해당 div 가 빠져있으면 캐러셀 이벤트가 작동을 안하더라..!
여튼 이렇게 Nested 한 형태로 flicking-viewport 안에 다시 flicking-viewport 안에 다시 flicking-viewport 를 주면서 동시에 vertical
로 다양한 형태의 중첩 캐러셀을 심플하게 구현할 수 있음이 좋았다.
근데 bound 값을 주니까 작동을 안하고,, vertical
에서 왜 첫 번째 친구가 표현되지 않는지,, css issue 인지,,
사실 이렇게 바닐라 js 에서 바로 활용하려고 보니 아직 핵심 class 인자 값 (flicking-panel, nested-wide, vertical, flicking-camera 등) 이 와닿지가 않는다..!
<div id="flick4" class="flicking-viewport vertical">
<div class="flicking-camera">
<div class="flicking-panel">One</div>
<div class="flicking-panel">Two</div>
<div class="flicking-panel">Three</div>
<div class="flicking-panel">Four</div>
<div class="flicking-panel">Five</div>
<div class="flicking-panel">Six</div>
</div>
</div>
const flicking4 = new Flicking("#flick4", {
circular: true,
horizontal: false,
});
// Simple AutoPlay function
setInterval(function () {
flicking4.next().catch(() => {}); // Ignore errors
}, 2000); // Change slide every 2 seconds
flicking-plugin 이 따로 있으며 여기서 AutoPlay
를 통해 자동 넘기기를 구현할 수 있다.
하지만 CDN 으로 바로 사용하는 것은 지원하지 않기에 직접 소스 코드 내장하기 싫어서, setInterval
과 함께 간단하게 flicking4.next().catch...
로 구현할 수 있다.
official docs 에서는 이런 리스트 형식, 날씨와 같은, 자동 넘기기를 심플하게 구현할때 아주 요긴하게 쓸만하다.
여기서 꼭 주의할 점은 [ Sum of panel sizes - panel size >= viewport size ] 이 지켜져야 circular: true,
이 제대로 작동한다는 점!!
그 외 실제로 사용하는 예제들이나 시선을 사로잡는 캐로셀 컴포넌트 Flicking 을 소개합니다 에서 실전 flicking 예시를 좀 더 많이 살펴볼 수 있다!!
infinitegrid
사용하기flicking
만큼 사랑받는 infinitegrid
라는 라이브러리도 있다. 물론 flicking
자체에서도 Grid 로 활용해 배치하는 방법도 있지만, pinterest
가 아래와 같이 배치하는 이미지 방식, 그리고 "무한 스크롤" 을 구현하는 것은 좀 귀찮다.그리고 이미지 사이징을 고려하면서 배치하는 것 역시 귀찮고, 때에 따라 다르게 보여주고자 하면 배보다 배꼽이 큰 느낌이 있다.
Naver D2 - 카드형 UI와 eg.InfiniteGrid 3.0 에서 이런 고민의 흔적을 볼 수 있으며, 이에 따라 우리는 InfiniteGrid
를 잘 찍어먹을 수 있게 되었다.
Masonry
의 사전적 의미는 벽돌을 쌓아올린다는 뜻으로, 웹에서의 Masonry 레이아웃은 하나의 축(대부분 열)이 일반적인 그리드 레이아웃을 사용하고 다른 한 축이 Masonry 레이아웃을 사용하는 레이아웃 방법을 말한다. (아래 사진)위 Masonry
배치 컨셉을 new InfiniteGrid.MasonryInfiniteGrid
로 구현할 수 있다. - Docs 가이드 페이지, 실제 구현된 데모 페이지
테스트용으로 많은 랜덤 이미지도 제공해줘서 편하다 ㅎ, 그리고 최소한의 css를 좀 먹여야 적용되는걸 편하게 볼 수 있으니 이점 유의! (item
자체의 width 를 안주면 이미지 크기에 따라 width 가 꽉 차버릴 수 있다!)
<!-- EGJS infinitegrid CDN -->
<script src="https://unpkg.com/@egjs/infinitegrid/dist/infinitegrid.min.js"></script>
<style>
.container {
margin: 50px;
}
.item {
width: 250px;
}
.item img {
width: 100%;
}
</style>
<div class="container"></div>
function getItems(nextGroupKey, count) {
const nextItems = [];
for (let i = 0; i < count; ++i) {
const num = nextGroupKey * count + i;
nextItems.push(`
<div class="item">
<div class="thumbnail">
<img src="https://naver.github.io/egjs-infinitegrid/assets/image/${
(num % 33) + 1
}.jpg" alt="egjs" />
</div>
<div class="info">egjs ${num}</div>
</div>
`);
}
return nextItems;
}
const ig = new InfiniteGrid.MasonryInfiniteGrid(".container", {
gap: 5,
});
ig.on("requestAppend", (e) => {
const nextGroupKey = (+e.groupKey || 0) + 1;
ig.append(getItems(nextGroupKey, 10), nextGroupKey);
});
ig.renderItems();
container
div 안에 dynamic 하게 item
을 dynamic 하게 만들어서 만든 배열을 return 하는 함수 getItems
를 활용한다. 위와 같이 기가 막힌 "무한 스크롤" + "Masonry" 가 만들어 졌다.
"스켈레톤 컴포넌트, Skeleton UI" 와 같은 "컴포넌트 미리보기, loading 되는 것 처럼", "Placeholder" 를 추가할 수 있다.
ig.setPlaceholder({
html: `<div class="placeholder"></div>`,
});
ig.on("requestAppend", (e) => {
const nextGroupKey = (+e.groupKey || 0) + 1;
e.wait();
e.currentTarget.appendPlaceholders(5, nextGroupKey);
setTimeout(() => {
e.ready();
ig.append(getItems(nextGroupKey, 10), nextGroupKey);
}, 1000);
});
ig.renderItems();
JustifiedInfiniteGrid
와 같이 이미지를 배열에 맞춰, 이미지도 비율에 맞게 리사이징 된 상태로 배치하는 것도 있으며
FrameInfiniteGrid
, PackingInfiniteGrid
와 같이 정해진 비율, 마치 css grid
사용하는 것과 같은 효과를 내는 패턴도 가능하다.
데모가 잘되어 있으니 꼭 살펴보는 것을 추천한다! 위에서 사용한 코드 데모 결과는 아래 2개 링크에서 바로 확인 가능하다!
naver 가 사내 library를 확장해 global open source 까지 만들고 관리하는 것을 보면, 국내에서는 그래도 오픈 소스 개발 생태계에 가장 활발하지 않는가 생각이 든다.
메인테이너도 활발하고issues
피드백도 빠른것 같다. 더욱이 test code 의 coverage 도 같이 신경쓰고 있으며 글 작성 시점 기준, flicking
은 80% 이상의 test code coverage 를 보여주고 있다.
또, EGJS 에 "I'm Ready!" 와 같은 심플하지만 활용할만한 라이브러리도 많다.
최근에 쓴 글이 너무 무겁기도 했고, 요즘 "개발"보다 너무 코딩에만 몰두한 것 같아 self refresh 할 겸 light한 library를 다뤄본다.
오늘도 재밌게 읽었어요!