๐ŸŽจ Tailwind Advanced 2์žฅ: ์Šคํฌ๋กค ๋งˆ์Šคํ„ฐ โ€” Scroll Snap & ๋ถ€๋“œ๋Ÿฌ์šด UX

2026๋…„ 3์›” 5์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

CSS Scroll Snap, overscroll-behavior, scroll-smooth๋ฅผ ์™„์ „ํžˆ ์ดํ•ดํ•˜๊ณ  JavaScript ์—†์ด ์นด๋“œ ์บ๋Ÿฌ์…€๊ณผ ์„น์…˜ ์Šค๋ƒ… ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 22๋ถ„ (์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 10๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ์˜์ˆ™(๋””์ž์ด๋„ˆ): "์˜์ฒ  ๋‹˜, ์Šคํ„ฐ๋”” ์นด๋“œ ๋ชฉ๋ก์„ ์ขŒ์šฐ ์Šคํฌ๋กค๋กœ ๋„˜๊ธฐ๋Š” ์บ๋Ÿฌ์…€๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ์–ด์š”. ๊ทผ๋ฐ ๊ทธ๋ƒฅ ์Šคํฌ๋กค์ด ์•„๋‹ˆ๋ผ, ์นด๋“œ ๋‹จ์œ„๋กœ ๋”ฑ๋”ฑ ๋ฉˆ์ถ”๊ฒŒ ํ•ด์ฃผ์„ธ์š”! ์š”์ฆ˜ ๋ชจ๋ฐ”์ผ ์•ฑ ๋ณด๋ฉด ๋‹ค ๊ทธ๋ ‡๊ฒŒ ๋˜์ž–์•„์š”."
  • ์˜์ฒ (์‹ ์ž…): "์•„! ์บ๋Ÿฌ์…€์ด์š”? ์ œ๊ฐ€ touchstart, touchend ์ด๋ฒคํŠธ ๊ฐ์ง€ํ•ด์„œ ์Šค์™€์ดํ”„ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐํ•˜๊ณ , requestAnimationFrame์œผ๋กœ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ด๋™ํ•˜๋Š” ์ฝ”๋“œ ์งœ๋ฉด ๋˜๊ฒ ๋Š”๋ฐ์š”? 200์ค„ ์ •๋„๋ฉด..."
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "์˜์ฒ  ๋‹˜, snap-x snap-mandatory๋ž‘ snap-center ๋‘ ํด๋ž˜์Šค๋ฉด ๋ฉ๋‹ˆ๋‹ค. JavaScript ํ•œ ์ค„๋„ ํ•„์š” ์—†์–ด์š”."

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • snap-x, snap-y, snap-mandatory, snap-proximity ์˜ ์กฐํ•ฉ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•œ๋‹ค.
  • snap-start, snap-center, snap-end ๋ฅผ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • overscroll-contain ์œผ๋กœ ๋ชจ๋‹ฌ/๋“œ๋กœ์–ด ๋‚ด๋ถ€ ์Šคํฌ๋กค ์ „ํŒŒ๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.
  • scroll-mt-* ๋กœ sticky ํ—ค๋”์— ๊ฐ€๋ ค์ง€๋Š” ์•ต์ปค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
์˜์ˆ™์ด์˜ ์Šค๋ƒ… ์บ๋Ÿฌ์…€ ์š”์ฒญ โ†’ Scroll Snap ๋ถ€๋ชจ-์ž์‹ ๊ตฌ์กฐ โ†’ mandatory vs proximity โ†’ ๋ฐฉํ–ฅ๋ณ„ ์ •๋ ฌ โ†’ smooth scroll โ†’ overscroll ์ œ์–ด โ†’ ์‹ค์ „ ๊ตฌํ˜„


๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€

์บ๋Ÿฌ์…€ ์Šฌ๋ผ์ด๋”. ์›น ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ˆ„๊ตฌ๋‚˜ ํ•œ๋ฒˆ์ฏค ๊ตฌํ˜„ํ•ด๋ดค๊ฑฐ๋‚˜, ๊ตฌํ˜„ ์š”์ฒญ์„ ๋ฐ›์•„๋ณธ ๊ฒฝํ—˜์ด ์žˆ์„ ๊ฑฐ์•ผ.

์ „ํ†ต์ ์ธ ๋ฐฉ์‹์€ ์ด๋žฌ์–ด:

  1. ์ปจํ…Œ์ด๋„ˆ ๋„ˆ๋น„ ์ธก์ •
  2. ํ˜„์žฌ ์Šคํฌ๋กค ์œ„์น˜ ์ถ”์ 
  3. ์Šค์™€์ดํ”„/ํด๋ฆญ ์ด๋ฒคํŠธ ๊ฐ์ง€
  4. ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์นด๋“œ ์œ„์น˜ ๊ณ„์‚ฐ
  5. requestAnimationFrame ์œผ๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹คํ–‰

์ด๊ฒŒ boilerplate๋งŒ 100์ค„์ด ๋„˜์–ด. ๊ทธ๋Ÿฐ๋ฐ ์ˆ˜๋งŽ์€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ์ด ๋ณต์žก์„ฑ์„ ์ˆจ๊ธฐ๋Š” ๋ฐ ์„ฑ๊ณตํ•˜๋ฉด์„œ swiper.js, embla-carousel ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์ƒ๊ฒจ๋‚ฌ์ง€.

๊ทผ๋ฐ ์ž ๊น. CSS ํ‘œ์ค€ ์œ„์›ํšŒ๊ฐ€ 2019๋…„์— ์ด ๋ชจ๋“  ๊ฑธ CSS๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ช…์„ธ๋ฅผ ์ •์‹์œผ๋กœ ํ™•์ •ํ–ˆ์–ด. ์ด๋ฆ„ํ•˜์—ฌ CSS Scroll Snap. ๊ทธ๋ฆฌ๊ณ  ํ˜„์žฌ ๋ชจ๋“  ์ฃผ์š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ์™„๋ฒฝํžˆ ์ง€์›๋ผ.

๋ชจ๋ฅด๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ ํƒํ•˜๊ณ , ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ๊ฑฑ์ •ํ•˜๊ณ , ์—…๋ฐ์ดํŠธ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , ๋ฒ„๊ทธ๋ฅผ ์ง์ ‘ ๊ณ ์ณ์•ผ ํ•ด. ์•Œ๋ฉด ๊ทธ๋ƒฅ ํด๋ž˜์Šค ๋‘ ๊ฐœ๋ฉด ๋์ด์•ผ.


๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿšƒ ๊ธฐ์ฐจ ์Šน๊ฐ•์žฅ์˜ ์•ˆ์ „์„  ์‹œ์Šคํ…œ

CSS Scroll Snap์€ ๊ธฐ์ฐจ์—ญ์˜ ์Šน๊ฐ•์žฅ๊ณผ ๋น„์Šทํ•ด.

  • ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ (๋ถ€๋ชจ): ๊ธฐ์ฐจ ์„ ๋กœ. "์ด ์œ„์—์„œ ๊ธฐ์ฐจ๊ฐ€ ๋‹ค๋‹ˆ๋ฉฐ ์Šค๋ƒ… ๊ทœ์น™์„ ์ •ํ•ด."
  • ์Šค๋ƒ… ์•„์ดํ…œ (์ž์‹๋“ค): ์Šน๊ฐ•์žฅ. "๊ธฐ์ฐจ๊ฐ€ ์—ฌ๊ธฐ์„œ ์ •์ฐจํ•ด์•ผ ํ•ด."
  • snap-mandatory: ๊ฐ•์ œ ์ •์ฐจ. "๊ธฐ์ฐจ๋Š” ๋ฐ˜๋“œ์‹œ ์Šน๊ฐ•์žฅ์—์„œ๋งŒ ๋ฉˆ์ถฐ์•ผ ํ•ด. ์ค‘๊ฐ„์—์„œ ๋ฉˆ์ถ”๋Š” ๊ฒƒ์€ ํ—ˆ์šฉ ์•ˆ ํ•ด."
  • snap-proximity: ์ž„์˜ ์ •์ฐจ. "์Šน๊ฐ•์žฅ ๊ฐ€๊นŒ์ด ์™”์„ ๋•Œ๋งŒ ์ž๋™์œผ๋กœ ๋Œ์–ด๋‹น๊ฒจ. ๋ฉ€๋ฆฌ ์žˆ์œผ๋ฉด ๊ทธ๋ƒฅ ๋‘์–ด."
  • snap-center: ์Šน๊ฐ•์žฅ ์ค‘์•™ ํ‘œ์‹œ. "๊ธฐ์ฐจ ๋ฌธ์ด ์ด ๋งˆํฌ์— ๋”ฑ ๋งž๊ฒŒ ์„œ."

๋ถ€๋ชจ๋Š” "์–ด๋А ๋ฐฉํ–ฅ์œผ๋กœ, ์–ผ๋งˆ๋‚˜ ์—„๊ฒฉํ•˜๊ฒŒ" ์Šค๋ƒ…ํ• ์ง€ ์ •ํ•˜๊ณ ,
์ž์‹์€ "๋‚ด ์–ด๋А ์ง€์ ์— ๊ฑธ๋ฆด์ง€" ์ •ํ•ด. ์ด ๊ณ„์•ฝ์ด Scroll Snap์ด์•ผ.


๐ŸŽฏ 1๋‹จ๊ณ„: Scroll Snap ํ•ต์‹ฌ ๊ตฌ์กฐ โ€” ๋ถ€๋ชจ์™€ ์ž์‹์˜ ๊ณ„์•ฝ

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • Scroll Snap์—์„œ ๋ถ€๋ชจ์™€ ์ž์‹์ด ๊ฐ๊ฐ ๋ฌด์Šจ ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๋Š”์ง€ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ตœ์†Œํ•œ์˜ ํด๋ž˜์Šค๋กœ ์ž‘๋™ํ•˜๋Š” ์Šค๋ƒ… ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

Scroll Snap์˜ ๊ตฌ์กฐ๋Š” ํ•ญ์ƒ 2๊ฐœ์˜ ๋ ˆ์ด์–ด๋กœ ๋‚˜๋‰˜์–ด.

<!-- ๐Ÿฃ ์˜์ฒ : "snap-x๋งŒ ๋„ฃ์—ˆ๋Š”๋ฐ ์™œ ์•ˆ ๊ฑธ๋ ค์š”?" -->
<!-- snap-x ํ˜ผ์ž์„œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ•ด. ์ž์‹์— align์ด ์žˆ์–ด์•ผ ํ•จ! -->
 
<!-- โŒ ์ž์‹์— snap align์ด ์—†์œผ๋ฉด ์Šค๋ƒ… ํฌ์ธํŠธ๊ฐ€ ์—†์–ด์„œ ๋™์ž‘ ์•ˆ ํ•จ -->
<div class="flex overflow-x-auto snap-x snap-mandatory gap-4">
  <div class="w-80 h-48 bg-gray-200 shrink-0">์นด๋“œ 1</div>  <!-- snap ํฌ์ธํŠธ ์—†์Œ! -->
  <div class="w-80 h-48 bg-gray-200 shrink-0">์นด๋“œ 2</div>
</div>
 
<!-- โœ… ๋ถ€๋ชจ: ์ปจํ…Œ์ด๋„ˆ ์„ค์ • / ์ž์‹: ์ •๋ ฌ ํฌ์ธํŠธ ์„ค์ • -->
<div class="
  flex overflow-x-auto  /* ์Šคํฌ๋กค ํ™œ์„ฑํ™” */
  snap-x               /* X์ถ• ๋ฐฉํ–ฅ ์Šค๋ƒ… */
  snap-mandatory       /* ์—„๊ฒฉํ•œ ์Šค๋ƒ… */
  gap-4
">
  <!-- ๊ฐ ์ž์‹์— snap-center ๋˜๋Š” snap-start ์ถ”๊ฐ€ -->
  <div class="w-80 h-48 bg-gray-200 shrink-0 snap-center">์นด๋“œ 1</div>
  <div class="w-80 h-48 bg-gray-200 shrink-0 snap-center">์นด๋“œ 2</div>
  <div class="w-80 h-48 bg-gray-200 shrink-0 snap-center">์นด๋“œ 3</div>
</div>

๋ถ€๋ชจ๊ฐ€ ๋‹ด๋‹นํ•˜๋Š” ๊ฒƒ:

<!-- 1. ์Šค๋ƒ… ๋ฐฉํ–ฅ -->
snap-x      <!-- ๊ฐ€๋กœ ์Šคํฌ๋กค ์Šค๋ƒ… -->
snap-y      <!-- ์„ธ๋กœ ์Šคํฌ๋กค ์Šค๋ƒ… -->
snap-both   <!-- ์–‘๋ฐฉํ–ฅ ์Šค๋ƒ… -->
snap-none   <!-- ์Šค๋ƒ… ํ•ด์ œ -->
 
<!-- 2. ์Šค๋ƒ… ์—„๊ฒฉ๋„ -->
snap-mandatory   <!-- ํ•ญ์ƒ ์Šค๋ƒ… ํฌ์ธํŠธ์—์„œ ๋ฉˆ์ถค -->
snap-proximity   <!-- ๊ฐ€๊นŒ์šธ ๋•Œ๋งŒ ์Šค๋ƒ… ํฌ์ธํŠธ์— ๋Œ๋ฆผ -->

์ž์‹์ด ๋‹ด๋‹นํ•˜๋Š” ๊ฒƒ:

<!-- ์Šค๋ƒ… ์ •๋ ฌ ์œ„์น˜ -->
snap-start   <!-- ์•„์ดํ…œ์˜ ์‹œ์ž‘์ ์ด ์Šค๋ƒ… ํฌ์ธํŠธ -->
snap-center  <!-- ์•„์ดํ…œ์˜ ์ค‘์•™์ด ์Šค๋ƒ… ํฌ์ธํŠธ -->
snap-end     <!-- ์•„์ดํ…œ์˜ ๋์ ์ด ์Šค๋ƒ… ํฌ์ธํŠธ -->
snap-align-none  <!-- ์ด ์•„์ดํ…œ์€ ์Šค๋ƒ… ๋Œ€์ƒ์—์„œ ์ œ์™ธ -->

๐Ÿ”’ 2๋‹จ๊ณ„: snap-mandatory vs snap-proximity โ€” ๊ฐ•์ œ๋ƒ ์ž์œ ๋ƒ

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • snap-mandatory ์™€ snap-proximity ์ค‘ ์–ด๋–ค ์ƒํ™ฉ์— ์–ด๋А ๊ฒƒ์„ ์จ์•ผ ํ•˜๋Š”์ง€ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐ๊ฐ์˜ UX ํŠน์„ฑ๊ณผ ์ฃผ์˜์‚ฌํ•ญ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

snap-mandatory โ€” ์ ˆ๋Œ€ ๋ณต์ข…ํ˜•

์Šคํฌ๋กค์ด ๋ฉˆ์ถ”๋ฉด ๋ฌด์กฐ๊ฑด ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์Šค๋ƒ… ํฌ์ธํŠธ๋กœ ์ด๋™ํ•ด. ์ค‘๊ฐ„ ์–ด์ •์ฉกํ•œ ์œ„์น˜์— ๋ฉˆ์ถ”๋Š” ๊ฒƒ์ด ํ—ˆ์šฉ๋˜์ง€ ์•Š์•„.

<!-- snap-mandatory: ํ•ญ์ƒ ์นด๋“œ ๋‹จ์œ„๋กœ ๋”ฑ๋”ฑ ๋ฉˆ์ถค -->
<div class="flex overflow-x-auto snap-x snap-mandatory gap-6 px-6">
  <div class="snap-center shrink-0 w-72">์Šคํ„ฐ๋”” ์นด๋“œ 1</div>
  <div class="snap-center shrink-0 w-72">์Šคํ„ฐ๋”” ์นด๋“œ 2</div>
  <div class="snap-center shrink-0 w-72">์Šคํ„ฐ๋”” ์นด๋“œ 3</div>
</div>

snap-mandatory ์ฃผ์˜์‚ฌํ•ญ: ์•„์ดํ…œ ํฌ๊ธฐ๊ฐ€ ์ปจํ…Œ์ด๋„ˆ๋ณด๋‹ค ํฌ๋ฉด ์Šคํฌ๋กค์ด ๋ถˆ๊ฐ€๋Šฅํ•ด์งˆ ์ˆ˜ ์žˆ์–ด.

<!-- ๐Ÿšจ ์œ„ํ—˜ํ•œ ํŒจํ„ด: ์•„์ดํ…œ์ด ์ปจํ…Œ์ด๋„ˆ๋ณด๋‹ค ํฌ๋ฉด ๊ฑด๋„ˆ๋œ€ ํ˜„์ƒ ๋ฐœ์ƒ -->
<!-- ์ปจํ…Œ์ด๋„ˆ: 400px, ์•„์ดํ…œ: 600px -->
<div class="w-[400px] overflow-x-auto snap-x snap-mandatory">
  <div class="w-[600px] snap-start">์ด ์•„์ดํ…œ์„ 100% ๋ณผ ์ˆ˜ ์—†์–ด!</div>
  <!-- mandatory๋ฉด ํ•ญ์ƒ ๋‹ค์Œ ์Šค๋ƒ… ํฌ์ธํŠธ๋กœ ์ด๋™ โ†’ ๊ธด ์ฝ˜ํ…์ธ  ์ฝ๊ธฐ ๋ถˆ๊ฐ€ -->
</div>
 
<!-- โœ… ์ด๋Ÿฐ ๊ฒฝ์šฐ์—” snap-proximity๊ฐ€ ๋” ์ ํ•ฉ -->
<div class="w-[400px] overflow-x-auto snap-x snap-proximity">
  <div class="w-[600px] snap-start">์ด ์•„์ดํ…œ์€ ์ฒœ์ฒœํžˆ ์ฝ์„ ์ˆ˜ ์žˆ์–ด</div>
</div>

snap-proximity โ€” ์ ๋‹นํžˆ ํ˜‘์กฐํ˜•

์Šค๋ƒ… ํฌ์ธํŠธ์— ๊ฐ€๊นŒ์ด ์™”์„ ๋•Œ๋งŒ ๋Œ์–ด๋‹น๊ฒจ. ๋ฉ€๋ฆฌ ์žˆ์œผ๋ฉด ๊ทธ๋ƒฅ ์Šคํฌ๋กค ๊ด€์„ฑ๋Œ€๋กœ ๋ฉˆ์ถฐ.

<!-- snap-proximity: ๊ฐ€๊นŒ์šธ ๋•Œ๋งŒ ์Šค๋ƒ…, ๊ฐ•์ œ ์ด๋™ ์—†์Œ -->
<div class="flex overflow-x-auto snap-x snap-proximity gap-6">
  <div class="snap-center shrink-0 w-72">์Šคํ„ฐ๋”” ์นด๋“œ 1</div>
  <div class="snap-center shrink-0 w-72">์Šคํ„ฐ๋”” ์นด๋“œ 2</div>
  <div class="snap-center shrink-0 w-72">์Šคํ„ฐ๋”” ์นด๋“œ 3</div>
</div>

์–ธ์ œ ๋ฌด์—‡์„ ์“ธ๊นŒ:

์ƒํ™ฉ์ถ”์ฒœ
์นด๋“œ ์บ๋Ÿฌ์…€ (์นด๋“œ๊ฐ€ ํ™”๋ฉด๋ณด๋‹ค ์ž‘์Œ)snap-mandatory
ํ’€ํŽ˜์ด์ง€ ์„น์…˜ ์Šคํฌ๋กค (์„น์…˜์ด ์ •ํ™•ํžˆ ํ™”๋ฉด ํฌ๊ธฐ)snap-mandatory
๊ธด ๊ธฐ์‚ฌ/๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ์˜ ์„น์…˜ ์ด๋™snap-proximity
์•„์ดํ…œ ํฌ๊ธฐ๊ฐ€ ์ปจํ…Œ์ด๋„ˆ๋ณด๋‹ค ํฐ ๊ฒฝ์šฐsnap-proximity

๐Ÿ“ 3๋‹จ๊ณ„: snap-start, snap-center, snap-end โ€” ์–ด๋””์— ๊ฑธ๋ฆด๊นŒ

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • snap-start, snap-center, snap-end ์˜ ์‹œ๊ฐ์  ์ฐจ์ด๋ฅผ ์ดํ•ดํ•œ๋‹ค.
  • ์นด๋“œ ์บ๋Ÿฌ์…€ vs ํ’€ํŽ˜์ด์ง€ ์„น์…˜ ๋“ฑ ์šฉ๋„์— ๋งž๋Š” ์ •๋ ฌ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

snap-start โ€” ์•„์ดํ…œ์˜ ์™ผ์ชฝ(์œ„)์ด ๊ธฐ์ค€์ 

<!-- snap-start: ์•„์ดํ…œ์˜ ์‹œ์ž‘(left)์ด ์ปจํ…Œ์ด๋„ˆ์˜ ์‹œ์ž‘(left)์— ๋งž์ถฐ์ง -->
<!--
  [์นด๋“œ1][์นด๋“œ2][์นด๋“œ3]
   ^
   ์ปจํ…Œ์ด๋„ˆ ์™ผ์ชฝ๊ณผ ์นด๋“œ ์™ผ์ชฝ์ด ๋งž์ถฐ์ง
-->
<div class="flex overflow-x-auto snap-x snap-mandatory">
  <div class="snap-start shrink-0 w-72 mr-4">์นด๋“œ 1</div>
  <div class="snap-start shrink-0 w-72 mr-4">์นด๋“œ 2</div>
  <div class="snap-start shrink-0 w-72 mr-4">์นด๋“œ 3</div>
</div>

snap-center โ€” ์•„์ดํ…œ์˜ ์ค‘์•™์ด ๊ธฐ์ค€์ 

<!-- snap-center: ์•„์ดํ…œ์˜ ์ค‘์•™์ด ์ปจํ…Œ์ด๋„ˆ์˜ ์ค‘์•™์— ๋งž์ถฐ์ง -->
<!--
  [  ์นด๋“œ1  ][  ์นด๋“œ2  ][  ์นด๋“œ3  ]
        ^
        ์ปจํ…Œ์ด๋„ˆ ์ค‘์•™๊ณผ ์นด๋“œ ์ค‘์•™์ด ๋งž์ถฐ์ง
-->
<div class="flex overflow-x-auto snap-x snap-mandatory">
  <!-- ๐Ÿ“Œ ์–‘ ๋ ์—ฌ๋ฐฑ ์•„์ดํ…œ: ์ค‘์•™ ์ •๋ ฌ ์‹œ ์ฒ˜์Œ/๋งˆ์ง€๋ง‰ ์นด๋“œ๊ฐ€ ์ค‘์•™์— ์˜ค๋„๋ก -->
  <div class="shrink-0 w-[calc(50%-9rem)]"></div>  {/* ์นด๋“œ๊ฐ€ 144px(w-72/2)์ผ ๋•Œ ์—ฌ๋ฐฑ */}
  <div class="snap-center shrink-0 w-72 mr-4">์นด๋“œ 1</div>
  <div class="snap-center shrink-0 w-72 mr-4">์นด๋“œ 2</div>
  <div class="snap-center shrink-0 w-72 mr-4">์นด๋“œ 3</div>
  <div class="shrink-0 w-[calc(50%-9rem)]"></div>
</div>

snap-end โ€” ์•„์ดํ…œ์˜ ์˜ค๋ฅธ์ชฝ(์•„๋ž˜)์ด ๊ธฐ์ค€์ 

<!-- snap-end: ์•„์ดํ…œ์˜ ๋(right)์ด ์ปจํ…Œ์ด๋„ˆ์˜ ๋(right)์— ๋งž์ถฐ์ง -->
<div class="flex overflow-x-auto snap-x snap-mandatory">
  <div class="snap-end shrink-0 w-72 mr-4">์นด๋“œ 1</div>
  <div class="snap-end shrink-0 w-72 mr-4">์นด๋“œ 2</div>
  <div class="snap-end shrink-0 w-72">์นด๋“œ 3</div>  {/* ๋งˆ์ง€๋ง‰์—” mr ์—†์Œ */}
</div>

์‹ค๋ฌด ํŒ โ€” ์–ด๋А align์„ ์“ธ๊นŒ:

์นด๋“œ ์บ๋Ÿฌ์…€ (์ผ๋ฐ˜) โ†’ snap-start (์ž์—ฐ์Šค๋Ÿฌ์šด ์™ผ์ชฝ ์ •๋ ฌ)
์นด๋“œ ์บ๋Ÿฌ์…€ (์ค‘์•™ ๊ฐ•์กฐ) โ†’ snap-center (์–‘๋ ์—ฌ๋ฐฑ ์•„์ดํ…œ ์ถ”๊ฐ€ ํ•„์š”)
ํ’€ํŽ˜์ด์ง€ ์„น์…˜ ์Šค๋ƒ… โ†’ snap-start (๊ฐ ์„น์…˜์˜ ์‹œ์ž‘์ด ํ™”๋ฉด ์ƒ๋‹จ์— ๋งž์ถฐ์ง)
์„ธ๋กœ ์Šฌ๋ผ์ด๋“œ์‡ผ โ†’ snap-center (์ฝ˜ํ…์ธ ๊ฐ€ ์ค‘์•™์— ์˜ค๊ฒŒ)

๐ŸŒŠ 4๋‹จ๊ณ„: scroll-smooth โ€” ๋ถ€๋“œ๋Ÿฌ์šด ์•ต์ปค ์ด๋™

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • scroll-smooth ๋กœ ์•ต์ปค ๋งํฌ ํด๋ฆญ ์‹œ ๋ถ€๋“œ๋Ÿฌ์šด ์Šคํฌ๋กค ์ด๋™์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • prefers-reduced-motion ์ ‘๊ทผ์„ฑ ๊ณ ๋ ค์˜ ์ค‘์š”์„ฑ์„ ์ดํ•ดํ•œ๋‹ค.

scroll-behavior: smooth ๋Š” ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜์ง€๋งŒ UX๋ฅผ ๊ทน์ ์œผ๋กœ ๊ฐœ์„ ํ•˜๋Š” ์†์„ฑ์ด์•ผ. ์•ต์ปค ๋งํฌ(#section)๋ฅผ ํด๋ฆญํ•  ๋•Œ ์ˆœ๊ฐ„ ์ด๋™์ด ์•„๋‹ˆ๋ผ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์Šคํฌ๋กค๋ผ.

<!-- โŒ ์˜์ฒ ์ด์˜ ์ฝ”๋“œ: JavaScript๋กœ smooth scroll ๊ตฌํ˜„ -->
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
  anchor.addEventListener('click', (e) => {
    e.preventDefault();
    const target = document.querySelector(anchor.getAttribute('href'));
    target.scrollIntoView({ behavior: 'smooth' });
  });
});
 
<!-- โœ… ์˜ํ˜ธ์˜ ๋ฐฉ์‹: html ํƒœ๊ทธ์— ํด๋ž˜์Šค ํ•˜๋‚˜ -->
<html class="scroll-smooth">
  <!-- ์ด์ œ ๋ชจ๋“  ์•ต์ปค ๋งํฌ๊ฐ€ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ด๋™ -->
</html>
<!-- ์‹ค์ „: ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฉ”๋‰ด์—์„œ ์„น์…˜์œผ๋กœ ์ด๋™ -->
<html class="scroll-smooth">
  <head>...</head>
  <body>
    <!-- ๋„ค๋น„๊ฒŒ์ด์…˜ -->
    <nav class="fixed top-0 ...">
      <a href="#studies">์Šคํ„ฐ๋”” ์ฐพ๊ธฐ</a>    <!-- ํด๋ฆญ ์‹œ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์Šคํฌ๋กค -->
      <a href="#how-it-works">์ด์šฉ ๋ฐฉ๋ฒ•</a>
      <a href="#testimonials">ํ›„๊ธฐ</a>
    </nav>
 
    <!-- ์„น์…˜๋“ค -->
    <section id="studies">...</section>
    <section id="how-it-works">...</section>
    <section id="testimonials">...</section>
  </body>
</html>

์ ‘๊ทผ์„ฑ ์ฃผ์˜์‚ฌํ•ญ:

/* ๋™์ž‘ ๊ฐ์†Œ ์„ค์ •์„ ํ•œ ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” ์ฆ‰์‹œ ์ด๋™์ด ๋‚˜์„ ์ˆ˜ ์žˆ์–ด */
/* Tailwind๋กœ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ๋ฐ˜์‘ํ˜• ๋ณ€ํ˜• ํ™œ์šฉ */
<!-- motion-reduce: ์ ‘๊ทผ์„ฑ ๋ฐฐ๋ ค -->
<html class="scroll-smooth motion-reduce:scroll-auto">
  <!-- ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ฐ์†Œ ์„ ํ˜ธ ์‚ฌ์šฉ์ž์—๊ฒ ์ฆ‰์‹œ ์ด๋™ -->
</html>

๐Ÿ›‘ 5๋‹จ๊ณ„: overscroll-behavior โ€” ์Šคํฌ๋กค ์ „ํŒŒ ๋ง‰๊ธฐ

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • "์Šคํฌ๋กค ์ฒด์ธ(scroll chaining)"์ด ๋ฌด์—‡์ธ์ง€ ์ดํ•ดํ•˜๊ณ , ์ด๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ชจ๋‹ฌ, ๋“œ๋กœ์–ด, ์‚ฌ์ด๋“œํŒจ๋„์˜ ์Šคํฌ๋กค ๋…๋ฆฝ์„ฑ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํฌ๋กค ์ฒด์ธ์ด๋ž€?

์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์š”์†Œ(์˜ˆ: ๋ชจ๋‹ฌ)๋ฅผ ๋๊นŒ์ง€ ์Šคํฌ๋กคํ•˜๋ฉด, ๊ทธ ์Šคํฌ๋กค ์ด๋ฒคํŠธ๊ฐ€ ๋ถ€๋ชจ(body)์—๊ฒŒ๋„ ์ „ํŒŒ๋˜๋Š” ํ˜„์ƒ์ด์•ผ.

์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋‹ฌ ๋‚ด๋ถ€๋ฅผ ์Šคํฌ๋กค โ†’
๋ชจ๋‹ฌ ์ฝ˜ํ…์ธ ๋ฅผ ๋‹ค ์˜ฌ๋ฆผ โ†’
์Šคํฌ๋กค์ด ๋‚จ์•„ ์žˆ์œผ๋ฉด body๊ฐ€ ์Šคํฌ๋กค๋จ โ† ์ด๊ฒŒ ์Šคํฌ๋กค ์ฒด์ธ ๋ฒ„๊ทธ!
<!-- โŒ ์˜์ฒ ์ด์˜ JavaScript ํ•ด๊ฒฐ์ฑ…: ์ด๋ฒคํŠธ ๋ง‰๊ธฐ -->
modal.addEventListener('wheel', (e) => {
  const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
  if (
    (e.deltaY < 0 && scrollTop === 0) ||
    (e.deltaY > 0 && scrollTop + clientHeight === scrollHeight)
  ) {
    e.preventDefault();
  }
}, { passive: false });
// ๐Ÿ’ฃ passive event ๋ฌธ์ œ, iOS Safari ๋ฒ„๊ทธ, ์„ฑ๋Šฅ ์ด์Šˆ... ๊ณจ์น˜ ์•„ํ””
 
<!-- โœ… ์˜ํ˜ธ์˜ CSS ํ•œ ๋ฐฉ ํ•ด๊ฒฐ -->
<div class="overflow-y-auto overscroll-contain max-h-screen">
  <!-- ๋ชจ๋‹ฌ ๋‚ด๋ถ€ ์ฝ˜ํ…์ธ  -->
  <!-- overscroll-contain: ์ด ์š”์†Œ์˜ ์Šคํฌ๋กค์ด ๋ถ€๋ชจ๋กœ ์ „ํŒŒ๋˜์ง€ ์•Š์Œ -->
</div>

overscroll 3๊ฐ€์ง€ ๋ชจ๋“œ

<!-- overscroll-auto (๊ธฐ๋ณธ๊ฐ’): ์Šคํฌ๋กค ์ฒด์ธ ํ—ˆ์šฉ -->
<div class="overflow-y-auto overscroll-auto">
  <!-- ๋์—์„œ ์Šคํฌ๋กคํ•˜๋ฉด ๋ถ€๋ชจ ์Šคํฌ๋กค ๋ฐœ์ƒ (๊ธฐ๋ณธ ๋ธŒ๋ผ์šฐ์ € ๋™์ž‘) -->
</div>
 
<!-- overscroll-contain: ์Šคํฌ๋กค ์ฒด์ธ ์ฐจ๋‹จ + ๋ฐ”์šด์Šค ํ—ˆ์šฉ -->
<div class="overflow-y-auto overscroll-contain">
  <!-- ๋์—์„œ ์Šคํฌ๋กค์ด ๋ถ€๋ชจ๋กœ ์ „ํŒŒ ์•ˆ ๋จ -->
  <!-- iOS Safari์˜ ๊ณ ๋ฌด์ค„ ๋ฐ”์šด์Šค ํšจ๊ณผ๋Š” ์œ ์ง€๋จ -->
</div>
 
<!-- overscroll-none: ์Šคํฌ๋กค ์ฒด์ธ + ๋ฐ”์šด์Šค ๋ชจ๋‘ ์ฐจ๋‹จ -->
<div class="overflow-y-auto overscroll-none">
  <!-- ๋์—์„œ ์Šคํฌ๋กค์ด ๋ถ€๋ชจ๋กœ๋„ ์ „ํŒŒ ์•ˆ ๋˜๊ณ  ๋ฐ”์šด์Šค๋„ ์—†์Œ -->
  <!-- ์•ฑ์ฒ˜๋Ÿผ ๋”ฑ๋”ฑํ•œ ๋А๋‚Œ -->
</div>

์–ธ์ œ ๋ฌด์—‡์„ ์“ธ๊นŒ:

์ƒํ™ฉ์ถ”์ฒœ
๋ชจ๋‹ฌ/๋“œ๋กœ์–ด ๋‚ด๋ถ€ ์Šคํฌ๋กคoverscroll-contain
์ฑ„ํŒ…์ฐฝ ๋ฉ”์‹œ์ง€ ์˜์—ญoverscroll-contain
์ž„๋ฒ ๋“œ๋œ ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆoverscroll-contain
๋ชจ๋ฐ”์ผ ์•ฑ์ฒ˜๋Ÿผ ๋”ฑ๋”ฑํ•œ UXoverscroll-none
ํ’€ํŽ˜์ด์ง€ ์Šคํฌ๋กค ์„น์…˜overscroll-none

์‹ค์ „: ๋ชจ๋‹ฌ ์Šคํฌ๋กค ๋…๋ฆฝ์„ฑ

// components/StudyDetailModal.tsx
// ๐Ÿฆ ์˜ํ˜ธ: "๋ชจ๋‹ฌ ์•ˆ์—์„œ ์Šคํฌ๋กคํ•  ๋•Œ ๋ฐฐ๊ฒฝ์ด ๊ฐ™์ด ์›€์ง์ด๋Š” ๋ฒ„๊ทธ ์žˆ์ง€? overscroll-contain ํ•˜๋‚˜๋ฉด ๋์ด์•ผ"
 
export default function StudyDetailModal({ isOpen, onClose, study }) {
  if (!isOpen) return null;
 
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center">
      {/* ๋ฐฐ๊ฒฝ ์˜ค๋ฒ„๋ ˆ์ด */}
      <div className="absolute inset-0 bg-black/50" onClick={onClose} />
 
      {/* ๋ชจ๋‹ฌ ํŒจ๋„ */}
      <div className="
        relative z-10
        bg-white rounded-2xl
        w-full max-w-lg
        max-h-[85vh]         /* ์ตœ๋Œ€ ๋†’์ด ์ œํ•œ */
        overflow-y-auto      /* ๋‚ด๋ถ€ ์Šคํฌ๋กค ํ™œ์„ฑํ™” */
        overscroll-contain   /* ์Šคํฌ๋กค ์ „ํŒŒ ์ฐจ๋‹จ */
        shadow-2xl
      ">
        {/* ๋ชจ๋‹ฌ ํ—ค๋” (sticky) */}
        <div className="sticky top-0 bg-white border-b px-6 py-4 flex justify-between items-center">
          <h2 className="text-lg font-semibold">{study.title}</h2>
          <button onClick={onClose} className="text-gray-400 hover:text-gray-600">โœ•</button>
        </div>
 
        {/* ๋ชจ๋‹ฌ ์ฝ˜ํ…์ธ  - ์•„๋ฌด๋ฆฌ ์Šคํฌ๋กคํ•ด๋„ ๋ฐฐ๊ฒฝ body๋Š” ์•ˆ ์›€์ง์ž„ */}
        <div className="px-6 py-4 space-y-4">
          <p className="text-gray-700 leading-relaxed">{study.description}</p>
          {/* ... ๋” ๋งŽ์€ ์ฝ˜ํ…์ธ  ... */}
        </div>
      </div>
    </div>
  );
}

๐Ÿ“ 6๋‹จ๊ณ„: scroll-margin & scroll-padding โ€” sticky ํ—ค๋” ์˜คํ”„์…‹ ํ•ด๊ฒฐ

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • sticky ํ—ค๋”๊ฐ€ ์žˆ์„ ๋•Œ ์•ต์ปค ์ด๋™ ํ›„ ์ฝ˜ํ…์ธ ๊ฐ€ ํ—ค๋”์— ๊ฐ€๋ ค์ง€๋Š” ๋ฒ„๊ทธ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • scroll-mt-* ์™€ scroll-pt-* ์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•œ๋‹ค.

๋ฌธ์ œ ์ƒํ™ฉ: ํ—ค๋” ์•„๋ž˜๋กœ ์ˆจ๋Š” ์ฝ˜ํ…์ธ 

<!-- ๐Ÿฃ ์˜์ฒ : "nav์— fixed ์ฃผ๊ณ  ์•ต์ปค ์ด๋™ํ•˜๋ฉด ์„น์…˜ ์ œ๋ชฉ์ด nav ๋’ค์— ์ˆจ์–ด์š”!" -->
<!--
  ํ™”๋ฉด:
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚   Sticky Nav    โ”‚ โ† height: 64px (h-16)
  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
  โ”‚ (์ด ๋ถ€๋ถ„์ด ๊ฐ€๋ ค์ง) โ† ์„น์…˜ ์ œ๋ชฉ์ด ์—ฌ๊ธฐ ์žˆ์–ด์„œ ์•ˆ ๋ณด์ž„!
  โ”‚                 โ”‚
  โ”‚  ์„น์…˜ ๋‚ด์šฉ ...  โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
-->

ํ•ด๊ฒฐ์ฑ…: scroll-margin-top

<!-- โŒ ์˜์ฒ ์ด์˜ ๋ฐฉ๋ฒ•: ์„น์…˜์— padding-top ์ฃผ๊ธฐ -->
<section id="studies" class="pt-16">  <!-- ํ•˜์ง€๋งŒ ์„น์…˜ ๋‚ด๋ถ€ ๋ ˆ์ด์•„์›ƒ์ด ๋ง๊ฐ€์ง -->
 
<!-- โœ… ์˜ํ˜ธ์˜ ๋ฐฉ๋ฒ•: scroll-mt-16 -->
<section id="studies" class="scroll-mt-16">
  <!-- scroll-mt-16 = scroll-margin-top: 4rem -->
  <!-- ์•ต์ปค๋กœ ์ด๋™ ์‹œ 64px ์•„๋ž˜์—์„œ ๋ฉˆ์ถค โ†’ ํ—ค๋” ๋ฐ‘์— ์•ˆ ๊ฐ€๋ ค์ง -->
  <h2>์Šคํ„ฐ๋”” ๋ชฉ๋ก</h2>
</section>

scroll-padding vs scroll-margin

<!-- scroll-padding: ์ปจํ…Œ์ด๋„ˆ(๋ถ€๋ชจ)์— ์ ์šฉ -->
<!-- ์ปจํ…Œ์ด๋„ˆ ์•ˆ์˜ ์Šค๋ƒ…/์•ต์ปค ์ด๋™ ์‹œ ์‹œ์ž‘ ์˜คํ”„์…‹ -->
<div class="snap-y snap-mandatory scroll-pt-16 overflow-y-auto h-screen">
  <!-- ๋ชจ๋“  ์ž์‹์˜ ์Šค๋ƒ… ํฌ์ธํŠธ์— 16(4rem) ์˜คํ”„์…‹ ์ ์šฉ -->
  <section class="snap-start h-screen">์„น์…˜ 1</section>
  <section class="snap-start h-screen">์„น์…˜ 2</section>
</div>
 
<!-- scroll-margin: ์•„์ดํ…œ(์ž์‹)์— ์ ์šฉ -->
<!-- ์ด ์•„์ดํ…œ์ด ์Šค๋ƒ…/์•ต์ปค ์ด๋™ ๋Œ€์ƒ์ด ๋  ๋•Œ ์—ฌ๋ฐฑ -->
<section id="studies" class="snap-start scroll-mt-16">
  <!-- ์ด ์„น์…˜์œผ๋กœ ์ด๋™ ์‹œ 16(4rem) ์œ„์—์„œ ๋ฉˆ์ถค -->
</section>

์‹ค์ „ ์ „๋žต - sticky ํ—ค๋” ์˜คํ”„์…‹:

// tailwind.config.ts - ํ—ค๋” ๋†’์ด๋ฅผ CSS ๋ณ€์ˆ˜๋กœ ๊ด€๋ฆฌ
// ๋ชจ๋“  ์„น์…˜์— ๋™์ผํ•œ scroll-mt ์ ์šฉ์„ ์œ„ํ•ด
 
// JSX์—์„œ
const HEADER_HEIGHT = "h-16";  // 64px
const SCROLL_OFFSET = "scroll-mt-16";  // ๋™์ผํ•˜๊ฒŒ ๋งž์ถค
 
// layout.tsx
<html className="scroll-smooth">
  <body>
    <nav className={`sticky top-0 z-50 ${HEADER_HEIGHT}`}>
      ...๋„ค๋น„
    </nav>
    <main>
      <section id="hero">...</section>
      <section id="studies" className={SCROLL_OFFSET}>...</section>
      <section id="how-it-works" className={SCROLL_OFFSET}>...</section>
    </main>
  </body>
</html>

๐Ÿ† ์‹ค์ „: ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ์Šคํ„ฐ๋”” ์นด๋“œ ์บ๋Ÿฌ์…€ + ์„น์…˜ ์Šค๋ƒ…

์ˆ˜ํ‰ ์นด๋“œ ์บ๋Ÿฌ์…€

// components/StudyCarousel.tsx
// ๐Ÿฆ ์˜ํ˜ธ: "JavaScript ํ•œ ์ค„ ์—†์ด ์Šค์™€์ดํ”„ ์บ๋Ÿฌ์…€ ์™„์„ฑ์ด์—์š”"
 
interface Study {
  id: string;
  title: string;
  topic: string;
  memberCount: number;
  thumbnail: string;
}
 
export default function StudyCarousel({ studies }: { studies: Study[] }) {
  return (
    <div className="relative">
      <h2 className="text-2xl font-bold mb-4 px-4">์ถ”์ฒœ ์Šคํ„ฐ๋””</h2>
 
      {/* ๐Ÿ”ฎ ํ•ต์‹ฌ: ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ */}
      <div className="
        flex                    /* ๊ฐ€๋กœ ์ •๋ ฌ */
        overflow-x-auto         /* ๊ฐ€๋กœ ์Šคํฌ๋กค ํ™œ์„ฑํ™” */
        snap-x                  /* X์ถ• ์Šค๋ƒ… */
        snap-mandatory          /* ๊ฐ•์ œ ์Šค๋ƒ… */
        gap-4                   /* ์นด๋“œ ๊ฐ„๊ฒฉ */
        px-4                    /* ์–‘์ชฝ ์—ฌ๋ฐฑ */
        pb-4                    /* ์Šคํฌ๋กค๋ฐ” ๊ณต๊ฐ„ */
        -mx-4                   /* ๋ถ€๋ชจ padding ์ƒ์‡„ */
 
        /* ์Šคํฌ๋กค๋ฐ” ์ˆจ๊ธฐ๊ธฐ (ํฌ๋กœ์Šค ๋ธŒ๋ผ์šฐ์ €) */
        scrollbar-hide          /* tailwind-scrollbar-hide ํ”Œ๋Ÿฌ๊ทธ์ธ ํ•„์š” */
        [&::-webkit-scrollbar]:hidden  /* ๋˜๋Š” ์ด ๋ฐฉ์‹ */
      ">
        {/* ์ฒซ ๋ฒˆ์งธ ์—ฌ๋ฐฑ ์•„์ดํ…œ - snap-center๋ฅผ ์œ„ํ•ด ํ•„์š” */}
        {/* snap-center ์“ฐ์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ด๊ฑด ์ƒ๋žต ๊ฐ€๋Šฅ */}
 
        {studies.map((study) => (
          <div
            key={study.id}
            className="
              snap-start              /* ์นด๋“œ ์™ผ์ชฝ์ด ์Šค๋ƒ… ํฌ์ธํŠธ */
              shrink-0                /* flex ์•„์ดํ…œ ์ถ•์†Œ ๋ฐฉ์ง€ */
              w-[280px]               /* ์นด๋“œ ๋„ˆ๋น„ ๊ณ ์ • */
              rounded-2xl overflow-hidden
              bg-white shadow-md
              hover:shadow-xl
              transition-shadow duration-200
              cursor-pointer
            "
          >
            {/* ์ธ๋„ค์ผ */}
            <div className="h-40 overflow-hidden">
              <img
                src={study.thumbnail}
                alt={study.title}
                className="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
              />
            </div>
 
            {/* ์นด๋“œ ๋‚ด์šฉ */}
            <div className="p-4">
              <span className="text-xs font-medium text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded-full">
                {study.topic}
              </span>
              <h3 className="font-semibold text-gray-900 mt-2 line-clamp-2">{study.title}</h3>
              <p className="text-sm text-gray-500 mt-1">๋ฉค๋ฒ„ {study.memberCount}๋ช…</p>
            </div>
          </div>
        ))}
      </div>
 
      {/* ์ธ๋””์ผ€์ดํ„ฐ (CSS only - overflow ๋’ค์— gradient fade) */}
      <div className="absolute top-0 right-0 bottom-4 w-16 bg-linear-to-l from-gray-50 to-transparent pointer-events-none" />
    </div>
  );
}

ํ’€ํŽ˜์ด์ง€ ์„น์…˜ ์Šค๋ƒ…

// app/landing/page.tsx
// ๐Ÿฆ ์˜ํ˜ธ: "๊ฐ ์„น์…˜์ด ๋”ฑ๋”ฑ ๋งž๊ฒŒ ์Šค๋ƒ…๋˜๋Š” ๋žœ๋”ฉํŽ˜์ด์ง€์•ผ"
// ๐Ÿฃ ์˜์ฒ : "์ด๊ฑฐ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฐพ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ..."
 
export default function LandingPage() {
  return (
    // ํ’€ํŽ˜์ด์ง€ ์Šค๋ƒ… ์ปจํ…Œ์ด๋„ˆ
    <div className="
      h-screen                /* ๋ทฐํฌํŠธ ๋†’์ด๋กœ ์ œํ•œ */
      overflow-y-auto         /* ์„ธ๋กœ ์Šคํฌ๋กค */
      snap-y                  /* Y์ถ• ์Šค๋ƒ… */
      snap-mandatory          /* ๊ฐ•์ œ ์Šค๋ƒ… */
      overscroll-none         /* ํ’€ํŽ˜์ด์ง€ ํŠน์„ฑ์ƒ ๋ฐ”์šด์Šค ์ œ๊ฑฐ */
      scroll-smooth           /* ๋ถ€๋“œ๋Ÿฌ์šด ์Šค๋ƒ… ์ด๋™ */
    ">
      {/* ์„น์…˜ 1: ํžˆ์–ด๋กœ */}
      <section className="
        snap-start             /* ์„น์…˜ ์ƒ๋‹จ์ด ๋ทฐํฌํŠธ ์ƒ๋‹จ์— ๋งž์ถฐ์ง */
        h-screen               /* ์ •ํ™•ํžˆ 1 ํ™”๋ฉด ๋†’์ด */
        flex items-center justify-center
        bg-linear-to-br from-violet-900 to-indigo-900
      ">
        <div className="text-center text-white">
          <h1 className="text-6xl font-black mb-6
            bg-linear-to-r from-violet-300 to-indigo-300
            bg-clip-text text-transparent">
            ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ
          </h1>
          <p className="text-xl text-white/70">
            ๊ฐœ๋ฐœ์ž ์Šคํ„ฐ๋”” ๋งค์นญ ํ”Œ๋žซํผ
          </p>
        </div>
      </section>
 
      {/* ์„น์…˜ 2: ๊ธฐ๋Šฅ ์†Œ๊ฐœ */}
      <section className="
        snap-start
        h-screen
        flex items-center justify-center
        bg-white
      ">
        <div className="max-w-4xl mx-auto px-4 text-center">
          <h2 className="text-4xl font-bold text-gray-900 mb-12">์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋‚˜์š”?</h2>
          <div className="grid grid-cols-3 gap-8">
            {/* 3๋‹จ๊ณ„ ์†Œ๊ฐœ ์นด๋“œ */}
            <div className="p-6 rounded-2xl bg-indigo-50">
              <div className="text-3xl mb-3">๐Ÿ”</div>
              <h3 className="font-bold mb-2">์Šคํ„ฐ๋”” ํƒ์ƒ‰</h3>
              <p className="text-gray-600 text-sm">๊ด€์‹ฌ ๊ธฐ์ˆ  ์Šคํƒ์œผ๋กœ ํ•„ํ„ฐ๋ง</p>
            </div>
            <div className="p-6 rounded-2xl bg-purple-50">
              <div className="text-3xl mb-3">๐Ÿค</div>
              <h3 className="font-bold mb-2">๋งค์นญ ์‹ ์ฒญ</h3>
              <p className="text-gray-600 text-sm">์›ํด๋ฆญ์œผ๋กœ ์ฐธ์—ฌ ์‹ ์ฒญ</p>
            </div>
            <div className="p-6 rounded-2xl bg-pink-50">
              <div className="text-3xl mb-3">๐Ÿš€</div>
              <h3 className="font-bold mb-2">ํ•จ๊ป˜ ์„ฑ์žฅ</h3>
              <p className="text-gray-600 text-sm">์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…์œผ๋กœ ์†Œํ†ต</p>
            </div>
          </div>
        </div>
      </section>
 
      {/* ์„น์…˜ 3: ์Šคํ„ฐ๋”” ๋ชฉ๋ก ๋ฏธ๋ฆฌ๋ณด๊ธฐ */}
      <section className="
        snap-start
        h-screen
        flex flex-col items-center justify-center
        bg-gray-50
        px-4
      ">
        <h2 className="text-4xl font-bold text-gray-900 mb-8">์ธ๊ธฐ ์Šคํ„ฐ๋””</h2>
        {/* ์„น์…˜ ๋‚ด๋ถ€์— ๊ฐ€๋กœ ์บ๋Ÿฌ์…€ ์‚ฝ์ž… */}
        {/* overscroll-contain์œผ๋กœ ๊ฐ€๋กœ ์Šคํฌ๋กค์ด ์„ธ๋กœ ํ’€ํŽ˜์ด์ง€์— ๊ฐ„์„ญ ์•ˆ ํ•จ */}
        <div className="
          w-full max-w-4xl
          flex overflow-x-auto
          snap-x snap-mandatory
          overscroll-x-contain    /* ๊ฐ€๋กœ ์Šคํฌ๋กค์ด ์„ธ๋กœ ์Šค๋ƒ…์— ์ „ํŒŒ ์•ˆ ๋จ */
          gap-4 pb-4
        ">
          {[1, 2, 3, 4, 5].map((i) => (
            <div key={i} className="snap-center shrink-0 w-64 bg-white rounded-xl shadow p-4">
              <div className="h-32 bg-indigo-100 rounded-lg mb-3" />
              <h3 className="font-semibold">React ์‹ฌํ™” ์Šคํ„ฐ๋”” {i}</h3>
              <p className="text-sm text-gray-500">4๋ช… ์ฐธ์—ฌ ์ค‘</p>
            </div>
          ))}
        </div>
      </section>
    </div>
  );
}

๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

์œ ํ‹ธ๋ฆฌํ‹ฐ์—ญํ• ์ ์šฉ ๋Œ€์ƒ
snap-x๊ฐ€๋กœ ์Šค๋ƒ… ํ™œ์„ฑํ™”๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ
snap-y์„ธ๋กœ ์Šค๋ƒ… ํ™œ์„ฑํ™”๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ
snap-mandatoryํ•ญ์ƒ ์Šค๋ƒ… ํฌ์ธํŠธ์—์„œ ๋ฉˆ์ถค๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ
snap-proximity๊ฐ€๊นŒ์šธ ๋•Œ๋งŒ ์Šค๋ƒ…๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ
snap-start์•„์ดํ…œ ์‹œ์ž‘์ ์ด ์Šค๋ƒ… ํฌ์ธํŠธ์ž์‹ ์•„์ดํ…œ
snap-center์•„์ดํ…œ ์ค‘์•™์ด ์Šค๋ƒ… ํฌ์ธํŠธ์ž์‹ ์•„์ดํ…œ
snap-end์•„์ดํ…œ ๋์ ์ด ์Šค๋ƒ… ํฌ์ธํŠธ์ž์‹ ์•„์ดํ…œ
scroll-smooth์•ต์ปค ์ด๋™ ์‹œ ๋ถ€๋“œ๋Ÿฌ์šด ์Šคํฌ๋กค<html> ๋˜๋Š” ์ปจํ…Œ์ด๋„ˆ
overscroll-contain์Šคํฌ๋กค ์ „ํŒŒ(์ฒด์ธ) ์ฐจ๋‹จ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์š”์†Œ
overscroll-none์Šคํฌ๋กค ์ „ํŒŒ + ๋ฐ”์šด์Šค ๋ชจ๋‘ ์ฐจ๋‹จํ’€ํŽ˜์ด์ง€ ์Šคํฌ๋กค
scroll-mt-*์•ต์ปค ์ด๋™ ์‹œ ์œ„์ชฝ ์—ฌ๋ฐฑ์•ต์ปค ๋Œ€์ƒ ์š”์†Œ
scroll-pt-*์Šค๋ƒ…/์•ต์ปค ์ด๋™ ์‹œ ์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘ ์˜คํ”„์…‹์Šค๋ƒ… ์ปจํ…Œ์ด๋„ˆ

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ์˜์ˆ™์ด๊ฐ€ ์š”์ฒญํ•œ "์นด๋“œ ๋‹จ์œ„๋กœ ๋”ฑ๋”ฑ ๋ฉˆ์ถ”๋Š” ๊ฐ€๋กœ ์บ๋Ÿฌ์…€"์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์ปจํ…Œ์ด๋„ˆ(๋ถ€๋ชจ)์— ํ•„์š”ํ•œ ์ตœ์†Œ ํด๋ž˜์Šค ์กฐํ•ฉ์€?

โœ… ์ •๋‹ต: overflow-x-auto snap-x snap-mandatory

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: Scroll Snap์€ ํ•ญ์ƒ ๋ถ€๋ชจ + ์ž์‹ ๋‘ ๋ ˆ์ด์–ด๊ฐ€ ํ•„์š”ํ•ด. ๋ถ€๋ชจ์—๋Š” โ‘  overflow-x-auto (์Šคํฌ๋กค ํ™œ์„ฑํ™”), โ‘ก snap-x (๊ฐ€๋กœ ๋ฐฉํ–ฅ ์Šค๋ƒ… ํ™œ์„ฑํ™”), โ‘ข snap-mandatory (์—„๊ฒฉํ•œ ์Šค๋ƒ…) ์„ธ ๊ฐ€์ง€๊ฐ€ ํ•„์š”ํ•˜๊ณ , ๊ฐ ์ž์‹ ์นด๋“œ์—๋Š” snap-start ๋˜๋Š” snap-center๊ฐ€ ์žˆ์–ด์•ผ ์‹ค์ œ๋กœ ๋™์ž‘ํ•ด.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: snap-x๋งŒ ์ค˜๋„ ์•ˆ ๋ผ. ์ž์‹์— snap-center ์—†์œผ๋ฉด ์Šค๋ƒ… ํฌ์ธํŠธ๊ฐ€ ์—†์–ด์„œ ํšจ๊ณผ ์—†์–ด. ๋ฐ˜๋“œ์‹œ ๋ถ€๋ชจ + ์ž์‹์˜ ๋‘ ๋ ˆ์ด์–ด ์กฐํ•ฉ์ด์•ผ.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๋ถ€๋ชจ๋Š” "๋ฐฉํ–ฅ + ์—„๊ฒฉ๋„", ์ž์‹์€ "์ •๋ ฌ ์œ„์น˜". ๋ถ€๋ชจ๊ฐ€ "์–ด๋””์„œ ์Šค๋ƒ…ํ• ์ง€", ์ž์‹์ด "๋‚ด ์–ด๋””์— ๊ฑธ๋ฆด์ง€"๋ฅผ ๊ฐ๊ฐ ๋‹ด๋‹นํ•ด.

Q2. ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ ์•ฑ์—์„œ ์ฑ„ํŒ…์ฐฝ ๋‚ด๋ถ€๋ฅผ ์Šคํฌ๋กคํ•˜๋‹ค ๋์— ๋„๋‹ฌํ•˜๋ฉด ๋ฐฐ๊ฒฝ ํŽ˜์ด์ง€๋„ ํ•จ๊ป˜ ์Šคํฌ๋กค๋˜๋Š” ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ์–ด๋–ค Tailwind ํด๋ž˜์Šค๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?

โœ… ์ •๋‹ต: ์ฑ„ํŒ… ์ปจํ…Œ์ด๋„ˆ์— overscroll-contain ์ถ”๊ฐ€

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: ์ด ํ˜„์ƒ์„ "์Šคํฌ๋กค ์ฒด์ธ(scroll chaining)" ์ด๋ผ ํ•ด. ๋‚ด๋ถ€ ์Šคํฌ๋กค์ด ๋๋‚˜๋ฉด ์ด๋ฒคํŠธ๊ฐ€ ๋ถ€๋ชจ๋กœ ์ „ํŒŒ๋˜๋Š” ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋™์ž‘์ด์•ผ. overscroll-contain ์€ CSS์˜ overscroll-behavior: contain ์„ ์ ์šฉํ•ด ์Šคํฌ๋กค ์ด๋ฒคํŠธ๊ฐ€ ์ด ์š”์†Œ๋ฅผ ๋ฒ—์–ด๋‚˜์ง€ ๋ชปํ•˜๊ฒŒ ์ฐจ๋‹จํ•ด. iOS Safari์˜ ๋ฐ”์šด์Šค ํšจ๊ณผ๋Š” ์œ ์ง€๋ผ์„œ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋А๋‚Œ๋„ ์‚ด์•„์žˆ์–ด.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: JavaScript preventDefault()๋กœ ๋ง‰์œผ๋ฉด passive event listener ๊ฒฝ๊ณ ์™€ ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด. CSS ํ•œ ์ค„์ด ํ›จ์”ฌ ์•ˆ์ „ํ•˜๊ณ  ํšจ์œจ์ ์ด์•ผ.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๋ชจ๋‹ฌ/์ฑ„ํŒ…์ฐฝ/๋“œ๋กœ์–ด์ฒ˜๋Ÿผ "์Šคํฌ๋กค ๋…๋ฆฝ ๊ณต๊ฐ„"์ด ํ•„์š”ํ•œ ๊ณณ์—” ํ•ญ์ƒ overscroll-contain ์„ธํŠธ.

Q3. ์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ์— sticky ๋„ค๋น„๊ฒŒ์ด์…˜(๋†’์ด 64px)์ด ์žˆ๋‹ค. ์•ต์ปค ๋งํฌ๋กœ #studies ์„น์…˜์œผ๋กœ ์ด๋™ ์‹œ ์„น์…˜ ์ œ๋ชฉ์ด ๋„ค๋น„ ์•„๋ž˜์— ์ˆจ๋Š” ๋ฒ„๊ทธ๋ฅผ CSS๋งŒ์œผ๋กœ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด?

โœ… ์ •๋‹ต: #studies ์„น์…˜์— scroll-mt-16 ์ถ”๊ฐ€ (16 = 64px)

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: scroll-margin-top ์†์„ฑ์€ ์•ต์ปค ์ด๋™ ์‹œ ํ•ด๋‹น ์š”์†Œ๊ฐ€ ๋ทฐํฌํŠธ ์ƒ๋‹จ์œผ๋กœ๋ถ€ํ„ฐ ์–ผ๋งˆ๋‚˜ ๋–จ์–ด์ง„ ๊ณณ์— ์œ„์น˜ํ• ์ง€๋ฅผ ์„ค์ •ํ•ด. scroll-mt-16 = 64px์ด๋ฏ€๋กœ, 64px ๋†’์ด์˜ sticky ํ—ค๋” ์•„๋ž˜์—์„œ ๋”ฑ ์‹œ์ž‘๋ผ. padding-top๊ณผ ๋‹ฌ๋ฆฌ ๋ ˆ์ด์•„์›ƒ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ ์žฅ์ ์ด์•ผ.
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: pt-16์œผ๋กœ ํ•ด๊ฒฐํ•˜๋ฉด ์„น์…˜ ๋‚ด๋ถ€ ๊ฐ„๊ฒฉ์ด ํ‹€์–ด์ ธ์„œ ๋ ˆ์ด์•„์›ƒ์ด ๋ง๊ฐ€์ ธ. scroll-mt-* ๋Š” ์Šคํฌ๋กค ์œ„์น˜๋งŒ ์กฐ์ •ํ•˜๊ณ  ์‹ค์ œ ๋ ˆ์ด์•„์›ƒ์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•„.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: sticky ํ—ค๋” ๋†’์ด = scroll-mt ๊ฐ’. h-16 nav โ†’ scroll-mt-16 sections. ํ•ญ์ƒ ์ง์„ ๋งž์ถฐ.

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜์€ ์ง„์งœ ์ถฉ๊ฒฉ์ด์—ˆ๋‹ค.

์˜์ˆ™ ๋ˆ„๋‚˜๊ฐ€ "์นด๋“œ๋ฅผ ๋”ฑ๋”ฑ ๋ฉˆ์ถ”๋Š” ์Šค์™€์ดํ”„ ์บ๋Ÿฌ์…€"์„ ์š”์ฒญํ–ˆ๊ณ , ๋‚˜๋Š” ์ฆ‰์‹œ ๊ตฌ๊ธ€์—์„œ
"react swiper carousel" ์„ ๊ฒ€์ƒ‰ํ–ˆ๋‹ค. Swiper.js ๊ณต์‹ ๋ฌธ์„œ๋„ ์—ด์—ˆ๋‹ค.

์˜ํ˜ธ ํ˜•์ด ๋’ค์—์„œ ๋ณด๋”๋‹ˆ "์˜์ฒ  ๋‹˜, ๊ทธ๊ฑฐ snap-x snap-mandatory snap-center ์„ธ ๊ฐœ๋ฉด ๋˜๋Š”๋ฐ์š”?"๋ผ๊ณ  ํ–ˆ๋‹ค.

์ง„์งœ๋กœ ์„ธ ํด๋ž˜์Šค์˜€๋‹ค. NPM ํŒจํ‚ค์ง€๋„ ์—†๊ณ , ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋„ 0, JavaScript๋„ 0.

๊ทธ๋Ÿฌ๋ฉด์„œ ์˜ํ˜ธ ํ˜•์ด "CSS๋Š” 90๋…„๋Œ€๋ถ€ํ„ฐ ์Šคํฌ๋กค์„ ๋‹ค๋ฃจ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ,
์šฐ๋ฆฌ๊ฐ€ 10๋…„ ๋™์•ˆ JavaScript๋กœ ๊ทธ๊ฑธ ์žฌ๋ฐœ๋ช…ํ•˜๊ณ  ์žˆ์—ˆ๋˜ ๊ฑฐ์˜ˆ์š”"๋ผ๋Š” ๋ง์„ ํ–ˆ๋Š”๋ฐ,
๊ทธ ๋ง์ด ๊ณ„์† ๋จธ๋ฆฟ์†์—์„œ ๋งด๋Œ์•˜๋‹ค.

์–ผ๋งˆ๋‚˜ ๋งŽ์€ ๊ฒƒ๋“ค์ด ์ด๋ฏธ CSS์— ์žˆ๋Š”๋ฐ ๋‚ด๊ฐ€ ๋ชจ๋ฅด๋Š” ๊ฑธ๊นŒ.


๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ