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) 네임스페이스/호환(대부분 자동)
- xmlns="http://www.w3.org/2000/svg"
인라인 SVG는 브라우저가 보통 자동으로 처리하지만, 독립 파일이나 외부 임베드에선 명시. - xmlns:xlink="http://www.w3.org/1999/xlink"
SVG2 이후 xlink: 대신 href 권장이라 보통 불필요.
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"로 고정하거나, 스케일이 큰 컨테이너 안에서는 도형 개별로 굵기를 조정.