Graphic/SVG

SVG(1)

curious week 2025. 9. 22. 20:55

SVG 태그

        <!-- viewBox 시작 x, 시작 y, width, height (x, y, width, height으로 대체 가능)-->
        <svg
          viewBox="0 0 400 200"
          width="100%"
          style="background: #0b0b10; display: block">
          <!-- 좌표 격자 -->
          <g stroke="#223" stroke-width="1">
            <path d="M0 100H400M200 0V200" />
          </g>

          <!-- 기준점 -->
          <circle cx="200" cy="100" r="4" fill="#0af" />
          <rect
            x="40"
            y="40"
            width="80"
            height="40"
            rx="6"
            fill="#162033"
            stroke="#0af" />
          <text
            x="200"
            y="30"
            fill="#cfd6e6"
            text-anchor="middle"
            font-family="Inter, system-ui">
            viewBox(0 0 400 200)
          </text>
        </svg>

<svg
  class="svg--pa"                        // CSS 훅(여러 개 가능). ex) 'svg svg--pa is-dark'
  role="img"                             // 접근성: 이 SVG를 '이미지' 역할로 노출
  aria-labelledby="morsTitle morsDesc"   // 스크린리더가 <title>, <desc>를 읽도록 연결
  viewBox="0 0 400 200"                  // 내부 좌표계(minX=0, minY=0, width=400, height=200)
                                         //  └ 형식: "minX minY width height"
                                         //     minX/minY: 내부 원점(좌상단). 보통 0 0 사용
  width="100%"                           // 렌더 크기(부모 기준 % 가능). px, em, vw 등도 가능
                                         //  └ 예) width="640"  // px 정수·소수 가능
  height="auto"                          // 높이(반응형). 컨테이너가 높이를 갖고 있으면 "100%"도 가능
                                         //  └ 예) height="200" (px), height="50vh" 등
  preserveAspectRatio="xMidYMid meet"    // 뷰박스→렌더박스 맞출 때 비율/정렬
                                         //  ├ 정렬 토큰: xMin|xMid|xMax + YMin|YMid|YMax
                                         //  ├ 스케일 토큰: meet(전체 보이게) | slice(빈틈 없이 채우기, 잘릴 수 있음)
                                         //  └ 특수: none(비율 무시, 찌그러질 수 있음)
  fill="none"                            // 기본 채우기색(하위 요소로 상속). ex) '#0af' | 'currentColor'
  stroke="#0af"                          // 기본 선색(상속)
  stroke-width="2"                       // 기본 선 두께(상속). 단위 없는 수=px로 해석
  vector-effect="non-scaling-stroke"     // 확대/축소해도 stroke 두께 고정(아이콘/UI에 유용)
  shape-rendering="auto"                 // 라인 렌더링 힌트: auto | crispEdges | geometricPrecision
  text-rendering="optimizeLegibility"    // 텍스트 렌더링 힌트: optimizeLegibility | geometricPrecision
  pointer-events="auto"                  // 히트 테스트 제어: auto | none | visiblePainted …
  tabindex="0"                           // 키보드 포커스 가능(인터랙티브일 때만 추천)
  focusable="true"                       // 일부 UA/임베드 환경 호환용(대부분 생략 가능)
  // xmlns="http://www.w3.org/2000/svg"  // 독립 .svg 파일/외부 임베드에선 명시 권장(인라인은 보통 생략)
>
  <!-- 대체 텍스트 제목 -->
  <title id="morsTitle">MORS — Motion-Oriented Reference for Styles</title>  
  <!-- 추가 설명 -->
  <desc id="morsDesc">Logo grid and reference bar</desc>                      

  <!-- 내용 예시 -->
  <rect x="20" y="20" width="360" height="160" rx="10" stroke="#223"/>      
  <!-- fill은 상위에서 none 상속 -->
  <line x1="0" y1="100" x2="400" y2="100" stroke="#223" stroke-dasharray="4 4"/>
</svg>
  • “좌표계 필요하면 중첩 <svg>, 아니면 <g>.”
  • “자식 <svg>는 작은 캔버스다: x,y로 배치, width/height로 크기, viewBox로 내부 좌표, preserveAspectRatio로 채우기.”

1) x, y

  • 의미: 부모 좌표계(부모 <svg>의 viewBox)에서 자식 <svg>의 좌상단 위치를 정한다.
  • 단위: 숫자(px로 해석)·퍼센트(부모 뷰포트에 대한 비율) 모두 가능.
  • 루트 <svg>에서는 무시(문서 루트는 HTML 레이아웃 규칙을 따르므로).
<svg viewBox="0 0 400 200" width="100%">
  <!-- 가운데 가이드 -->
  <line x1="200" y1="0" x2="200" y2="200" stroke="#333"/>

  <!-- 자식 SVG를 부모의 (200, 30)에 배치 -->
  <svg x="200" y="30" width="160" height="120" viewBox="0 0 160 120" preserveAspectRatio="xMidYMid meet" style="outline:1px dashed #666">
    <rect x="0" y="0" width="160" height="120" fill="#0a1a2a"/>
    <circle cx="80" cy="60" r="40" fill="#0af"/>
  </svg>
</svg>

2) width, height

  • 의미: 자식 <svg>가 부모 좌표계에서 차지하는 렌더링 크기.
  • 퍼센트 사용 시: 부모 뷰포트 크기에 대한 비율로 계산.
  • 포인트: 자식 내부의 실제 도형은 자식의 viewBox 기준으로 그려지며, 그 결과가 width × height 박스에 맞춰 스케일된다.

3) viewBox

  • 의미: 자식 <svg> 내부 사용 좌표계의 범위.
  • 패턴: 내부를 viewBox="0 0 W H"로 정의해 두고, 외부(부모)에서 x,y,width,height로 배치/스케일한다.
  • 장점: 내부 콘텐츠의 일관된 스케일 유지 + 다양한 배치에 재사용 쉬움.
<!-- 같은 내부 viewBox를 가진 아이콘을 서로 다른 크기/위치에 재사용 -->
<svg viewBox="0 0 400 200" width="100%">
  <svg x="20"  y="20"  width="60"  height="60"  viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="#0af"/></svg>
  <svg x="120" y="20"  width="120" height="60"  viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="#0af"/></svg>
  <svg x="260" y="20"  width="60"  height="120" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="#0af"/></svg>
</svg>

4) preserveAspectRatio

  • 의미: viewBox → 실제 박스(width × height)에 맞출 때 비율 유지/자르기/정렬을 결정.
  • preserveAspectRatio="xMidYMid meet|slice|none" (xMidYMid meet(기본). 꽉 채우고 잘라도 되면 slice. 비율 무시하려면 none.)
  • xMin | xMid | xMax 는 가로 정렬(anchor).
    • xMin = 왼쪽 정렬, xMid = 가운데 정렬, xMax = 오른쪽 정렬.
  • YMin | YMid | YMax 는 세로 정렬(anchor).
    • YMin = 위쪽, YMid = 세로 가운데, YMax = 아래쪽.
  • 그래서 xMidYMid는 가로·세로 모두 중앙 정렬을 의미.
  • 자주 쓰는 값
    • xMidYMid meet : 전체 보이게(여백 생길 수 있음).
    • xMidYMid slice: 빈틈 없이 채우기(잘릴 수 있음).
    • none : 비율 무시(찌그러질 수 있음).
  • 중첩 <svg>마다 다르게 설정할 수 있어, 한 화면 안에서 서로 다른 채우기 전략을 혼용 가능.
<svg viewBox="0 0 360 120" width="100%">
  <!-- 같은 viewBox지만 다른 채우기 규칙 -->
  <svg x="0"   y="0" width="120" height="120" viewBox="0 0 100 60" preserveAspectRatio="xMidYMid meet"  style="outline:1px solid #555">
    <rect x="0" y="0" width="100" height="60" fill="#0af"/>
  </svg>
  <svg x="120" y="0" width="120" height="120" viewBox="0 0 100 60" preserveAspectRatio="xMidYMid slice" style="outline:1px solid #555">
    <rect x="0" y="0" width="100" height="60" fill="#7c3aed"/>
  </svg>
  <svg x="240" y="0" width="120" height="120" viewBox="0 0 100 60" preserveAspectRatio="none"        style="outline:1px solid #555">
    <rect x="0" y="0" width="100" height="60" fill="#12b886"/>
  </svg>
</svg>

5) overflow

  • 기본: 대부분 브라우저에서 중첩 <svg>의 렌더링 박스를 넘는 요소는 잘릴 수 있음(브라우저·컨텍스트에 따라 다름).
  • 필요 시 노출: <svg overflow="visible">로 내부가 바깥으로 삐져나오도록 허용.
  • 대안: <g> 그룹은 기본적으로 부모 viewBox 내에서만 그려지므로, “잘림 없이 한 좌표계에서만” 다루려면 <g>가 단순하다.

6) 스타일 상속·이벤트

  • 스타일: 중첩 <svg>에 지정한 fill, stroke 등은 그 자식들에만 상속. 바깥 <svg>와 분리된 기본값 영역을 만들 수 있다.
  • 포인터 이벤트: 중첩 <svg>에 pointer-events="none"을 주면, 안쪽 전체가 히트에서 제외된다(필요 시 자식에만 다시 켜기).
  • fill, stroke, stroke-width, opacity
    예: fill="none" stroke="#0af" stroke-width="2".
  • stroke-linecap="butt|round|square", stroke-linejoin="miter|round|bevel", stroke-miterlimit="…"
  • shape-rendering="auto|crispEdges|geometricPrecision"
    얇은 1px 라인 또렷하게: crispEdges.
  • text-rendering="optimizeLegibility|geometricPrecision"
    텍스트 품질 힌트.
  • vector-effect="non-scaling-stroke"
    확대/축소해도 선 굵기 고정(자주 유용).

7) 중첩 <svg>에서만 쓰이는 것 (+ <g>와의 차이)

  • 루트 <svg>(HTML 문서에 직접 붙어 있는 최상위)에서는 x, y가 의미 없음.
  • 중첩 <svg>(다른 <svg> 내부에 들어간 자식 <svg>)에서는 x, y, width, height, viewBox, preserveAspectRatio가 그 자체로 작은 “독립 뷰포트”를 만든다.
  • 단순한 그룹핑/스타일 공유만 필요하면 <g>*를 쓰고, 좌표계를 따로 두고 싶으면 중첩 <svg>를 쓴다.

언제 <g> 대신 중첩 <svg>가 필요한가?

  • 독립 좌표계가 필요할 때: 안쪽 콘텐츠를 자기만의 viewBox로 스케일/정렬하고 싶다.
  • 다른 비율/자르기 규칙을 적용하고 싶을 때: 부모는 meet, 자식은 slice 같은 식으로 분리.
  • 클리핑/오버플로우 제어를 지역적으로 하고 싶을 때: 중첩 <svg>는 기본적으로 자신만의 뷰포트와 경계가 있어 레이아웃이 안정적.
  • 반면, 색/선/transform 공통 적용 정도면 <g>로 충분하다. <g>는 좌표계를 새로 만들지 않는다.

8) 네임스페이스/호환(대부분 자동)

9) 접근성/역할

  • role="img"
    이미지로 인식시키기(아이콘/로고에 좋음).
  • aria-label="문구" 또는 aria-labelledby="titleId descId"
    스크린리더용 대체 텍스트. 함께 <title>/<desc> 자식 요소를 두면 좋아.
  • tabindex="0"
    키보드 포커스 가능(인터랙티브일 때만).
  • focusable="false|true"
    일부 브라우저/레거시 IE/SVG 임베드 케이스에서 포커싱 제어.

중첩 svg 실전 패턴 ①: 아이콘 스프라이트처럼 쓰기

한 번 만든 아이콘을 다양한 크기/비율로 재활용하는 전형적 패턴.

<svg viewBox="0 0 420 160" width="100%">
  <!-- 아이콘 정의용: 중첩 SVG (독립 좌표계 0..100) -->
  <svg id="icon-base" viewBox="0 0 100 100" width="0" height="0" style="position:absolute">
    <circle cx="50" cy="50" r="40" fill="#0af"/>
    <path d="M20 50 H80" stroke="#fff" stroke-width="10" stroke-linecap="round"/>
  </svg>

  <!-- 재사용: 서로 다른 배치/크기/채우기 규칙 -->
  <svg x="20"  y="20"  width="60"  height="60"  viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
    <use href="#icon-base"/>
  </svg>
  <svg x="120" y="20"  width="120" height="60"  viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice">
    <use href="#icon-base"/>
  </svg>
  <svg x="260" y="20"  width="60"  height="120" viewBox="0 0 100 100" preserveAspectRatio="none">
    <use href="#icon-base"/>
  </svg>
</svg>

g 실전 패턴 ②: 로고/히어로에서 안전한 레이아웃

히어로 안에 작은 배지나 미니 차트를 찌그러짐 없이 넣고 싶을 때 유용.

<svg viewBox="0 0 800 360" width="100%">
  <!-- 배경 -->
  <rect x="0" y="0" width="800" height="360" fill="#0b0b10"/>

  <!-- 왼쪽 정보 카드(같은 좌표계) -->
  <g transform="translate(40,40)">
    <rect width="320" height="120" rx="12" fill="#121a26" stroke="#223"/>
    <text x="16" y="42" fill="#cfe6ff" font-family="Inter" font-size="20">MORS</text>
  </g>

  <!-- 오른쪽 미니 그래프: 독립 좌표/비율로 안전하게 -->
  <svg x="440" y="60" width="300" height="160" viewBox="0 0 300 160" preserveAspectRatio="xMidYMid meet" style="outline:1px dashed #333">
    <polyline points="0,130 40,120 80,90 120,100 160,60 200,80 240,40 300,55" fill="none" stroke="#0af" stroke-width="3"/>
  </svg>
</svg>

디버깅 체크(중첩 <svg>에서 특히 흔한 실수)

  • 안 보임: 자식 <svg>의 width/height가 0이거나, viewBox 범위를 벗어난 좌표를 쓰고 있는지 확인.
  • 찌그러짐: 의도치 않게 preserveAspectRatio="none"이거나, 부모/자식의 비율이 크게 다름.
  • 클릭 안 됨: 부모/자식 중 누군가 pointer-events="none"을 갖고 있거나, overflow로 잘려 히트영역이 줄어든 경우.
  • 선 두께가 이상: vector-effect="non-scaling-stroke"로 고정하거나, 스케일이 큰 컨테이너 안에서는 도형 개별로 굵기를 조정.