๐ŸŒˆ ์นœ์ ˆํ•œ SVG ์ž…๋ฌธ ๊ฐ€์ด๋“œ ("A Friendly Introduction to SVG" ์š”์•ฝ)

okorionยท2025๋…„ 8์›” 7์ผ

๐Ÿ“ Document ์š”์•ฝ ๋ฐ ๋ฒˆ์—ญ

๋ชฉ๋ก ๋ณด๊ธฐ
12/90
post-thumbnail

์›๋ฌธ: A Friendly Introduction to SVG


๐Ÿ“š ๋ชฉ์ฐจ

  1. SVG๋ž€ ๋ฌด์—‡์ธ๊ฐ€?
  2. Hello SVG: ์ฝ”๋“œ๋กœ ์‹œ์ž‘ํ•˜๋Š” SVG
  3. SVG์˜ ๊ธฐ๋ณธ ๋„ํ˜• ์š”์†Œ
    • Line (์„ )
    • Rect (์‚ฌ๊ฐํ˜•)
    • Circle (์›)
    • Ellipse (ํƒ€์›)
    • Polygon (๋‹ค๊ฐํ˜•)
  4. SVG๋ฅผ ์Šค์ผ€์ผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ: viewBox
  5. SVG์˜ ์Šคํƒ€์ผ๋ง: ํ‘œํ˜„ ์†์„ฑ
  6. ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ฐ€๋Šฅํ•œ ์ŠคํŠธ๋กœํฌ ํšจ๊ณผ
  7. SVG์˜ ๊ฐ•๋ ฅํ•œ ํž˜

โœจ SVG๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

SVG(Scalable Vector Graphics)๋Š” JPG, PNG์ฒ˜๋Ÿผ ์ด๋ฏธ์ง€ ํฌ๋งท์˜ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ SVG๋Š” ๋ฒกํ„ฐ ๊ธฐ๋ฐ˜ ์ด๋ฏธ์ง€ ํฌ๋งท์œผ๋กœ, XML ๋ฌธ๋ฒ•์„ ํ†ตํ•ด ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค.

๐Ÿ“Œ ์ฆ‰, ํ”ฝ์…€ ๋‹จ์œ„๋กœ ์ƒ‰์„ ์ง€์ •ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, โ€œ๋ฌด์—‡์„ ์–ด๋–ป๊ฒŒ ๊ทธ๋ฆด ๊ฒƒ์ธ์ง€โ€์— ๋Œ€ํ•œ ๋ช…๋ น์–ด(๊ทธ๋ฆฌ๊ธฐ ์ง€์นจ)๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

โœ… ์žฅ์  ์š”์•ฝ

  • HTML ๋ฌธ์„œ์ฒ˜๋Ÿผ ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฝ”๋“œ ํŽธ์ง‘ ๊ฐ€๋Šฅ
  • CSS/JS๋กœ ์ง์ ‘ ์Šคํƒ€์ผ ๋ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ฐ€๋Šฅ
  • ํ™•๋Œ€ํ•ด๋„ ๊นจ์ง€์ง€ ์•Š๋Š” ๋ฒกํ„ฐ ๋ฐฉ์‹
  • DOM์— ํฌํ•จ๋˜์–ด ๋‹ค๋ฅธ ์š”์†Œ์ฒ˜๋Ÿผ ์กฐ์ž‘ ๊ฐ€๋Šฅ

๐Ÿš€ Hello SVG: ์ฝ”๋“œ๋กœ ์‹œ์ž‘ํ•˜๋Š” SVG

SVG๋Š” <img src="..."/>๋กœ ์‚ฝ์ž…ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ง„์ •ํ•œ ํž˜์€ inline SVG์— ์žˆ์Šต๋‹ˆ๋‹ค.

<img
  alt="return to homepage"
  src="/images/home.svg"
/>

์ด๋ ‡๊ฒŒ HTML ์•ˆ์— ์ง์ ‘ SVG๋ฅผ ์ž‘์„ฑํ•˜๋ฉด, JavaScript๋‚˜ CSS๋กœ DOM์ฒ˜๋Ÿผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”ฅ CSS์™€์˜ ์—ฐ๋™ ์˜ˆ์‹œ

<style>
  circle {
    fill: hotpink;
    transition: r 400ms, cy 600ms;
  }
  button:hover circle {
    r: 50;
    cy: 100;
  }
</style>

<button>
  <svg width="100" height="100">
    <circle r="30" cx="50" cy="50" />
  </svg>
</button>

๐Ÿ”ท SVG์˜ ๊ธฐ๋ณธ ๋„ํ˜• ์š”์†Œ

๐Ÿ“ Line (์„ )

<line x1="80" y1="80" x2="200" y2="200" stroke="blue" stroke-width="5" />
  • x1, y1: ์‹œ์ž‘์ 
  • x2, y2: ๋์ 
  • stroke: ์„  ์ƒ‰
  • stroke-width: ์„  ๋‘๊ป˜

๐Ÿ“ฆ Rect (์‚ฌ๊ฐํ˜•)

<rect x="60" y="100" width="180" height="100" stroke="green" fill="none" stroke-width="5"/>
  • x, y: ์ขŒ์ƒ๋‹จ ๊ธฐ์ค€ ์œ„์น˜
  • width, height: ํฌ๊ธฐ
  • rx, ry: ๋‘ฅ๊ทผ ๋ชจ์„œ๋ฆฌ ๋ฐ˜์ง€๋ฆ„

โšช Circle (์›)

<circle cx="140" cy="140" r="70" stroke="purple" fill="none" stroke-width="5"/>
  • cx, cy: ์ค‘์‹ฌ ์œ„์น˜
  • r: ๋ฐ˜์ง€๋ฆ„

๐Ÿฅš Ellipse (ํƒ€์›)

<ellipse cx="150" cy="150" rx="75" ry="50" stroke="orange" fill="none" stroke-width="5"/>
  • rx, ry: ์ˆ˜ํ‰/์ˆ˜์ง ๋ฐ˜์ง€๋ฆ„

๐Ÿ”บ Polygon (๋‹ค๊ฐํ˜•)

<polygon points="60,100 100,180 140,140 180,180 220,100" />
  • points: ์ขŒํ‘œ๋“ค์˜ ๋‚˜์—ด
  • ์ ๊ณผ ์ ์„ ์ง์„ ์œผ๋กœ ์ž‡๊ณ , ๋งˆ์ง€๋ง‰ ์ ๊ณผ ์ฒซ ์ ์„ ์—ฐ๊ฒฐ

๐Ÿ“Œ ์ฐธ๊ณ : ์ •๋‹ค๊ฐํ˜•(์˜ˆ: ์œก๊ฐํ˜•)์„ ๋งŒ๋“ค๋ ค๋ฉด ์‚ผ๊ฐํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ์ขŒํ‘œ๋ฅผ ๊ณ„์‚ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“ SVG๋ฅผ ์Šค์ผ€์ผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ: viewBox

<svg
  width="300"
  viewBox="0 0 300 220"
>
  <circle
    cx="150"
    cy="110"
    r="60"
    stroke="var(--gold)"
    stroke-width="10"
  />
</svg>
  • viewBox="minX minY width height" ํ˜•์‹
  • ๋‚ด๋ถ€ ์ขŒํ‘œ๊ณ„๋ฅผ ์„ค์ •ํ•ด ํ™•๋Œ€/์ถ•์†Œ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ฆ
  • ํ”ฝ์…€ ๊ธฐ๋ฐ˜์ด ์•„๋‹Œ ๋น„์œจ ๊ธฐ๋ฐ˜ ๋ ˆ์ด์•„์›ƒ์„ ๊ตฌํ˜„ ๊ฐ€๋Šฅ

โœ… ์žฅ์ 

  • ๋ฐ˜์‘ํ˜• UI์— ์ตœ์ ํ™”
  • ๋‹ค์–‘ํ•œ ํ™”๋ฉด์—์„œ ๊นจ์ง€์ง€ ์•Š๊ณ  ์„ ๋ช…ํ•จ ์œ ์ง€

๐ŸŽจ SVG์˜ ์Šคํƒ€์ผ๋ง: ํ‘œํ˜„ ์†์„ฑ

SVG๋Š” fill, stroke, stroke-width ๋“ฑ์˜ ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ์†์„ฑ์œผ๋กœ ์Šคํƒ€์ผ๋งํ•ฉ๋‹ˆ๋‹ค.

<circle
  cx="100"
  cy="100"
  r="50"
  stroke="hsl(45 100% 50%)"
  stroke-width="6"
  stroke-dasharray="20, 14"
  stroke-linecap="round"
/>
  • stroke-dasharray: ์ ์„ /๊ฐ„๊ฒฉ ํŒจํ„ด
  • stroke-linecap: ์„ ์˜ ๋ ๋ชจ์–‘ (butt, round, square)

๐ŸŽž ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ฐ€๋Šฅํ•œ ์ŠคํŠธ๋กœํฌ ํšจ๊ณผ

SVG์˜ stroke, stroke-width, stroke-dasharray ๋“ฑ์˜ ์†์„ฑ์€ CSS์—์„œ transition๊ณผ animation์œผ๋กœ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋ณ€ํ™”์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด, SVG๋Š” ๋‹จ์ˆœํ•œ ์ด๋ฏธ์ง€ ์ด์ƒ์˜ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ‘œํ˜„ ๋„๊ตฌ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.


๐Ÿ” ์˜ˆ์ œ 1: ์ ์„ ์ด ํšŒ์ „ํ•˜๋Š” ํšจ๊ณผ

@keyframes spin {
  from {
    stroke-dashoffset: 0;
  }
  to {
    stroke-dashoffset: -100;
  }
}

circle {
  stroke: hotpink;
  stroke-width: 4;
  stroke-dasharray: 10, 10;
  animation: spin 1s linear infinite;
}
  • stroke-dasharray: 10, 10: 10px ์„ , 10px ๊ณต๋ฐฑ์„ ๋ฐ˜๋ณตํ•˜๋Š” ์ ์„ ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • stroke-dashoffset: ์ด ๊ฐ’์„ ์• ๋‹ˆ๋ฉ”์ด์…˜ํ•˜๋ฉด ์ ์„ ์ด ์›€์ง์ด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.

๐Ÿ’ก ๋ฐ˜๋ณต ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋Š๊ธฐ์ง€ ์•Š๊ฒŒ ํ•˜๋ ค๋ฉด dasharray์˜ ์ดํ•ฉ๊ณผ dashoffset์˜ ๋ฒ”์œ„๋ฅผ ์ผ์น˜์‹œ์ผœ์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.


โœ๏ธ ์˜ˆ์ œ 2: ์„ ์ด ๊ทธ๋ ค์ง€๋Š” ๋“ฏํ•œ ํŠธ๋ฆญ

const el = document.querySelector('polygon');
const length = el.getTotalLength();

el.style.strokeDasharray = `${length} ${length}`;
el.style.strokeDashoffset = length;
polygon {
  stroke: steelblue;
  stroke-width: 3;
  fill: none;
  transition: stroke-dashoffset 3s ease;
}
  • getTotalLength()๋Š” SVG ๊ฒฝ๋กœ ์ „์ฒด ๊ธธ์ด๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • strokeDasharray์™€ strokeDashoffset์„ ๋™์ผํ•˜๊ฒŒ ์„ค์ •ํ•œ ๋’ค offset์„ 0์œผ๋กœ ์ค„์ด๋ฉด ์™ธ๊ณฝ์„ ์ด ๊ทธ๋ ค์ง€๋Š” ๋“ฏํ•œ ์—ฐ์ถœ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ๋ฐฉ๋ฒ•์€ path, circle, polygon ๋“ฑ ๋Œ€๋ถ€๋ถ„์˜ SVG ์š”์†Œ์— ์ ์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋กœ๋”ฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด๋‚˜ ์ธํŠธ๋กœ ํšจ๊ณผ๋กœ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.


๐Ÿงช ์˜ˆ์ œ 3: ์ปค์Šคํ…€ path ๊ธธ์ด ์ •์˜ํ•˜๊ธฐ (pathLength ์†์„ฑ)

<style>
  polygon {
    stroke-dasharray: 100, 10000;
    stroke-dashoffset: 100;
    transition: stroke-dashoffset 3000ms;
  }
</style>
<svg viewBox="0 0 280 320">
  <polygon
    pathLength="100"
    points="..."
  />
</svg>
  • pathLength="100"์€ ๊ฒฝ๋กœ ์ „์ฒด ๊ธธ์ด๋ฅผ 100์œผ๋กœ ๊ฐ€์ƒ ์žฌ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด CSS์—์„œ stroke-dasharray, stroke-dashoffset ๊ฐ’์„ ๋‹ค๋ฃฐ ๋•Œ ํ›จ์”ฌ ์ง๊ด€์ ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ๋‹ค๋งŒ, ์‹ค์ œ ๊ธธ์ด์™€ ๋‹ค๋ฅด๊ฒŒ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋””๋ฒ„๊น…ํ•  ๋•Œ ํ˜ผ๋ž€์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋™์  SVG๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋งŒ ์‚ฌ์šฉ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.


โšก SVG์˜ ๊ฐ•๋ ฅํ•œ ํž˜

  • HTML์ฒ˜๋Ÿผ DOM ์กฐ์ž‘ ๊ฐ€๋Šฅ
  • CSS/JS๋กœ ์Šคํƒ€์ผ๊ณผ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ ๊ฐ€๋Šฅ
  • ํฌ๊ธฐ์— ์ƒ๊ด€์—†์ด ์–ธ์ œ๋‚˜ ์„ ๋ช…ํ•œ ํ’ˆ์งˆ
  • ๋ณต์žกํ•œ UI/UX์— ํ™œ์šฉ ๊ฐ€๋Šฅ (์ฐจํŠธ, ์ธํ„ฐ๋ž™์…˜ ๋“ฑ)

๐Ÿ“Œ ๋‹ค์Œ ์ด์•ผ๊ธฐ: <path> ์š”์†Œ

์ด ๊ธ€์—์„œ๋Š” <path>๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š์•˜์ง€๋งŒ, SVG์˜ ์ง„์ •ํ•œ ํž˜์€ ์ด ์š”์†Œ์— ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ž์œ ๋กœ์šด ๊ณก์„ 
  • ๋ณต์žกํ•œ ๋ฒ ์ง€์–ด ๊ณก์„ , ํƒ€์›ํ˜• ์•„ํฌ ๋“ฑ ์ง€์›
  • DSL ํ˜•์‹์˜ ๊ฒฝ๋กœ ์–ธ์–ด (d="M 10 10 L 50 50")

๋‹ค์Œ ํฌ์ŠคํŠธ์—์„œ <path>์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์˜ˆ์‹œ์™€ ์‘์šฉ๋ฒ•์„ ์†Œ๊ฐœํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


๐Ÿ“ฌ ๋งˆ์น˜๋ฉฐ

SVG๋Š” ์›น ๊ฐœ๋ฐœ์—์„œ ๊ผญ ์•Œ์•„์•ผ ํ•  ํ•„์ˆ˜ ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ์ด ๊ธ€์ด SVG ์ž…๋ฌธ์˜ ์žฅ๋ฒฝ์„ ๋‚ฎ์ถ”๋Š” ๋ฐ ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

SVG๋ฅผ ํ™œ์šฉํ•œ ์ธํ„ฐ๋ž™์…˜, ์• ๋‹ˆ๋ฉ”์ด์…˜, ๋ฐ˜์‘ํ˜• UI๊นŒ์ง€!
์ง€๊ธˆ ์‹œ์ž‘ํ•ด๋„ ๋Šฆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. โœจ

profile
okorion's Tech Study Blog.

0๊ฐœ์˜ ๋Œ“๊ธ€