SVG.js: 그림 가져오기, 경로 편집, SVG 내보내기
신뢰할 수 있는 SVG 마크업을 SVG.js로 가져와 이름이 있는 경로의 색을 바꾸세요. 전체 루트·자식 전용 출력을 비교하고 원본 도형과 저장 파일을 확인합니다.
목차
부분 편집을 위해 SVG.js에 그림을 가져오려면 신뢰할 수 있는 SVG 마크업을 .svg()에 전달하고, 작성한 ID로 원하는 요소를 찾아 그 요소의 채움 색을 바꾸세요. 독립 파일로는 그림의 전체 SVG 루트를 내보냅니다. 자식만 내보낸 결과는 조각입니다. 문서로 전달하려면 의도한 치수와 viewBox를 가진 루트가 필요합니다.
이 안내는 직접 만든 속 빈 마름모와 별도의 삼각형을 사용합니다. 삼각형은 황토색에서 산호색으로 바뀌고, 마름모는 청록색 채움과 구멍을 유지합니다. 아래의 완전한 예제를 공식 3.2.6 릴리스로 고정한 SVG.js 3.2.6으로 시험했습니다. 이 관찰은 단순한 자체 작성 경로 두 개에 관한 것이며 다른 SVG의 모든 기능을 다루지는 않습니다.
편집 가능한 도형과 이미지 중 무엇이 필요한지 정하기
파일 URL을 만드는 JavaScript import 문은 로드 문제에 답합니다. 그것만으로 파일의 경로가 편집기의 실제 SVG 트리에 들어가지는 않습니다. SVG.js의 현재 가져오기·내보내기 문서는 마크업을 가져오고 바깥 SVG 요소 또는 그 자식을 얻는 방법을 설명합니다.
| 표현 방식 | 이 예제에서 접근할 수 있는 대상 | 전달 점검 |
|---|---|---|
| 실제 SVG 루트로 가져온 신뢰할 수 있는 마크업 | 이름이 accent인 경로 | 편집 루트를 확인하고 내보낸 파일 다시 열기 |
| 이미지 요소로 로드한 SVG | 이미지 요소 전체 | 이미지 데이터 별도 점검 |
| 자식 전용 마크업 | 원래 루트 없는 경로 요소 두 개 | 루트, 치수, viewBox를 의도적으로 제공 |
이미지 안의 SVG도 여전히 SVG 콘텐츠입니다. 다만 이미지 래퍼 대조군에서 바깥 SVG 트리에는 이미지 요소 하나만 있고 직접적인 경로는 없습니다. 그 트리에서 accent를 찾아도 아무것도 나오지 않습니다. 내장 이미지의 문서 안까지 접근하지 않기 때문입니다. MDN은 별도의 SVG 이미지 컨텍스트와 제한을 설명합니다. 선명한 렌더링만으로 편집 방식을 추정하지 마세요.
더 넓은 삽입 방식 선택은 인라인 SVG와 이미지 비교를 읽으세요. Fabric의 캔버스 객체 모델을 원한다면 Fabric.js 가져오기 안내가 다른 파서와 편집 API를 다룹니다.
이름이 있는 경로 하나를 편집하고 각 표현 저장하기
아래 완전한 코드를 빈 폴더에 starter.html로 저장하세요. 예를 들어 python3 -m http.server 8000으로 HTTP 서버를 실행한 뒤 http://localhost:8000/starter.html을 엽니다. 스크립트는 CDN에서 고정 버전 패키지를 로드하므로 네트워크 연결이 필요합니다.
페이지는 원본 그림, 실제로 편집한 사본, 이미지 래퍼 대조군을 표시합니다. 링크에서는 원본, 편집 전후 전체 루트, 자식만 있는 텍스트 조각, 새 루트로 감싼 사본, 이미지 래퍼를 다운로드합니다. 보고서는 캔버스에 그린 SVG 이미지에서 픽셀을 읽으며 PNG를 내보내지는 않습니다.
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Original SVG.js artwork import and export</title>
<style>
body { margin: 24px; font: 16px system-ui; background: #f6f2e8; }
main { display: grid; grid-template-columns: repeat(3, 260px); gap: 20px; }
figure { margin: 0; }
svg, img { display: block; background: white; border: 1px solid #bbb; }
pre { white-space: pre-wrap; font-size: 12px; }
</style>
<h1>Trusted artwork, one fill edit and two export forms</h1>
<main>
<figure><img id="original" alt="Original artwork"><figcaption>Original SVG image</figcaption></figure>
<figure><div id="live"></div><figcaption>Imported live SVG, accent edited</figcaption></figure>
<figure><div id="wrapper"></div><figcaption>Image wrapper, original artwork</figcaption></figure>
</main>
<p id="downloads"></p>
<pre id="report"></pre>
<script src="https://cdn.jsdelivr.net/npm/@svgdotjs/svg.js@3.2.6/dist/svg.min.js"></script>
<script>
const markup = '<svg xmlns="http://www.w3.org/2000/svg" id="original-artwork" width="240" height="160" viewBox="0 0 240 160">'
+ '<path id="body" d="M80 20 L140 80 L80 140 L20 80 Z M80 56 L104 80 L80 104 L56 80 Z" fill="#176b5b" fill-rule="evenodd"/>'
+ '<path id="accent" d="M172 48 L220 80 L172 112 Z" fill="#d4a53a"/></svg>';
const url = text => 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(text);
async function pixels(text) {
const image = new Image();
image.src = url(text);
await image.decode();
const canvas = document.createElement('canvas');
canvas.width = 240;
canvas.height = 160;
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
const rgba = (x, y) => [...ctx.getImageData(x, y, 1, 1).data];
return { naturalWidth: image.naturalWidth, naturalHeight: image.naturalHeight,
opening: rgba(80, 80), body: rgba(80, 40), accent: rgba(188, 80), corner: rgba(0, 0) };
}
function inventory(text) {
const node = new DOMParser().parseFromString(text, 'image/svg+xml').documentElement;
return { root: node.localName, viewBox: node.getAttribute('viewBox'),
paths: [...node.querySelectorAll('path')].map(p => ({ id: p.id, d: p.getAttribute('d'), fill: p.getAttribute('fill') })),
imageCount: node.querySelectorAll('image').length };
}
async function run() {
document.getElementById('original').src = url(markup);
const host = SVG().addTo('#live').size(240, 160).viewbox(0, 0, 240, 160);
host.svg(markup);
const artwork = host.findOne('#original-artwork');
const before = artwork.svg();
const accent = artwork.findOne('#accent');
const beforeD = accent.attr('d');
accent.fill('#b95434');
const full = artwork.svg();
const children = artwork.svg(false);
const rewrapped = SVG().size(240, 160).viewbox(0, 0, 240, 160);
rewrapped.svg(children);
const wrappedFull = rewrapped.svg();
const imageWrapper = SVG().addTo('#wrapper').size(240, 160).viewbox(0, 0, 240, 160);
imageWrapper.image(url(markup)).size(240, 160);
const wrapperFull = imageWrapper.svg();
const files = { 'master.svg': markup, 'before.svg': before, 'edited.svg': full,
'children-only.txt': children, 'rewrapped.svg': wrappedFull, 'image-wrapper.svg': wrapperFull };
for (const [name, text] of Object.entries(files)) {
const a = document.createElement('a');
a.href = name.endsWith('.txt') ? 'data:text/plain;charset=utf-8,' + encodeURIComponent(text) : url(text);
a.download = name;
a.textContent = name;
a.style.marginRight = '12px';
document.getElementById('downloads').append(a);
}
const results = {};
for (const [name, text] of Object.entries(files)) {
if (name.endsWith('.svg')) results[name] = { inventory: inventory(text), pixels: await pixels(text) };
}
document.getElementById('report').textContent = JSON.stringify({
requestedPackageVersion: '3.2.6',
scriptURL: document.querySelector('script[src]').src,
selectedAccentFound: Boolean(accent), selectedAccentDUnchanged: beforeD === accent.attr('d'),
allPathDUnchanged: JSON.stringify(inventory(before).paths.map(p => p.d)) === JSON.stringify(inventory(full).paths.map(p => p.d)),
childrenHasOuterSVG: /<svg[\s>]/i.test(children),
wrapperDirectPathCount: imageWrapper.find('path').length,
wrapperAccentFound: Boolean(imageWrapper.findOne('#accent')), results
}, null, 2);
}
run();
</script>
</html>
가져온 마크업에는 original-artwork라는 중첩 루트가 있습니다. 예제는 내보내기 전에 그 루트를 선택하므로, 전달 사본은 무관한 상위 컨테이너에 의존하지 않고 작성한 240 × 160 뷰포트를 가집니다. 그다음 해당 루트 안의 accent를 선택합니다. SVG.js는 선택자 기반 참조와 요소 메서드를 문서화합니다. 명시적인 부분 이름을 사용하면 경로 순서에 의존하지 않아도 됩니다.
이 코드는 의도적으로 자체 고정 마크업에만 한정됩니다. SVG 업로드 정화 도구가 아닙니다. 임의의 문자열을 파싱하거나 삽입한다고 안전함이 입증되지는 않습니다. MDN의 DOMParser 참고 문서는 파싱과 신뢰할 수 없는 콘텐츠 삽입의 위험을 구분합니다. 업로드에 이 루틴을 적용하기 전에 별도 검증·신뢰 정책을 설계하세요.
실시간 미리보기뿐 아니라 저장 결과 확인하기
실제 브라우저 실행에서 다음을 확인했습니다.
| 저장 표현 | 문서 트리의 경로 | 이미지 | 삼각형 채움 |
|---|---|---|---|
| 원본 | 2 | 0 | #d4a53a |
| 편집 전 전체 루트 | 2 | 0 | #d4a53a |
| 편집 후 전체 루트 | 2 | 0 | #b95434 |
| 새 전체 루트에 넣은 자식 | 2 | 0 | #b95434 |
| 이미지 래퍼 | 0 | 1 | 이미지 데이터 안의 원래 황토색 |
원래 두 경로의 d 문자열은 편집 전후 출력에서 동일하게 유지됐습니다. 삼각형 내부 표본은 RGBA (212, 165, 58, 255)에서 (185, 84, 52, 255)로 바뀌었습니다. 마름모 내부 표본은 (23, 107, 91, 255)를 유지했고, 구멍과 바깥 모서리는 알파 0이었습니다. 독립 SVG 파일 다섯 개 모두 이미지로 다시 열었을 때 고유 치수가 240 × 160이었습니다.

이 결과는 이 시험 파일에서 의도한 편집을 확인합니다. 바이트 단위로 같은 XML 보존, 모든 필터·CSS 지원, 제조 작업 적합성을 입증하지는 않습니다. 라이브러리의 직렬화는 이 특정 경로 문자열을 유지하면서도 문서 마크업을 바꿀 수 있습니다. 원본을 전달 사본과 함께 보관하세요.
자신의 그림에서는 실제 사용하는 구조를 확인하세요. 이름과 그룹, 클리핑, 그라디언트, 변환, 스트로크, 텍스트가 해당합니다. 내보낸 파일을 수신 애플리케이션에서 다시 열고 의도한 편집을 반복하세요. SVG 편집 가이드는 가져오기 코드를 바꾸기 전에 원본 구조를 진단하는 데 도움이 됩니다.
자식 전용 출력을 의도적인 조각으로 다루기
이번 실행에서 artwork.svg(false)는 바깥 svg 태그 없이 경로 두 개를 반환했습니다. 그림 루트의 뷰포트가 빠졌습니다. 별도의 rewrapped SVG 루트는 자식을 받기 전에 240 × 160 치수와 같은 viewBox를 제공합니다. 내보낸 파일에서는 확인한 산호색 삼각형이 렌더링됩니다.
대상에 따라 완전한 문서와 조각 중 하나를 고르세요. 독립 다운로드에는 의도한 전체 루트를 유지합니다. 기존 그림에 삽입한다면 자식이 어느 상위 좌표계, 상속 스타일, 정의를 사용할지 정하세요. 이 단순한 시험 파일에는 외부 리소스나 공유 정의가 없으므로, 참조가 있는 조각이 필요한 모든 의존성을 함께 전달하는지는 시험하지 않습니다.
이미지 래퍼 출력에는 디코딩 바이트가 원본과 일치하는 인코딩 SVG가 유지됐습니다. 이 대조군은 표현 방식의 차이를 보여 주며 래스터 변환이 아닙니다. 원본이 이미 사용 가능한 벡터 도형이라면 다시 트레이싱하기 전에 요소 선택이나 내보내기 경계를 고치세요.
원본에 편집할 경로가 없는 경우
평면 PNG나 JPG 일러스트에 개별적으로 색을 바꿀 영역이 필요하다면 PerfectVector로 SVG 후보를 준비하세요. 윤곽과 구멍을 확인한 다음 애플리케이션에 필요한 부분 이름을 작성합니다. 벡터화는 어느 영역의 이름이 accent여야 하는지 알지 못하며, 가져온 문서가 편집기 데이터 모델에 맞는다고 보장하지도 않습니다.
해당 도형이 이미 있다면 적절한 SVG 원본을 유지하세요. 벡터화 개요로 코드 앞단에 래스터-벡터 준비가 필요한지 판단할 수 있습니다.
자주 묻는 질문
SVG.js는 편집용 SVG 문자열을 가져올 수 있나요? 네. 이 시작 예제는 자체의 신뢰할 수 있는 마크업을 실제 SVG 컨테이너로 가져와 작성한 경로를 선택하고 채움을 바꿉니다. 임의 업로드에는 별도 신뢰·검증 정책이 필요하며 마크업 API는 정화 도구가 아닙니다.
그림은 보이는데 accent 경로를 찾을 수 없는 이유는 무엇인가요? 실제 마크업을 가져왔는지 이미지를 놓았는지 확인하세요. 이 이미지 래퍼 대조군은 SVG 그림을 표시하지만 래퍼 트리에서 직접 접근할 accent 경로는 없습니다. 선택한 상위 요소와 원본의 실제 ID도 확인하세요.
자식만 내보낸 결과가 완전한 SVG 파일인가요? 이 시험 파일에서는 아닙니다. 바깥 SVG 뷰포트 없이 경로 두 개만 포함합니다. 독립 파일에는 의도한 전체 루트를 쓰거나, 조각을 삽입할 때 대상 루트와 좌표계를 명시적으로 제공하세요.
편집에 성공하면 내보내기가 모든 SVG 기능을 보존한다고 볼 수 있나요? 아닙니다. 단순한 두 경로, 채움 값, 정확한 경로 데이터, 다시 연 파일 다섯 개를 확인했습니다. 그림에 필요한 특정 기능과 수신 애플리케이션을 시험하고 원본을 보관하세요.
출처
- SVG.js — 가져오기·내보내기, 버전 3.2 — 마크업 가져오기와 전체 루트·자식 전용 직렬화입니다.
- SVG.js — 요소 참조·만들기, 버전 3.2 — 이름이 있는 요소를 찾고 속성을 바꾸는 방법입니다.
- SVG.js — 릴리스 3.2.6 — 실행한 시작 예제에서 고정한 공식 릴리스입니다.
- MDN — 이미지로 사용하는 SVG — 이미지 삽입 컨텍스트와 제한입니다.
- MDN — DOMParser.parseFromString — 마크업 파싱과 신뢰할 수 없는 입력의 별도 처리입니다.
일러스트가 래스터 픽셀로만 있고 부분 편집이 필요하다면 자신의 그림으로 벡터 후보를 준비하세요. 윤곽과 구멍을 확인하고 원본을 보관한 뒤 수신 작업 과정에서 이름을 지정한 편집과 저장 SVG를 검증하세요.


