๐ป Bennett & Clive ํด๋ก ์ฝ๋ฉ
์์ ์์ ์ ๊ฐ์ด ๊ธฐ์กด ์ปจํ ์ธ ์์ญ์ ๊ณ ์ ๋ ์ํ์์ ๋ค์ ์์ญ์ด ์ฌ๋ผ์ค๋ ํจ๊ณผ๋ฅผ ๋ง๋ค์ด ๋ณด์๋ค. ์คํฌ๋กค์ ํ๋ค ์ค๊ฐ์ ๋ฉ์ถ๋ฉด ์ค๋ ๋๋ ํจ๊ณผ๊น์ง ์ถ๊ฐํ์๋ค.
์ฝ๋ ์ค๋ช ์ ์๋์ ๊ฐ๋ค.
HTML
<div class="container">
<div class="sticky">
<div class="item">
<div class="content">
content 1
</div>
</div>
<div class="item">
<div class="content">
content 2
</div>
</div>
<!-- ...์ดํ ์๋ต(item ์ด ๊ฐ์: 5) -->
</div>
</div>
CSS
mask-image
์์ฑ ์ฃผ๊ธฐ (ํด๋น ์์ฑ์ด ์์ด์ผ ์คํฌ๋กค ์ ๋ค์ ์์ญ์ด ๋ณด์ฌ์ง) .container {
position: relative;
height: 350vh; /* 101vh ์ด์์ ์ํ๋ ์คํฌ๋กค ๊ฐ ์ฃผ๊ธฐ */
}
.sticky {
position: sticky;
top: 0;
height: 100vh;
overflow: hidden;
}
.item {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%; /* ์คํฌ๋กค ์ height 0์ผ๋ก ์ค์ด๋ฆ */
mask-image: radial-gradient(#fff, #000); /* ํ์ ์์ฑ ! ์์ผ๋ฉด ์คํฌ๋กค ํด๋ ๋ค์ ์์ญ์ด ๋ณด์ด์ง ์์ */
}
/* absolute๋ก item ๋ค์ด ๋ชจ๋ ๊ฒน์ณ์ง ์ํ. ๋ฐ๋ผ์ z-index๋ฅผ ์ฃผ๊ธฐ */
.item:nth-child(1) {
z-index: 4;
}
.item:nth-child(2) {
z-index: 3;
}
.item:nth-child(3) {
z-index: 2;
}
.item:nth-child(4) {
z-index: 1;
}
.item:nth-child(5) {
z-index: 0;
}
.content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh; /* ๋ถ๋ชจ๋ 100% -> 0%๋ก ์ค์ด๋ค์ง๋ง ์์์ ์คํฌ๋กค ํด๋ ์์ญ์ด ์ค์ด๋ค์ง ์์์ผ ํ๊ธฐ์ 100vh ๊ฐ ์ฃผ๊ธฐ */
display: flex;
justify-content: center;
align-items: center;
font-size: 5vw;
font-weight: 700;
color: #fff;
}
JS
const tl = gsap.timeline({
scrollTrigger: {
trigger: '.container',
start: '0% 0%',
end: '100%, 100%',
scrub: 0,
snap: {
snapTo: 'labels', // ํ์๋ผ์ธ์์ ๊ฐ์ฅ ๊ฐ๊น์ด ๋ผ๋ฒจ์ ์ค๋
duration: {min: 0.2, max: 1}, // ์ต์ 0.2 ์ต๋ 1์ด ๋์
delay: 0.1, // ์ค๋
์ ํ๊ธฐ ์ 0.1์ด ๋์ ์ง์ฐ
// ease: 'power1.inOut' // ์ค๋
์ ๋๋ฉ์ด์
์ ease (๊ธฐ๋ณธ์ ์ผ๋ก 'power3')
}
}
});
const itemEls = document.querySelectorAll('.item');
itemEls.forEach(function(itemEl, index) {
const lastIndex = itemEls.length - 1;
if(index !== lastIndex) {
tl.to(itemEl, {height: 0}, `a${index}`)
}
});
์๋ ๋ฐ๋ชจ์ ๊ฐ์ด ๊ธ์๊ฐ ํ์ชฝ์์๋ ๋ํ๋๊ณ ๋ฐ๋ํธ์์ ์ฌ๋ผ์ง๋ ํจ๊ณผ๋ ์ถ๊ฐํด๋ณผ ์ ์๋ค.
ํ
์คํธ ์์ญ ๊ตฌ์กฐ ์ถ๊ฐ๋ก ๊ธฐ์กด ํด๋์ค๋ช
๋ ์์ ํ๋ค(item -> slide)
HTML
<div class="container">
<div class="sticky">
<ul class="brand-list left">
<li class="brand-item">Adidas</li>
<li class="brand-item">Calvin Klein</li>
<li class="brand-item">Hourglass</li>
<li class="brand-item">Eilish</li>
<li class="brand-item">The Outset</li>
<li class="brand-item">MAC</li>
</ul>
<ul class="brand-list right" aria-hidden="true">
<li class="brand-item">Calvin Klein</li>
<li class="brand-item">Hourglass</li>
<li class="brand-item">Eilish</li>
<li class="brand-item">The Outset</li>
<li class="brand-item">MAC</li>
</ul>
<div class="slide-area">
<div class="slide">
<div class="content"></div>
</div>
<!-- ...์ดํ ์๋ต(slide ์ด ๊ฐ์: 5) -->
</div>
</div>
</div>
CSS
** transform-origin ์ด๋?
transform์ ๊ธฐ์ค์ ์ ์ค์ ํ ์ ์๋ ์์ฑ์ผ๋ก ์ด๊ธฐ ๊ฐ์ 50% 50% 0(x์ถ y์ถ z์ถ) ์ด๋ค.
.brand-list {
z-index: 5; /* slide z-index ๋ณด๋ค ๋๊ฒ ์ค์ . ๊ทธ๋์ผ ํ
์คํธ๊ฐ ๋ณด์ฌ์ง */
position: absolute; /* ํ
์คํธ ์์ญ absolute ๋ฐฐ์น */
display: flex;
flex-direction: column;
}
.brand-list.left {
top: 0;
left: 0.4rem;
}
.brand-list.right {
top: calc(50dvh + 1.04167vw);
right: 0.72917vw;
align-items: flex-end;
}
.brand-item {
font-family: "Inter", sans-serif;
font-size: 10vw;
font-weight: 700;
letter-spacing: -0.07em;
color: #fff;
text-transform: uppercase;
white-space: nowrap;
}
.brand-list.left .brand-item {
padding-top: 1rem;
line-height: .73;
transform-origin: left top; /* transform ๊ธฐ์ค์ left top์ผ๋ก ์ค์ */
}
.brand-list.left .brand-item:not(:first-child) {
/* ์ผ์ชฝ์ ์ฒซ ๋ฒ์งธ ํ
์คํธ ์ ์ธํ๊ณ ์๋ณด์ด๋ ์ํ์์ ์คํฌ๋กค ํ ๋๋ง๋ค ํ๋ ์ฉ ๋ณด์ฌ์ง ์์ ์ผ๋ก ๊ธฐ๋ณธ ์ธํ
์ผ๋ก scale 0 ์ ์ฉ*/
transform: scale(0);
}
.brand-list.right .brand-item {
line-height: .79;
transform-origin: right top; /* transform ๊ธฐ์ค์ right top์ผ๋ก ์ค์ */
}
JS
์ผ์ชฝ ํ ์คํธ์ ์ค๋ฅธ์ชฝ ํ ์คํธ์ ๋ํด ๊ฐ๊ฐ ๋ฐ๋ณต๋ฌธ์ ๋๋ฆฐ๋ค.
nth-child์ i ๊ฐ์ ๋ฃ์ ์์ ์ผ๋ก i๋ 1๋ถํฐ ์์ํ๋๋ก ํ๋ค.
์ผ์ชฝ ํ ์คํธ ๊ฐ์: 6 / ์ค๋ฅธ์ชฝ ํ ์คํธ ๊ฐ์: 5๋ก ์ผ์ชฝ์ด 1๊ฐ ๋ ๋ง๋ค. ์ผ์ชฝ ๋งจ ๋ง์ง๋ง ํ ์คํธ๋ ์ ๋๋ฉ์ด์ ํจ๊ณผ๊ฐ ์์ด์ผ ํ๋ฏ๋ก ์กฐ๊ฑด๋ฌธ์ ์ผ์ชฝ์ length ๊ธธ์ด ๋ณด๋ค ๋ฏธ๋ง(<) ์ด๋๋ก ์ค์ ํ๊ณ , ์ค๋ฅธ์ชฝ์ ์ดํ(<=) ์ด๋๋ก ์ค์ ํ๋ค.
์ผ์ชฝ ๋ฐ๋ณต๋ฌธ - ์ฌ๋ผ์ก๋ค๊ฐ ๋ํ๋จ
1) ์คํฌ๋กค ์ i
๋ฒ์งธ ํ
์คํธ๋ yPercent ๊ฐ ๋งํผ ์์ชฝ์ผ๋ก ์ด๋ํ๋ค.
2) i + 1
๋ฒ์งธ ํ
์คํธ๊ฐ scale์ด 1์ด ๋๋ฉด์ ๋ํ๋๋ค.
3) i
๋ฒ ์งธ ๋ค์ ์๋ ๋ชจ๋ ํ
์คํธ๋ค์ ๋ค์ ์คํฌ๋กค ์ค๋น๋ฅผ ์ํด yPercent ๊ฐ ๋งํผ ์์ชฝ์ผ๋ก ์ด๋ํ๋ค. ๋๋จธ์ง ํ
์คํธ๋ค์ scale์ด 0์ธ ์ํ๋ผ ์์ชฝ์ผ๋ก ์ด๋ํด๋ ๊ทธ ๋ชจ์ต์ด ํ๋ฉด์ ๋ณด์ฌ์ง์ง ์๋๋ค.
์ค๋ฅธ์ชฝ ๋ฐ๋ณต๋ฌธ - ๋ํ๋ฌ๋ค๊ฐ ์ฌ๋ผ์ง
1) i
๋ฒ์งธ ํ
์คํธ๋ scale์ด 0์ด ๋๋ฉด์ ์ฌ๋ผ์ง๋ค.
2) i
๋ฒ ์งธ ๋ค์ ์๋ ๋ชจ๋ ํ
์คํธ๋ค์ yPercent ๊ฐ ๋งํผ ์์ชฝ์ผ๋ก ์ด๋ํ๋ค.
slide ์ ๋๋ฉ์ด์
(์คํฌ๋กค์ height 0 ๋๋)๊ณผ ๋์ ์คํ๋ ์ ์๋๋ก label ์ฐ๊ฒฐ a0, a1...
a${i - 1}
์ธ ์ด์ ๋ ํ
์คํธ ๋ฐ๋ณต๋ฌธ์์ i๊ฐ 1๋ถํฐ ์์ํ๊ธฐ ๋๋ฌธ! ์ฌ๋ผ์ด๋ ๋ฐ๋ณต๋ฌธ์์๋ index๊ฐ 0๋ถํฐ ์์์ด๋ค.
for(let i = 1; i < $('.brand-list.left .brand-item').length; i++) {
tl.to(`.brand-list.left .brand-item:nth-child(${i})`, {
yPercent: `-${i}00`,
}, `a${i - 1}`)
.to(`.brand-list.left .brand-item:nth-child(${i + 1})`, {
scale: 1
}, `a${i - 1}`)
.to(`.brand-list.left .brand-item:nth-child(n + ${i + 1})`, {
yPercent:`-${i}00`,
}, `a${i - 1}`)
}
for(let i = 1; i <= $('.brand-list.right .brand-item').length; i++) {
tl.to(`.brand-list.right .brand-item:nth-child(${i})`, {
scale: 0,
}, `a${i - 1}`)
.to(`.brand-list.right .brand-item:nth-child(n + ${i + 1})`, {
yPercent:`-${i}00`,
}, `a${i - 1}`)
}
Bennett & Clive ์ฌ์ดํธ๋ ํธํฐ ์์ญ์ ์ฌ๋ฌด์ค ์์น์ ํ์ง ์๊ฐ์ ๋ํ๋ด๋ UI๊ฐ ์๋ค.
์ด ๋ถ๋ถ์ ์ด๋ป๊ฒ ๊ตฌํํ๋์ง ์ ๋ฆฌ๋ณด๊ณ ์ ํ๋ค.
ํ์ฌ ์๊ฐ ํ์ธํ๊ธฐ
new Date()
๋ฅผ ์ฌ์ฉํด์ ๊ฐ์ ธ์จ ํ์ฌ ๋ ์ง์ ์๊ฐ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉ์์ PC์ ์ค์ ๋ ํ์ค ์๊ฐ๋๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์๋๋ค. ๋ง์ฝ ์ฌ์ฉ์ PC์ ์ค์ ๋ ์๊ฐ์ด KST๋ผ๋ฉด 'new Date()'๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ ํ์ฌ ํ๊ตญ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก ๋ ์ง์ ์๊ฐ์ด ํํ๋๋ค. ํ์ง๋ง, ์ฌ์ฉ์ PC์ ํ์ค์๊ฐ๋๊ฐ ํ๊ตญ ํ์ค์๊ฐ ์๋ ๋ฏธ๊ตญ์ด๋ ์บ๋๋ค์์ ์ฌ์ฉํ๋ ๋๋ถ ํ์ค์๋ ์ค๋ถ ํ์ค์๋ก ์ค์ ์ด ๋์ด ์๋ค๋ฉด, ํด๋น ํ์ค์๊ฐ๋์ ํด๋นํ๋ ๋ ์ง์ ์๊ฐ์ด ๋ณด์ฌ์ง ๊ฒ์ด๋ค.
console.log(new Date()); // Tue Apr 09 2024 16:29:22 GMT+0900 (ํ๊ตญ ํ์ค์)
PC์ ํ์ค์๊ฐ๋ ์ค์ ๊ณผ ๊ด๊ณ ์์ด ํ๊ตญ ์๊ฐํ ์คํธ ํ์ํ๊ธฐ
// 1. ํ์ฌ ์๊ฐ(Locale) ๊ฐ์ ธ์ค๊ธฐ
const current = new Date();
// 2. UTC ์๊ฐ ๊ณ์ฐ
const utc = current.getTime() + (current.getTimezoneOffset() * 60 * 1000);
// 3. UTC to KST (UTC + 9์๊ฐ)
const krTimeDiff = 9 * 60 * 60 * 1000;
const krCurrent = new Date(utc + (krTimeDiff));
console.log(krCurrent); // Tue Apr 09 2024 16:47:50 GMT+0900 (ํ๊ตญ ํ์ค์)
๋จผ์ ํ์ฌ ์๊ฐ์ ๊ฐ์ ธ์จ๋ค. new Date()๋ ์ฌ์ฉ์ PC์ ์ค์ ๋ ์๊ฐ๋ ๊ธฐ์ค์ผ๋ก ์๊ฐ์ ํ์ํด ์ค๋ค.
const current = new Date();
getTime()
1970๋ 1์ 1์ผ 00:00:00(UTC)์ ๊ธฐ์ ์ผ๋ก ํ์ฌ ์๊ฐ๊น์ง ๊ฒฝ๊ณผ๋ ๋ฐ๋ฆฌ์ด๋ฅผ ๋ฐํ
const current = new Date();
const time = current.getTime();
console.log(current); // Tue Apr 09 2024 17:42:51 GMT+0900 (ํ๊ตญ ํ์ค์)
console.log(time); // 1712652171137
getTimezoneOffset()
ํ์ฌ ์ฌ์ฉ์ PC ์ค์ ์๊ฐ๋๋ก๋ถํฐ UTC ์๊ฐ๊น์ง์ ์ฐจ์ด๋ฅผ ๋ถ ๋จ์๋ก ๋ฆฌํด
์๊ฐ๋ ์คํ์ ์ UTC์ ํ์ง ์๊ฐ์ ์ฐจ์ด (๋ถ)์ ๋๋ค. ์ด๊ฒ์ ๋ก์ปฌ ์๊ฐ๋๊ฐ UTC๋ณด๋ค ๋ค๋จ์ด์ ธ ์์ผ๋ฉด ์คํ์ ์ด ์์์ด๊ณ ์์์์ ๊ฒฝ์ฐ ์์์์ ์๋ฏธํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์๊ฐ๋ UTC + 10 : 00 (์ค์คํธ๋ ์ผ๋ฆฌ์ ๋๋ถ ํ์ค์, ๋ธ๋ผ๋๋ณด์คํ ํฌ ์๊ฐ, ์ฐจ๋ชจ๋ก ํ์ค์)์ ๊ฒฝ์ฐ -600์ด ๋ฐํ๋ฉ๋๋ค.
- MDN
// ํธ์คํธ ์ฅ์น์ ํ์ฌ ์๊ฐ๋ ์คํ์
๊ฐ์ ธ ์ค๊ธฐ
const current = new Date();
const offset = current.getTimezoneOffset() / 60; // ๋๋๊ธฐ 60: ๋ถ -> ์๊ฐ ๋จ์๋ก ๋ณ๊ฒฝ
console.log(offset); // -9
KST(Korea Standard Time)๋ UTC์ 9์๊ฐ์ ๋ํ ์๊ฐ์ด๋ค. ์ฆ, UTC = KST - 9h ์ด๋ค.
UTC ์๊ฐ์ ๊ณ์ฐํ๊ธฐ ์ํด getTime()์ getTimezoneOffset() ํจ์์์ ๋์จ ๋ถ ๋จ์์ ์๊ฐ์ ๋ฐ๋ฆฌ์ด ๋จ์๋ก ๋ณํํ์ฌ ๋ํด์ฃผ์๋ค.
์ด์ UTC๋ ํ์ฌ ์๊ฐ์ UTC ์๊ฐ์ผ๋ก ๋ณํํ ๋ฐ๋ฆฌ์ด ๊ฐ์ด๋ค.
const utc = current.getTime() + (current.getTimezoneOffset() * 60 * 1000);
๐ฅ ๋จ์ ๊ณ์ฐ ๋ฐฉ๋ฒ์ด ํท๊ฐ๋ ค์.
1์ด = 1,000 ๋ฐ๋ฆฌ์ด
1๋ถ = 1000 * 60 = 60,000 ๋ฐ๋ฆฌ์ด
1์๊ฐ = 1000 * 60 * 60 = 3,600,000 ๋ฐ๋ฆฌ์ด
ํ๊ตญ ์๊ฐ(KST)์ UTC ์๊ฐ๋ณด๋ค 9์๊ฐ ๋ ๋น ๋ฅด๋ค. 9์๊ฐ์ ๋ฐ๋ฆฌ์ด ๋จ์๋ก ๋ณํํ์๋ค.
const krTimeDiff = 9 * 60 * 60 * 1000;
UTC ์๊ฐ์ ํ๊ตญ ์๊ฐ์ผ๋ก ๋ณํํ๊ธฐ ์ํด UTC ๋ฐ๋ฆฌ์ด ๊ฐ์ 9์๊ฐ(๋ฐ๋ฆฌ์ด ๋จ์๋ก ๋ณํํ ๊ฐ)์ ๋ํด ์ฃผ์๋ค. ๊ทธ๋ฆฌ๊ณ , new Date()
๋ฅผ ์ฌ์ฉํ์ฌ, ๋ฐ๋ฆฌ์ด ๊ฐ์ date๋ก ๋ณํํ์๋ค.
const krCurrent = new Date(utc + (krTimeDiff));
์ด๋ ๊ฒ ํ๋ฉด,ย ์ฌ์ฉ์ ํ๊ฒฝ์ ์๊ฐ๋๊ฐ ์ด๋ป๊ฒ ์ค์ ์ด ๋์ด ์๋ ย ์ฌ์ฉ์์๊ฒ ํ๊ตญ ์๊ฐ์ ๋ณด์ฌ์ค ์ ์๋ค. krTimeDiff ๊ฐ์ ๋ณ๊ฒฝํด์ค๋ค๋ฉด ํ๊ตญ๋ฟ๋ง ์๋๋ผ ๋ค๋ฅธ ๋๋ผ์ ์๊ฐ๋ ๋ณด์ฌ์ค ์ ์๋ค.
๋๋ ์ด ๋ฐฉ์์ ์์ฉํ์ฌ ๋ค๋ฅธ ์ง์ญ์ ์๊ฐ๋ ๊ตฌํ์๋ค.
๐ ์์ฑ์ฝ๋
๋ก๋ฉ ๋ชจ์ ์ด ๋๋๊ณ body ์์์ overflow: hidden ์์ฑ์ ํด์ ํ์๋๋ฐ, ์คํฌ๋กค๋ฐ๊ฐ ์ฌ๋ผ์ก๋ค๊ฐ ์๊ฒจ ์คํฌ๋กค๋ฐ ๋๋น๋งํผ ๋ทฐํฌํธ ๋๋น๊ฐ ๋ฐ๋์ด ํ๋ฉด์ด ์ฝ๊ฐ ๋์ปน ๊ฑฐ๋ฆฌ๋ ๋๋์ด ๋ฌ๋ค.
์๋ ๋ฒํผ์ ํด๋ฆญํด๋ณด์
์ด ๋ถ๋ถ์ ์์ ํ๊ณ ์ถ์ด์ ์ฐพ์ ๋ณด๋ ์ค ์คํฌ๋กค๋ฐ ๊ฑฐํฐ(gutter)์ ์กด์ฌ์ ๋ํ ์ ์ด๋ฅผ ํ๋ CSS ์์ฑ์ ์๊ฒ ๋์๋ค.
CSS ์์ฑ์ ์ฌ์ฉ ํ๋ฉด ์์ฑ์๊ฐ ์คํฌ๋กค ๋ง๋๋ฅผ ์ํ ๊ณต๊ฐ์ ์์ฝํ์ฌ ์ฝํ ์ธ ๊ฐ ์ปค์ง ๋ ์์น ์๋ ๋ ์ด์์ ๋ณ๊ฒฝ์ ๋ฐฉ์งํ๋ ๋์์ ์คํฌ๋กค์ด ํ์ํ์ง ์์ ๋ ๋ถํ์ํ ์๊ฐ์ ์์๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค. - MDN
html {
/* viewport ์คํฌ๋กค๋ฐ์ ์ฌ๋ฐฑ์ ์ํ๋ ๊ฒฝ์ฐ root"(html) ์์์ ํ ๋นํด์ผํ๋ค. */
scrollbar-gutter: stable;
}
์ ์ฝ๋๋ฅผ ์ถ๊ฐํ์๋ค. ๋ค์ ํ๋ฒ ๋ฒํผ์ ํด๋ฆญํด๋ณด์.
stable
์ ์คํฌ๋กค๋ฐ๊ฐ ์์ ๊ฒฝ์ฐ์๋ ์คํฌ๋กค๋ฐ ์์ญ์ ์์ฑํ์ฌ, ์คํฌ๋กค๋ฐ ์ ๋ฌด์ ์๊ด์์ด ๋ด๋ถ ์ฝํ
์ธ ๋๋น๊ฐ ๋์ผํ ๊ฐ์ ๊ฐ์ง ์ ์๋๋ก ํ๋ค.
.box {
scrollbar-gutter: stable;
}
both-edges
๋ stable
๊ณผ ํจ๊ป ์ฐ์ด๋ ์ต์
์ผ๋ก, ์คํฌ๋กค๋ฐ๊ฐ ์์นํ ์ค๋ฅธ์ชฝ๋ฟ๋ง ์๋๋ผ ์ผ์ชฝ์๋ ๋์ผํ ํฌ๊ธฐ์ ๊ณต๊ฐ์ ์์ฑํด ์ค์ผ๋ก์จ, ์ข์ฐ ์ ๋ ฌ์ด ํ์ด์ ธ ๋ณด์ด์ง ์๋๋ก ๋์์ค๋ค.
(์ฆ, ๋ด์ฉ์ด ์ค์์ ์ค๋๋ก ์์ ์์ชฝ์ ๋์นญ ๊ฐ๊ฒฉ์ ์ถ๊ฐ)
.box {
scrollbar-gutter: stable both-edges;
}
์์ฝ๊ฒ๋ scrollbar-gutter
์์ฑ์ Safari ๋ธ๋ผ์ฐ์ ์์๋ ์ง์๋์ง ์๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก scrollbar-gutter
๋ฅผ ์ฌ์ฉํ๊ณ ์ ํ๋ค๋ฉด, ๋ธ๋ผ์ฐ์ ๋ณ ๋์์ ์ถฉ๋ถํ ๊ณ ๋ คํ ํ ์ฌ์ฉํด์ผ ํ๋ค.
์ฐธ๊ณ ์ฌ์ดํธ
https://agal.tistory.com/213
https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter
https://velog.io/@kh8640/DropDown-2
https://wit.nts-corp.com/2024/02/06/6879
https://hianna.tistory.com/451