๋ฐ˜์‘ํ˜• ํ…Œ์ŠคํŠธ ๋„๊ตฌ
์—ฌ๊ธฐ์— ๊ด‘๊ณ ๋ฅผ ๋„ฃ์œผ์„ธ์š”
RTT ํŒ€ ํ๋ ˆ์ด์…˜

๋ฐ˜์‘ํ˜• ๋””์ž์ธ ๋ฐ ์„ฑ๋Šฅ์„ ์œ„ํ•œ ๊ฐœ๋ฐœ์ž ํŒ

HTML, CSS, JavaScript, ์„ฑ๋Šฅ, ์ ‘๊ทผ์„ฑ์— ๋Œ€ํ•œ ์‹ค์šฉ์ ์ธ ๋ณต์‚ฌ-๋ถ™์—ฌ๋„ฃ๊ธฐ ๊ฐ€๋Šฅํ•œ ํŒ์ž…๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๋ฌด๋ฃŒ ๋ทฐํฌํŠธ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, CSS ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ ์˜จ๋ผ์ธ ํ™•์ธ์„ ์‹œ๋„ํ•˜๊ฑฐ๋‚˜, ํƒœ๋ธ”๋ฆฟ์—์„œ ์›น์‚ฌ์ดํŠธ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ณด์ด๋Š”์ง€ ๊ถ๊ธˆํ•˜๋“  โ€” ์ด ํŒ๋“ค์€ ๋ฐ˜์‘ํ˜• ํ…Œ์ŠคํŠธ ๋„๊ตฌ ์›Œํฌํ”Œ๋กœ์šฐ์™€ ์™„๋ฒฝํ•˜๊ฒŒ ์–ด์šธ๋ฆฝ๋‹ˆ๋‹ค. ๊ฐ ํŒ์—๋Š” ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์ œ ์ฝ”๋“œ ์˜ˆ์ œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

30๊ฐœ ์ด์ƒ ํŒ
6๊ฐœ ์นดํ…Œ๊ณ ๋ฆฌ
์—…๋ฐ์ดํŠธ: 2026๋…„ 4์›”
๋ชจ๋ฐ”์ผ ์ค‘์‹ฌ
ํ•„ํ„ฐ:
์นดํ…Œ๊ณ ๋ฆฌ 01

HTML ๋ชจ๋ฒ” ์‚ฌ๋ก€

5๊ฐœ ํŒ

ํƒ„ํƒ„ํ•œ HTML์€ ๋ชจ๋“  ํ›Œ๋ฅญํ•œ ์›น์‚ฌ์ดํŠธ์˜ ๊ธฐ์ดˆ์ž…๋‹ˆ๋‹ค. ์ด ํŒ๋“ค์€ ๋ธŒ๋ผ์šฐ์ €์™€ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๊ฐ€ ์„ ํ˜ธํ•˜๋Š” ์˜๋ฏธ๋ก ์ ์ด๊ณ  ๊ฒฌ๊ณ ํ•˜๋ฉฐ ๋ฏธ๋ž˜ ์ง€ํ–ฅ์ ์ธ ๋งˆํฌ์—…์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

HTML ยท ์ด๋ฏธ์ง€
๋ ˆ์ด์•„์›ƒ ๋ณ€๊ฒฝ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด <img>์— ํ•ญ์ƒ width & height ์‚ฌ์šฉ
HTML์„ฑ๋Šฅ

์ด๋ฏธ์ง€์— ๋ช…์‹œ์ ์ธ width ๋ฐ height ์†์„ฑ์„ ์„ค์ •ํ•˜๋ฉด ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋“œ๋˜๊ธฐ ์ „์— ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๊ณต๊ฐ„์„ ์˜ˆ์•ฝํ•˜์—ฌ Google์˜ Core Web Vitals ์ค‘ ํ•˜๋‚˜์ธ ๋ˆ„์  ๋ ˆ์ด์•„์›ƒ ๋ณ€๊ฒฝ(CLS)์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

HTML
<!-- โŒ ๋‚˜์จ: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ด๋ฏธ์ง€ ๋กœ๋“œ ์ „๊นŒ์ง€ ํฌ๊ธฐ๋ฅผ ๋ชจ๋ฆ„ -->
<img src="hero.jpg" alt="ํžˆ์–ด๋กœ ์ด๋ฏธ์ง€">

<!-- โœ… ์ข‹์Œ: ๊ณต๊ฐ„ ์ฆ‰์‹œ ์˜ˆ์•ฝ -->
<img src="hero.jpg"
     alt="ํžˆ์–ด๋กœ ์ด๋ฏธ์ง€"
     width="1200"
     height="630"
     loading="lazy">
CSS์—์„œ aspect-ratio: auto์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด CSS๊ฐ€ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•  ๋•Œ์—๋„ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๋น„์œจ์„ ์ž๋™์œผ๋กœ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
HTML ยท ์‹œ๋งจํ‹ฑ
ํŽ˜์ด์ง€๋‹น ํ•˜๋‚˜์˜ <h1>์„ ์‚ฌ์šฉํ•˜๊ณ  ์ œ๋ชฉ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ์œ ์ง€
HTML์ ‘๊ทผ์„ฑ

์ ์ ˆํ•˜๊ฒŒ ์ค‘์ฒฉ๋œ ์ œ๋ชฉ์œผ๋กœ ๊ตฌ์ถ•๋œ ๋ฌธ์„œ ๊ฐœ์š”๋Š” ๊ฒ€์ƒ‰ ์—”์ง„์ด ์ฝ˜ํ…์ธ  ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž๊ฐ€ ํŽ˜์ด์ง€๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํƒ์ƒ‰ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

HTML
<h1>ํŽ˜์ด์ง€ ์ œ๋ชฉ (ํŽ˜์ด์ง€๋‹น ํ•˜๋‚˜)</h1>
  <h2>์ฃผ์š” ์„น์…˜</h2>
    <h3>ํ•˜์œ„ ์„น์…˜</h3>
    <h3>๋‹ค๋ฅธ ํ•˜์œ„ ์„น์…˜</h3>
  <h2>๋‹ค๋ฅธ ์ฃผ์š” ์„น์…˜</h2>
HTML ยท ๋งํฌ
์™ธ๋ถ€ ๋งํฌ์— rel="noopener noreferrer" ์ถ”๊ฐ€
HTML

target="_blank"๋กœ ์ƒˆ ํƒญ์—์„œ ๋งํฌ๋ฅผ ์—ด ๋•Œ, ์—ด๋ฆฐ ํŽ˜์ด์ง€๋Š” window.opener๋ฅผ ํ†ตํ•ด ๊ท€ํ•˜์˜ ํŽ˜์ด์ง€์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. rel="noopener noreferrer"๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์ด ๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ๋ฆฌํผ๋Ÿฌ ์ •๋ณด๊ฐ€ ์ „์†ก๋˜๋Š” ๊ฒƒ๋„ ๋ง‰์Šต๋‹ˆ๋‹ค.

HTML
<!-- โŒ ์ทจ์•ฝํ•จ -->
<a href="https://example.com" target="_blank">๋ฐฉ๋ฌธํ•˜๊ธฐ</a>

<!-- โœ… ์•ˆ์ „ํ•จ -->
<a href="https://example.com"
   target="_blank"
   rel="noopener noreferrer">๋ฐฉ๋ฌธํ•˜๊ธฐ</a>
HTML ยท ํผ
autocomplete ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋ฐ”์ผ ํผ UX ๊ฐœ์„ 
HTML๋ชจ๋ฐ”์ผ

autocomplete ์†์„ฑ์€ ๋ธŒ๋ผ์šฐ์ €์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ด€๋ฆฌ์ž์—๊ฒŒ ํผ ํ•„๋“œ๋ฅผ ๋ฏธ๋ฆฌ ์ฑ„์šฐ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋ฐ”์ผ ํผ ์™„๋ฃŒ์œจ์„ ๊ทน์ ์œผ๋กœ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

HTML
<input type="text"    autocomplete="given-name">
<input type="email"   autocomplete="email">
<input type="tel"     autocomplete="tel">
<input type="password" autocomplete="current-password">
<input type="text"    autocomplete="postal-code">
HTML ยท ๋ฉ”ํƒ€
์†Œ์…œ ๋ฏธ๋””์–ด ๊ณต์œ  ํ–ฅ์ƒ์„ ์œ„ํ•ด Open Graph ํƒœ๊ทธ ์ถ”๊ฐ€
HTML

Open Graph ๋ฉ”ํƒ€ ํƒœ๊ทธ๋Š” Facebook, LinkedIn, Twitter/X์™€ ๊ฐ™์€ ์†Œ์…œ ๋ฏธ๋””์–ด ํ”Œ๋žซํผ์—์„œ ๊ณต์œ ๋  ๋•Œ ํŽ˜์ด์ง€๊ฐ€ ์–ด๋–ป๊ฒŒ ํ‘œ์‹œ๋˜๋Š”์ง€ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. ์ด ํƒœ๊ทธ๊ฐ€ ์—†์œผ๋ฉด ํ”Œ๋žซํผ์—์„œ ์ž„์˜๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

HTML โ€” <head>
<meta property="og:title"       content="ํŽ˜์ด์ง€ ์ œ๋ชฉ">
<meta property="og:description"  content="์งง์€ ์„ค๋ช…">
<meta property="og:image"       content="https://yourdomain.com/og.jpg">
<meta property="og:url"         content="https://yourdomain.com/page">
<meta property="og:type"        content="website">
<!-- Twitter/X -->
<meta name="twitter:card"      content="summary_large_image">
์นดํ…Œ๊ณ ๋ฆฌ 02

CSS ํŒ๊ณผ ์š”๋ น

6๊ฐœ ํŒ

์ตœ์‹  CSS๋Š” ๋งค์šฐ ๊ฐ•๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒ๋“ค์€ ๋ ˆ์ด์•„์›ƒ, ์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ, ๊ทธ๋ฆฌ๊ณ  ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•˜๊ณ  ์Šคํƒ€์ผ์‹œํŠธ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋Š” ํŒจํ„ด์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

CSS ยท ๋ ˆ์ด์•„์›ƒ
CSS Grid๋กœ ๋ฌด์—‡์ด๋“  2์ค„๋กœ ์™„๋ฒฝํ•˜๊ฒŒ ์ค‘์•™ ๋ฐฐ์น˜
CSS

์œ„์น˜ ์ง€์ • ํ•ต๊ณผ ์‹ธ์šฐ์ง€ ๋งˆ์„ธ์š”. CSS Grid์˜ place-items ๋‹จ์ถ• ์†์„ฑ์€ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€๋กœ ๋ฐ ์„ธ๋กœ๋กœ ๋‘ ์ค„๋กœ ์ค‘์•™์— ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. calc๋„, transform๋„ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.

CSS
.center-everything {
  display: grid;
  place-items: center;
}

/* ์ „์ฒด ํŽ˜์ด์ง€ ์ค‘์•™ ๋ฐฐ์น˜์—๋„ ์ž‘๋™ */
body {
  display: grid;
  place-items: center;
  min-height: 100vh;
}
CSS ยท ์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ
์œ ์ง€ ๊ด€๋ฆฌ ๊ฐ€๋Šฅํ•œ ๋””์ž์ธ ์‹œ์Šคํ…œ์„ ์œ„ํ•ด CSS ์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ ์‚ฌ์šฉ
CSS

CSS ์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ(๋ณ€์ˆ˜)์„ ์‚ฌ์šฉํ•˜๋ฉด ๋””์ž์ธ ํ† ํฐ์„ ํ•œ ๋ฒˆ ์ •์˜ํ•˜๊ณ  ๋ชจ๋“  ๊ณณ์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์–ด ๋‹คํฌ ๋ชจ๋“œ ๋ฐ ํ…Œ๋งˆ ์ง€์ •์ด ๊ฐ„๋‹จํ•ด์ง‘๋‹ˆ๋‹ค.

CSS
:root {
  --color-primary: #6366f1;
  --color-bg: #ffffff;
  --radius-md: 12px;
  --spacing-md: 16px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0d1117;
  }
}

.btn {
  background: var(--color-primary);
  border-radius: var(--radius-md);
  padding: var(--spacing-md);
}
CSS ยท ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ
clamp()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ ๋™์ ์ธ ๋ฐ˜์‘ํ˜• ํƒ€์ดํฌ๊ทธ๋ž˜ํ”ผ ๊ตฌํ˜„
CSS๋ฐ˜์‘ํ˜•

clamp(min, preferred, max)๋Š” ๋ทฐํฌํŠธ ๋„ˆ๋น„์— ๋”ฐ๋ผ ๊ธ€๊ผด ํฌ๊ธฐ๊ฐ€ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์กฐ์ •๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ๊ฐ€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. ์ค‘๊ฐ„ ๊ฐ’์€ ์ผ๋ฐ˜์ ์œผ๋กœ vw ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.

CSS
/* clamp(์ตœ์†Œ, ์„ ํ˜ธ, ์ตœ๋Œ€) */
h1 {
  font-size: clamp(1.8rem, 5vw, 3.5rem);
}

p {
  font-size: clamp(0.9rem, 2vw, 1.1rem);
  line-height: 1.7;
  max-width: 65ch; /* ์ตœ์  ์ฝ๊ธฐ ๋„ˆ๋น„ */
}
CSS ยท ์Šคํฌ๋กค
์•ต์ปค ๋งํฌ๋ฅผ ์œ„ํ•œ ๋ถ€๋“œ๋Ÿฌ์šด ์Šคํฌ๋กค ๋ฐ scroll-margin
CSSUX

์ „์—ญ์ ์œผ๋กœ ๋ถ€๋“œ๋Ÿฌ์šด ์Šคํฌ๋กค์„ ํ™œ์„ฑํ™”ํ•˜๊ณ  ์•ต์ปค ๋Œ€์ƒ์— scroll-margin-top์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํฌ๋กค๋˜๋Š” ์ฝ˜ํ…์ธ  ์œ„์— ๊ณ ์ • ํ—ค๋”๊ฐ€ ๊ฒน์น˜์ง€ ์•Š๋„๋ก ํ•˜์„ธ์š”.

CSS
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* ๊ณ ์ • ํ—ค๋” ์˜คํ”„์…‹ (์˜ˆ: 70px ๋†’์ด) */
[id] {
  scroll-margin-top: 90px;
}
ํ•ญ์ƒ prefers-reduced-motion: no-preference ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ ์•ˆ์— scroll-behavior: smooth๋ฅผ ๋ž˜ํ•‘ํ•˜์„ธ์š”. ์ผ๋ถ€ ์‚ฌ์šฉ์ž๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํฌ๋กค๋กœ ์ธํ•ด ๋ฉ€๋ฏธ๋ฅผ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
CSS ยท ๋ ˆ์ด์•„์›ƒ
์ž๊ฐ€ ์น˜์œ  ์นด๋“œ ๋ ˆ์ด์•„์›ƒ์„ ์œ„ํ•ด CSS Grid auto-fill ์‚ฌ์šฉ
CSS๋ฐ˜์‘ํ˜•

auto-fill + minmax() ํŒจํ„ด์€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ณต๊ฐ„์— ๋”ฐ๋ผ ์—ด ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ •ํ•˜๋Š” ๊ทธ๋ฆฌ๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ๊ฐ€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.

CSS
.card-grid {
  display: grid;
  grid-template-columns: repeat(
    auto-fill,
    minmax(280px, 1fr)
  );
  gap: 24px;
}
/* ๋ชจ๋ฐ”์ผ์—์„œ 1์—ด โ†’ 2 โ†’ 3 โ†’ 4์—ด๋กœ ์ž๋™ ์กฐ์ • */
CSS ยท ๊ฐ€์‹œ์„ฑ
outline์„ ์ œ๊ฑฐํ•˜๋Š” ๋Œ€์‹  :focus-visible ์‚ฌ์šฉ
CSS์ ‘๊ทผ์„ฑ

์ ˆ๋Œ€ ์ „์—ญ์ ์œผ๋กœ outline: none์„ ํ•˜์ง€ ๋งˆ์„ธ์š”. ํ‚ค๋ณด๋“œ ํƒ์ƒ‰์„ ๋ฐฉํ•ดํ•ฉ๋‹ˆ๋‹ค. :focus-visible์„ ์‚ฌ์šฉํ•˜์—ฌ ํ‚ค๋ณด๋“œ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ ํฌ์ปค์Šค ๋ง์„ ํ‘œ์‹œํ•˜๊ณ  ๋งˆ์šฐ์Šค ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” ๋””์ž์ธ์„ ๊น”๋”ํ•˜๊ฒŒ ์œ ์ง€ํ•˜์„ธ์š”.

CSS
/* โŒ ์ ˆ๋Œ€ ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š” */
* { outline: none; }

/* โœ… ๋งˆ์šฐ์Šค์—๋Š” ์ˆจ๊ธฐ๊ณ  ํ‚ค๋ณด๋“œ์—๋Š” ํ‘œ์‹œ */
:focus:not(:focus-visible) {
  outline: none;
}
:focus-visible {
  outline: 2px solid #6366f1;
  outline-offset: 3px;
  border-radius: 4px;
}
์นดํ…Œ๊ณ ๋ฆฌ 03

JavaScript ํŒ

5๊ฐœ ํŒ

์ด๋Ÿฌํ•œ ์ตœ์‹  ํŒจํ„ด๊ณผ ๋ธŒ๋ผ์šฐ์ € API๋กœ ๋” ๊น”๋”ํ•˜๊ณ  ์„ฑ๋Šฅ์ด ๋›ฐ์–ด๋‚œ JavaScript๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.

JS ยท API
์ง€์—ฐ ๋กœ๋”ฉ ๋ฐ ์Šคํฌ๋กค ํšจ๊ณผ์— IntersectionObserver ์‚ฌ์šฉ
JS์„ฑ๋Šฅ

scroll ์ด๋ฒคํŠธ(์ดˆ๋‹น ์ˆ˜๋ฐฑ ๋ฒˆ ๋ฐœ์ƒ)๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๋Œ€์‹ , IntersectionObserver API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์†Œ๊ฐ€ ๋ทฐํฌํŠธ์— ๋“ค์–ด์˜ค๊ฑฐ๋‚˜ ๋‚˜๊ฐˆ ๋•Œ๋งŒ ๋ฐ˜์‘ํ•˜์„ธ์š”.

JavaScript
const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('visible');
        observer.unobserve(entry.target); // ๊ด€์ฐฐ ์ค‘์ง€
      }
    });
  },
  { rootMargin: '0px 0px -50px 0px' }
);

document.querySelectorAll('.animate-on-scroll')
  .forEach(el => observer.observe(el));
JS ยท ์„ฑ๋Šฅ
์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด resize ๋ฐ input ์ด๋ฒคํŠธ ๋””๋ฐ”์šด์Šค
JS์„ฑ๋Šฅ

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

JavaScript
function debounce(fn, delay = 300) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

// ์‚ฌ์šฉ๋ฒ•
window.addEventListener('resize',
  debounce(() => {
    recalculateLayout();
  }, 200)
);
JS ยท API
ํด๋ฆฝ๋ณด๋“œ ๋ณต์‚ฌ ๋ฒ„ํŠผ์— Clipboard API ์‚ฌ์šฉ
JSUX

์ตœ์‹  navigator.clipboard API๋Š” ํ”„๋กœ๋ฏธ์Šค ๊ธฐ๋ฐ˜์ด๋ฉฐ ๊ธฐ์กด์˜ document.execCommand('copy') ํ•ดํ‚น๋ณด๋‹ค ํ›จ์”ฌ ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค.

JavaScript
async function copyToClipboard(text) {
  try {
    await navigator.clipboard.writeText(text);
    showToast('๋ณต์‚ฌ๋จ!');
  } catch (err) {
    console.error('๋ณต์‚ฌ ์‹คํŒจ:', err);
  }
}

// ์‚ฌ์šฉ๋ฒ•
document.querySelector('.copy-btn')
  .addEventListener('click', () =>
    copyToClipboard('๋ณต์‚ฌํ•  ํ…์ŠคํŠธ')
  );
JS ยท ์ €์žฅ์†Œ
์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ—ฌํผ๋กœ localStorage ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉ
JS

localStorage๋Š” ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ ๋ธŒ๋ผ์šฐ์ง• ๋ชจ๋“œ๋‚˜ ์ €์žฅ ๊ณต๊ฐ„์ด ๊ฐ€๋“ ์ฐผ์„ ๋•Œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ try/catch ํ—ฌํผ๋กœ ๊ฐ์‹ธ์„ธ์š”.

JavaScript
const storage = {
  get(key, fallback = null) {
    try {
      const item = localStorage.getItem(key);
      return item ? JSON.parse(item) : fallback;
    } catch { return fallback; }
  },
  set(key, value) {
    try {
      localStorage.setItem(key, JSON.stringify(value));
      return true;
    } catch { return false; }
  }
};

storage.set('theme', 'dark');
const theme = storage.get('theme', 'light');
JS ยท ๋ฏธ๋””์–ด
JavaScript์—์„œ ์‚ฌ์šฉ์ž์˜ reduced-motion ํ™˜๊ฒฝ์„ค์ • ๊ฐ์ง€
JS์ ‘๊ทผ์„ฑ

JavaScript ๊ธฐ๋ฐ˜ ์• ๋‹ˆ๋ฉ”์ด์…˜(์˜ˆ: canvas, GSAP)์˜ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž์˜ prefers-reduced-motion ์„ค์ •์„ ํ™•์ธํ•˜๊ณ  ๊ทธ์— ๋”ฐ๋ผ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋‹จ์ˆœํ™”ํ•˜๊ฑฐ๋‚˜ ๋น„ํ™œ์„ฑํ™”ํ•˜์„ธ์š”.

JavaScript
const prefersReduced = window
  .matchMedia('(prefers-reduced-motion: reduce)')
  .matches;

if (!prefersReduced) {
  startAnimations();
} else {
  showStaticFallback();
}
์นดํ…Œ๊ณ ๋ฆฌ 04

์„ฑ๋Šฅ ํŒ

5๊ฐœ ํŒ

๋น ๋ฅธ ์›น์‚ฌ์ดํŠธ๋Š” ๋” ๋†’์€ ์ˆœ์œ„๋ฅผ ๊ธฐ๋กํ•˜๊ณ , ๋” ์ž˜ ์ „ํ™˜๋˜๋ฉฐ, ์‚ฌ์šฉ์ž๋ฅผ ํ–‰๋ณตํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด ํŒ๋“ค์€ ์›น ์„ฑ๋Šฅ์—์„œ ๊ฐ€์žฅ ํฐ ํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

์„ฑ๋Šฅ ยท ํฐํŠธ
Google Fonts์™€ ํ•จ๊ป˜ font-display: swap ๋ฐ preconnect ์‚ฌ์šฉ
์„ฑ๋Šฅ

font-display: swap์ด ์—†์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํฐํŠธ๊ฐ€ ๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€ ํ…์ŠคํŠธ๋ฅผ ์ˆจ๊น๋‹ˆ๋‹ค(FOIT). ์ด ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ์‹œ์Šคํ…œ ํฐํŠธ๊ฐ€ ์ฆ‰์‹œ ํ‘œ์‹œ๋œ ๋‹ค์Œ ๊ต์ฒด๋˜์–ด Largest Contentful Paint(LCP)๊ฐ€ ํฌ๊ฒŒ ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

HTML โ€” <head>
<!-- 1๋‹จ๊ณ„: preconnect -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- 2๋‹จ๊ณ„: URL์— &display=swap ์ถ”๊ฐ€ -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&family=Noto+Sans+KR:wght@700;900&display=swap"
      rel="stylesheet">
์„ฑ๋Šฅ ยท ์ด๋ฏธ์ง€
<picture> ํด๋ฐฑ์œผ๋กœ ์ตœ์‹  ์ด๋ฏธ์ง€ ํ˜•์‹ ์ œ๊ณต
์„ฑ๋ŠฅHTML

WebP ๋ฐ AVIF ์ด๋ฏธ์ง€๋Š” ๋™์ผํ•œ ํ’ˆ์งˆ์˜ JPEG ๋ฐ PNG๋ณด๋‹ค ํ›จ์”ฌ ์ž‘์Šต๋‹ˆ๋‹ค. <picture> ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์‹  ํ˜•์‹์„ ์ œ๊ณตํ•˜๊ณ  ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์œ„ํ•ด JPEG ํด๋ฐฑ์„ ์ œ๊ณตํ•˜์„ธ์š”.

HTML
<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg"
       alt="ํžˆ์–ด๋กœ ์ด๋ฏธ์ง€"
       width="1200" height="630"
       loading="lazy">
</picture>
์„ฑ๋Šฅ ยท ์Šคํฌ๋ฆฝํŠธ
defer ๋˜๋Š” async๋กœ ์ค‘์š”ํ•˜์ง€ ์•Š์€ ์Šคํฌ๋ฆฝํŠธ ๋กœ๋“œ
์„ฑ๋ŠฅHTML

์ผ๋ฐ˜ <script> ํƒœ๊ทธ๋Š” HTML ํŒŒ์‹ฑ์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. defer๋Š” ํŒŒ์‹ฑ์ด ์™„๋ฃŒ๋œ ํ›„ ์ˆœ์„œ๋Œ€๋กœ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. async๋Š” ๋‹ค์šด๋กœ๋“œ๋˜๋Š” ์ฆ‰์‹œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค(์ˆœ์„œ ๋ณด์žฅ ์•ˆ ๋จ).

HTML
<!-- ๋ Œ๋”๋ง ์ฐจ๋‹จ โŒ -->
<script src="app.js"></script>

<!-- DOM ์ดํ›„ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ โœ… (๋Œ€๋ถ€๋ถ„์˜ ์Šคํฌ๋ฆฝํŠธ์— ์‚ฌ์šฉ) -->
<script src="app.js" defer></script>

<!-- ์ค€๋น„๋˜๋Š” ์ฆ‰์‹œ ์‹คํ–‰ โœ… (๋ถ„์„, ๊ด‘๊ณ ) -->
<script src="analytics.js" async></script>
์„ฑ๋Šฅ ยท CSS
GPU ๊ฐ€์† ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์œ„ํ•ด will-change๋ฅผ ๋“œ๋ฌผ๊ฒŒ ์‚ฌ์šฉ
์„ฑ๋ŠฅCSS

transform๊ณผ opacity๋งŒ ์• ๋‹ˆ๋ฉ”์ด์…˜ํ•˜์„ธ์š”. ์ด๋“ค์€ ๋ ˆ์ด์•„์›ƒ์ด๋‚˜ ํŽ˜์ธํŠธ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•˜์ง€ ์•Š๊ณ  GPU ์ปดํฌ์ง€ํ„ฐ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. will-change๋Š” ํ”„๋กœํŒŒ์ผ๋ง ํ›„ ์‹ค์ œ ์ด์ ์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

CSS
/* โŒ ๋ ˆ์ด์•„์›ƒ ํŠธ๋ฆฌ๊ฑฐ (๋น„์šฉ ๋†’์Œ) */
.bad { transition: width 0.3s, top 0.3s; }

/* โœ… GPU ์ปดํฌ์ง€ํŠธ (๋น„์šฉ ๋‚ฎ์Œ) */
.good { transition: transform 0.3s, opacity 0.3s; }

/* ๋ณต์žกํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ „์—๋งŒ ๋“œ๋ฌผ๊ฒŒ ์‚ฌ์šฉ */
.modal-enter { will-change: transform; }
.modal-entered { will-change: auto; } /* ์ดํ›„ ์ดˆ๊ธฐํ™” */
์„ฑ๋Šฅ ยท ๋ฆฌ์†Œ์Šค
ํด๋“œ ์œ„ ์ค‘์š” ์ž์‚ฐ ํ”„๋ฆฌ๋กœ๋“œ
์„ฑ๋ŠฅHTML

<link rel="preload">๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ CSS๋‚˜ JS์—์„œ ๋ฐœ๊ฒฌํ•˜๊ธฐ ์ „์— ์ค‘์š” ๋ฆฌ์†Œ์Šค๋ฅผ ์ผ์ฐ ๊ฐ€์ ธ์˜ค๋„๋ก ์•Œ๋ฆฌ์„ธ์š”. ํžˆ์–ด๋กœ ์ด๋ฏธ์ง€, ์ค‘์š” ํฐํŠธ, ์ฃผ์š” ์Šคํฌ๋ฆฝํŠธ์— ์ด์ƒ์ ์ž…๋‹ˆ๋‹ค.

HTML โ€” <head>
<!-- ํžˆ์–ด๋กœ ์ด๋ฏธ์ง€ ํ”„๋ฆฌ๋กœ๋“œ (LCP ์š”์†Œ) -->
<link rel="preload" as="image"
      href="hero.webp" fetchpriority="high">

<!-- ์ค‘์š” ํฐํŠธ ํ”„๋ฆฌ๋กœ๋“œ -->
<link rel="preload" as="font"
      href="font.woff2" type="font/woff2"
      crossorigin>
์นดํ…Œ๊ณ ๋ฆฌ 05

์ ‘๊ทผ์„ฑ ํŒ

5๊ฐœ ํŒ

์ ‘๊ทผ์„ฑ์€ ์ฒดํฌ๋ฆฌ์ŠคํŠธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ข‹์€ ๋””์ž์ธ์ž…๋‹ˆ๋‹ค. ์ด ํŒ๋“ค์€ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ์‚ฌ์šฉ์ž์™€ ํ‚ค๋ณด๋“œ ํƒ์ƒ‰์ž๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ์‚ฌ๋žŒ์ด ์‚ฌ์ดํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

์ ‘๊ทผ์„ฑ ยท ARIA
์•„์ด์ฝ˜ ์ „์šฉ ๋ฒ„ํŠผ์— aria-label ์‚ฌ์šฉ
์ ‘๊ทผ์„ฑHTML

์•„์ด์ฝ˜๋งŒ ํฌํ•จ๋œ ๋ฒ„ํŠผ์—๋Š” ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๊ฐ€ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ํ…์ŠคํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. aria-label์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์„ค๋ช… ๋ ˆ์ด๋ธ”์„ ์ œ๊ณตํ•˜์„ธ์š”.

HTML
<!-- โŒ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”: "๋ฒ„ํŠผ" -->
<button><i class="fa-solid fa-times"></i></button>

<!-- โœ… ์Šคํฌ๋ฆฐ ๋ฆฌ๋”: "๋Œ€ํ™” ์ƒ์ž ๋‹ซ๊ธฐ" -->
<button aria-label="๋Œ€ํ™” ์ƒ์ž ๋‹ซ๊ธฐ">
  <i class="fa-solid fa-times" aria-hidden="true"></i>
</button>
์ ‘๊ทผ์„ฑ ยท ์–ธ์–ด
<html>์— ํ•ญ์ƒ lang ์†์„ฑ ์„ ์–ธ
์ ‘๊ทผ์„ฑHTML

lang ์†์„ฑ์€ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์— ์‚ฌ์šฉํ•  ์–ธ์–ด๋ฅผ ์•Œ๋ ค์ฃผ๊ณ , CSS์—์„œ ์˜ฌ๋ฐ”๋ฅธ ํ•˜์ดํ”ˆ ๋„ฃ๊ธฐ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉฐ, ๋ฒˆ์—ญ ๋„๊ตฌ๊ฐ€ ์ฝ˜ํ…์ธ  ์–ธ์–ด๋ฅผ ์‹๋ณ„ํ•˜๋„๋ก ๋•์Šต๋‹ˆ๋‹ค. RTL ์–ธ์–ด์˜ ๊ฒฝ์šฐ dir="rtl"๋„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

HTML
<!-- ํ•œ๊ตญ์–ด -->
<html lang="ko">

<!-- ์•„๋ž์–ด (RTL) -->
<html lang="ar" dir="rtl">

<!-- ํ”„๋ž‘์Šค์–ด -->
<html lang="fr">
์ ‘๊ทผ์„ฑ ยท ์ƒ‰์ƒ
ํ…์ŠคํŠธ์˜ ์ตœ์†Œ 4.5:1 ๋ช…์•”๋น„ ๋ณด์žฅ
์ ‘๊ทผ์„ฑCSS

WCAG 2.1 AA๋Š” ์ผ๋ฐ˜ ํ…์ŠคํŠธ์˜ ๊ฒฝ์šฐ ์ตœ์†Œ 4.5:1, ํฐ ํ…์ŠคํŠธ(18px+ ๊ตต๊ฒŒ ๋˜๋Š” 24px+ ์ผ๋ฐ˜)์˜ ๊ฒฝ์šฐ 3:1์˜ ๋ช…์•”๋น„๋ฅผ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ์ถœ์‹œ ์ „์— ๋ช…์•”๋น„ ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

ํ›Œ๋ฅญํ•œ ๋ฌด๋ฃŒ ๋„๊ตฌ: WebAIM Contrast Checker (webaim.org/resources/contrastchecker/) ๋˜๋Š” ๋ฐ์Šคํฌํ†ฑ ์•ฑ Colour Contrast Analyser ์„ค์น˜. Chrome DevTools์˜ ์ƒ‰์ƒ ์„ ํƒ๊ธฐ์—์„œ๋„ ๋ช…์•”๋น„๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
CSS โ€” ์˜ˆ์‹œ
/* โŒ #fff ์œ„ #6b7280 = 4.48:1 (์ž‘์€ ํ…์ŠคํŠธ AA ์‹คํŒจ) */
.muted { color: #6b7280; background: #fff; }

/* โœ… #fff ์œ„ #4b5563 = 7.0:1 (AA & AAA ํ†ต๊ณผ) */
.muted { color: #4b5563; background: #fff; }
์ ‘๊ทผ์„ฑ ยท ์Šคํฌ๋ฆฐ ๋ฆฌ๋”
์‹œ๊ฐ์ ์œผ๋กœ ์ˆจ๊ฒจ์ง„ ์ ‘๊ทผ ๊ฐ€๋Šฅ ํ…์ŠคํŠธ์— .sr-only ์‚ฌ์šฉ
์ ‘๊ทผ์„ฑCSS

๋•Œ๋กœ๋Š” ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๋งŒ ๋“ค์„ ์ˆ˜ ์žˆ๋Š” ํ…์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ ๋งํฌ ๋˜๋Š” ์‹œ๊ฐ์  ์š”์†Œ์˜ ๋ ˆ์ด๋ธ”). .sr-only ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋Š” ์ ‘๊ทผ์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์‹œ๊ฐ์ ์œผ๋กœ ์ˆจ๊น๋‹ˆ๋‹ค.

CSS + HTML
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

<!-- ๊ฑด๋„ˆ๋›ฐ๊ธฐ ๋งํฌ ์˜ˆ์‹œ -->
<a href="#main" class="sr-only">์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ</a>
์ ‘๊ทผ์„ฑ ยท ํ‚ค๋ณด๋“œ
๋ชจ๋‹ฌ ๋ฐ ๋Œ€ํ™” ์ƒ์ž ๋‚ด๋ถ€์— ํฌ์ปค์Šค ํŠธ๋žฉ
์ ‘๊ทผ์„ฑJS

๋ชจ๋‹ฌ์ด ์—ด๋ ค ์žˆ์„ ๋•Œ ํ‚ค๋ณด๋“œ ํฌ์ปค์Šค๋Š” ๋ชจ๋‹ฌ ๋‚ด๋ถ€์— ์ œํ•œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด Tab ํ‚ค๊ฐ€ ์˜ค๋ฒ„๋ ˆ์ด ๋’ค์˜ ์š”์†Œ์— ํฌ์ปค์Šค๋ฅผ ๋งž์ถฐ ํ‚ค๋ณด๋“œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋‹ฌ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

JavaScript
function trapFocus(modal) {
  const focusable = modal.querySelectorAll(
    'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const first = focusable[0];
  const last = focusable[focusable.length - 1];

  modal.addEventListener('keydown', e => {
    if (e.key !== 'Tab') return;
    if (e.shiftKey ? document.activeElement === first
                   : document.activeElement === last) {
      e.preventDefault();
      (e.shiftKey ? last : first).focus();
    }
  });
  first.focus();
}
์นดํ…Œ๊ณ ๋ฆฌ 06

๋ฐ˜์‘ํ˜• ๋ฐ ๋ชจ๋ฐ”์ผ ํŒ

4๊ฐœ ํŒ

๋ชจ๋ฐ”์ผ ํŠธ๋ž˜ํ”ฝ์ด ์ง€๋ฐฐ์ ์ž…๋‹ˆ๋‹ค. ์ด ํŒ๋“ค์€ 320px ํœด๋Œ€ํฐ์—์„œ 4K ๋ชจ๋‹ˆํ„ฐ์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ๋ชจ๋“  ํ™”๋ฉด ํฌ๊ธฐ์—์„œ ๋””์ž์ธ์ด ์•„๋ฆ„๋‹ต๊ฒŒ ์ž‘๋™ํ•˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜• ยท ๋ฉ”ํƒ€
์˜ฌ๋ฐ”๋ฅธ ๋ทฐํฌํŠธ ๋ฉ”ํƒ€ ํƒœ๊ทธ ์‚ฌ์šฉ
๋ชจ๋ฐ”์ผHTML

๋ทฐํฌํŠธ ๋ฉ”ํƒ€ ํƒœ๊ทธ๊ฐ€ ์—†์œผ๋ฉด ๋ชจ๋ฐ”์ผ ๋ธŒ๋ผ์šฐ์ €๋Š” ~980px ๋ฐ์Šคํฌํ†ฑ ๋„ˆ๋น„๋กœ ๋ Œ๋”๋งํ•œ ๋‹ค์Œ ์ถ•์†Œํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ํฌ๊ธฐ ์กฐ์ •์„ ํ—ˆ์šฉํ•˜์„ธ์š”. ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์€ ์ฃผ์š” ์ ‘๊ทผ์„ฑ ์žฅ๋ฒฝ์ด๋ฉฐ ๋ธŒ๋ผ์šฐ์ € ํ™•๋Œ€/์ถ•์†Œ๋ฅผ ๋ฐฉํ•ดํ•ฉ๋‹ˆ๋‹ค.

HTML โ€” <head>
<!-- โŒ ์‚ฌ์šฉ์ž ํ™•๋Œ€ ๋น„ํ™œ์„ฑํ™” (์ ‘๊ทผ์„ฑ ์œ„๋ฐ˜) -->
<meta name="viewport"
      content="width=device-width, initial-scale=1, user-scalable=no">

<!-- โœ… ์˜ฌ๋ฐ”๋ฆ„: ์ตœ๋Œ€ 5๋ฐฐ ํ™•๋Œ€ ํ—ˆ์šฉ -->
<meta name="viewport"
      content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5">
๋ฐ˜์‘ํ˜• ยท ํ„ฐ์น˜
๋ชจ๋ฐ”์ผ์—์„œ ํƒญ ๋Œ€์ƒ ์ตœ์†Œ 44ร—44px๋กœ ๋งŒ๋“ค๊ธฐ
๋ชจ๋ฐ”์ผCSS์ ‘๊ทผ์„ฑ

Apple์˜ Human Interface Guidelines์™€ WCAG 2.5.5๋Š” ์ตœ์†Œ 44ร—44 CSS ํ”ฝ์…€ ํƒญ ๋Œ€์ƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์ž‘์€ ๋Œ€์ƒ์€ ์‚ฌ์šฉ์ž๋ฅผ ๋ถˆํŽธํ•˜๊ฒŒ ํ•˜๊ณ  ์ž˜๋ชป๋œ ํƒญ์„ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค. ํŒจ๋”ฉ์ด๋‚˜ ๊ฐ€์ƒ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ๊ฐ์  ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ํด๋ฆญ ๊ฐ€๋Šฅํ•œ ์˜์—ญ์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CSS
/* ์‹œ๊ฐ์  ๋ณ€๊ฒฝ ์—†์ด ํƒญ ์˜์—ญ ํ™•์žฅ */
.icon-btn {
  position: relative;
  width: 24px; height: 24px;
}
.icon-btn::before {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  min-width: 44px; min-height: 44px;
}
๋ฐ˜์‘ํ˜• ยท RTL
RTL ์–ธ์–ด ์ง€์›์„ ์œ„ํ•ด ๋…ผ๋ฆฌ์  CSS ์†์„ฑ ์‚ฌ์šฉ
RTLCSS

margin-left ๋Œ€์‹  margin-inline-start์™€ ๊ฐ™์€ CSS ๋…ผ๋ฆฌ์  ์†์„ฑ์€ ๋ฌธ์„œ์˜ ์“ฐ๊ธฐ ๋ชจ๋“œ์— ๋”ฐ๋ผ ๋ฐฉํ–ฅ์„ ์ž๋™์œผ๋กœ ๋’ค์ง‘์–ด RTL ์ง€์›์„ ๊ฑฐ์˜ ์ˆ˜์›”ํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

CSS
/* ๋ฌผ๋ฆฌ์  (RTL์—์„œ ๋’ค์ง‘ํžˆ์ง€ ์•Š์Œ) */
.icon { margin-right: 8px; }

/* ๋…ผ๋ฆฌ์  (RTL์—์„œ ์ž๋™์œผ๋กœ ๋’ค์ง‘ํž˜) */
.icon { margin-inline-end: 8px; }

/* ๋” ๋งŽ์€ ๋…ผ๋ฆฌ์  ์†์„ฑ ์˜ˆ์‹œ */
.card {
  padding-inline: 16px;      /* ์™ผ์ชฝ + ์˜ค๋ฅธ์ชฝ */
  padding-block: 12px;       /* ์œ„์ชฝ + ์•„๋ž˜์ชฝ */
  border-start-start-radius: 12px; /* LTR์—์„œ ์™ผ์ชฝ ์œ„ */
}
๋ฐ˜์‘ํ˜• ยท ๋‹จ์œ„
๋ชจ๋ฐ”์ผ ์ „์ฒด ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ์— vh ๋Œ€์‹  dvh ์‚ฌ์šฉ
๋ชจ๋ฐ”์ผCSS

๋ชจ๋ฐ”์ผ ๋ธŒ๋ผ์šฐ์ €์—์„œ 100vh๋Š” ๋ธŒ๋ผ์šฐ์ € ํฌ๋กฌ(์ฃผ์†Œ์ฐฝ)์„ ํฌํ•จํ•˜์—ฌ ๋ง‰๋Œ€๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ ์˜ค๋ฒ„ํ”Œ๋กœ๋ฅผ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด dvh(๋™์  ๋ทฐํฌํŠธ ๋†’์ด) ๋‹จ์œ„๋Š” ๋ธŒ๋ผ์šฐ์ € ํฌ๋กฌ์ด ํ‘œ์‹œ๋˜๊ฑฐ๋‚˜ ์ˆจ๊ฒจ์งˆ ๋•Œ ์กฐ์ •๋ฉ๋‹ˆ๋‹ค.

CSS
/* โŒ ๋ชจ๋ฐ”์ผ์—์„œ ๋ธŒ๋ผ์šฐ์ € ํฌ๋กฌ ๋‚˜ํƒ€๋‚  ๋•Œ ์˜ค๋ฒ„ํ”Œ๋กœ */
.hero { height: 100vh; }

/* โœ… ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ €์šฉ ํด๋ฐฑ ํฌํ•จ */
.hero {
  height: 100vh;          /* ํด๋ฐฑ */
  height: 100dvh;         /* ๋™์  ๋ทฐํฌํŠธ ๋†’์ด */
}
2023๋…„๋ถ€ํ„ฐ ๋ชจ๋“  ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง€์›๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ svh(์ž‘์€ ๋ทฐํฌํŠธ, ํ•ญ์ƒ ํฌ๋กฌ ์ œ์™ธ) ๋ฐ lvh(ํฐ ๋ทฐํฌํŠธ, ํ•ญ์ƒ ํฌ๋กฌ ๊ณต๊ฐ„ ํฌํ•จ)๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•ํ…Œ์ŠคํŠธ๋„๊ตฌ ๋” ๋ณด๊ธฐ

๋ฐ˜์‘ํ˜• ํ…Œ์Šคํ„ฐ ํšŒ์‚ฌ ์†Œ๊ฐœ ๋ฌธ์˜ํ•˜๊ธฐ ๊ฐœ์ธ์ •๋ณด ์ฒ˜๋ฆฌ๋ฐฉ์นจ
๐Ÿ–ฅ๏ธ

์ด ํŒ๋“ค์„ ์‹ค์ œ๋กœ ์ ์šฉํ•ด ๋ณด์„ธ์š”

๋ฐ˜์‘ํ˜• ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ชจ๋ฐ”์ผ, ํƒœ๋ธ”๋ฆฟ, ๋ฐ์Šคํฌํ†ฑ์—์„œ ์–ด๋–ป๊ฒŒ ๋ณด์ด๋Š”์ง€ ์ฆ‰์‹œ, ๋ฌด๋ฃŒ๋กœ, ํšŒ์›๊ฐ€์ž… ์—†์ด ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

์ง€๊ธˆ ์›น์‚ฌ์ดํŠธ ํ…Œ์ŠคํŠธํ•˜๊ธฐ