> [Site index](https://perfectvector.com/llms.txt) — available pages and descriptions.

# SVG feComposite: in, out, xor 결과 비교하기

> SVG 필터의 in, out, xor 입력을 비교하고 피연산자 순서와 부분 알파를 시험합니다. 시각적인 구멍이 편집 가능한 형상이 아닐 때 원본 경로를 보관하세요.

Source: https://perfectvector.com/ko/blog/svg-composite-artwork

Language: ko
Author: Irene Kim
Published: 2026-10-06T08:00:00+09:00
Updated: 2026-10-06T08:00:00+09:00

SVG의 `feComposite`는 렌더링된 두 입력을 합성합니다. 완전히 불투명한 입력에서 `in`은 겹친 부분 안에 첫 번째 입력을 남기고, `out`은 겹친 부분 밖에 남깁니다. `xor`는 두 입력이 공유하는 겹친 부분을 제외한 영역을 합칩니다. 이 연산은 렌더링 시점의 칠을 바꿉니다. 원본 경로를 편집 가능한 교집합이나 차집합으로 다시 쓰지는 않습니다.

겹친 부분이 구멍처럼 보이지만 전달 파일에는 실제 윤곽이 필요할 때 이 구분이 중요합니다. 아래 예제는 자체 제작한 비대칭 도형 두 개를 비교하고, 입력을 뒤집고, 부분 투명도를 확인합니다. 필터 결과와 함께 원본 아트워크를 보관하세요.

## 연산자를 고르기 전에 두 입력에 이름 붙이기

[MDN feComposite 참조 문서](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/feComposite)는 이미지 공간에서 픽셀별로 수행하는 연산으로 설명합니다. `in`은 첫 번째 입력, `in2`는 두 번째 입력의 이름을 지정합니다. 필터 체인이 바뀌었을 때 피연산자가 눈치채지 못한 사이 달라지지 않도록 중간 결과에 명시적인 이름을 붙이세요.

내부가 불투명하면 다음 세 연산을 쉽게 비교할 수 있습니다.

| 연산    | 첫 번째 입력 A, 두 번째 입력 B     | 입력을 뒤집으면 달라지는 점    |
| ----- | ------------------------ | ------------------ |
| `in`  | 겹친 부분의 A 칠               | 겹친 부분에 대신 B의 칠이 남음 |
| `out` | B 바깥의 A 칠                | A 바깥의 B 칠은 다른 영역임  |
| `xor` | 공유하는 불투명 겹침 영역을 제외한 두 입력 | 동일한 비겹침 영역이 남음     |

같은 `operator` 속성은 다른 필터 기본 요소에도 다른 값으로 사용됩니다. 합성이라는 맥락을 명확히 하세요. [MDN operator 참조 문서](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/operator)는 형태학 연산과의 차이를 설명합니다.

**보이는 칠 바꾸기**

![색상 아트워크가 겹친 모습과 겹친 부분이 칠해지지 않은 합성 결과를 비교하는 개념도](https://perfectvector.com/blog/svg-composite-artwork/overlap-and-paint.webp)

*개념도: 겹치는 입력으로 칠하지 않은 영역을 남길 수 있습니다. 장식적인 실루엣과 화살표는 개념을 나타냅니다. 아래의 정확한 연산자, 알파 표본, 변경되지 않은 경로는 직접 제작해 실행한 브라우저 테스트 파일에서 나온 결과입니다.*

## 두 번째 아트워크 명시적으로 정렬하기

첫 번째 피연산자 A는 청록색 계단형 블록입니다. B는 황토색의 비뚤어진 연 모양입니다. 둘 다 같은 `0 0 240 160` viewBox를 사용합니다. B는 A의 로컬 바운딩 박스를 벗어나므로 A의 범위를 필터 영역으로 가정하는 것은 좋은 출발점이 아닙니다.

필터는 `filterUnits="userSpaceOnUse"`를 사용하고 영역을 `x=0`, `y=0`, `width=240`, `height=160`으로 명시합니다. [MDN filterUnits 참조 문서](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/filterUnits)는 이 좌표 선택을 설명합니다. 또한 `primitiveUnits="userSpaceOnUse"`를 설정하고 두 번째 입력에도 같은 전체 프레임 좌표를 부여합니다.

B는 `feImage`를 통해 전달하는 독립적인 SVG 데이터 URL이며 결과 이름은 `operandB`입니다. 이는 B를 이미지 입력으로 필터에 넣는 방식입니다. B의 경로를 필터가 적용된 아트워크의 새로운 직접 자식 요소로 삽입하지는 않습니다. [MDN feImage 참조 문서](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/feImage)는 이미지 입력 동작을 설명합니다.

필터는 명시적으로 `sRGB`를 선택합니다. feComposite 참조 문서에 설명된 것처럼 이 필터 기본 요소의 기본값은 `linearRGB`입니다. 그 기본값이나 연산자 이름이 경로 편집 기능을 제공하지는 않습니다.

## 자체 제작한 전체 비교 실행하기

이 내용을 `starter.html`로 저장하고 폴더를 로컬 HTTP로 제공하세요. 예를 들어 `python3 -m http.server 8766 --bind 127.0.0.1`을 사용할 수 있습니다. `http://127.0.0.1:8766/starter.html`을 엽니다. 필요하면 다른 빈 포트를 사용하세요.

페이지는 명시한 두 크기마다 원본 두 개와 필터 사례 일곱 개를 만듭니다. 필터 ID마다 사례와 너비를 넣어 같은 문서 안의 여러 인라인 SVG가 고유한 참조를 갖게 합니다. 각 SVG 저장 링크는 독립적인 필터와 인코딩된 B 입력을 유지합니다.

```html
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Original SVG composite artwork</title>
<style>
  body { margin: 24px; font: 16px system-ui; background: #f6f2e8; }
  main { display: grid; grid-template-columns: repeat(3, 260px); gap: 18px; }
  figure { margin: 0; }
  svg { display: block; background: white; border: 1px solid #bbb; }
  figcaption { margin: 6px 0; }
  pre { white-space: pre-wrap; font-size: 12px; }
</style>
<h1>Original operands and rendered composites</h1>
<main id="cases"></main>
<pre id="report"></pre>
<script>
  const A = 'M24 24 H132 V56 H192 V136 H24 Z';
  const B = 'M92 8 L216 84 L92 152 L60 84 Z';
  const svgNS = 'http://www.w3.org/2000/svg';
  const cases = [
    { id: 'a' }, { id: 'b' },
    { id: 'a-in-b', operator: 'in' },
    { id: 'a-out-b', operator: 'out' },
    { id: 'a-xor-b', operator: 'xor' },
    { id: 'b-in-a', operator: 'in', reverse: true },
    { id: 'b-out-a', operator: 'out', reverse: true },
    { id: 'b-xor-a', operator: 'xor', reverse: true },
    { id: 'a-out-half-b', operator: 'out', opacity: 0.5 },
  ];
  function documentSVG(test, width) {
    const height = width * 2 / 3;
    const id = test.id + '-' + width;
    const second = '<svg xmlns="' + svgNS + '" width="240" height="160" viewBox="0 0 240 160">'
      + '<path d="' + B + '" fill="#d4a53a" opacity="' + (test.opacity ?? 1) + '"/></svg>';
    const href = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(second);
    const first = test.reverse ? 'operandB' : 'SourceGraphic';
    const other = test.reverse ? 'SourceGraphic' : 'operandB';
    const defs = test.operator ? '<defs><filter id="' + id + '" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" x="0" y="0" width="240" height="160" color-interpolation-filters="sRGB">'
      + '<feImage href="' + href + '" x="0" y="0" width="240" height="160" preserveAspectRatio="none" result="operandB"/>'
      + '<feComposite in="' + first + '" in2="' + other + '" operator="' + test.operator + '"/></filter></defs>' : '';
    return '<svg xmlns="' + svgNS + '" width="' + width + '" height="' + height + '" viewBox="0 0 240 160">'
      + defs + '<path d="' + (test.id === 'b' ? B : A) + '" fill="' + (test.id === 'b' ? '#d4a53a' : '#176b5b')
      + '"' + (test.operator ? ' filter="url(#' + id + ')"' : '') + '/></svg>';
  }
  async function run() {
    const results = [];
    for (const width of [120, 240]) {
      for (const test of cases) {
        const source = documentSVG(test, width);
        const svg = new DOMParser().parseFromString(source, 'image/svg+xml').documentElement;
        const figure = document.createElement('figure');
        const caption = document.createElement('figcaption');
        caption.textContent = test.id + ', ' + width + ' × ' + width * 2 / 3;
        figure.append(document.importNode(svg, true), caption);
        document.getElementById('cases').append(figure);
        const path = figure.querySelector('path');
        const bbox = path.getBBox();
        const image = new Image();
        image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(source);
        await image.decode();
        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = width * 2 / 3;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(image, 0, 0);
        const sample = (x, y) => [...ctx.getImageData(Math.floor(x * width / 240), Math.floor(y * width / 240), 1, 1).data];
        const hash = [...new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(path.getAttribute('d'))))]
          .map(v => v.toString(16).padStart(2, '0')).join('');
        const download = document.createElement('a');
        download.href = image.src;
        download.download = test.id + '-' + width + '.svg';
        download.textContent = 'Save SVG';
        figure.append(download);
        results.push({ id: test.id, width, height: canvas.height, d: path.getAttribute('d'), dSHA256: hash,
          bbox: { x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height },
          directPaths: svg.querySelectorAll('path').length, filterImages: svg.querySelectorAll('feImage').length,
          aOnly: sample(40, 40), bOnly: sample(200, 84), overlap: sample(120, 84), outside: sample(230, 150) });
      }
    }
    document.getElementById('report').textContent = JSON.stringify({ A, B, results }, null, 2);
  }
  run();
</script>
</html>
```

연산자의 입력을 뒤집어도 코드는 같은 직접 A 경로를 유지합니다. 예를 들어 `b-in-a`는 `feComposite`를 `in="operandB"`와 `in2="SourceGraphic"`로 바꿉니다. 필터가 적용되는 원본 요소를 B로 대체하지 않습니다. 따라서 A의 기본 형상을 바꾸지 않고 피연산자 순서를 눈으로 확인할 수 있습니다.

여기서 픽셀을 검사할 때는 생성한 각 SVG로 그린 캔버스를 읽습니다. 다운로드하는 전달 파일은 여전히 필터가 적용된 SVG입니다. 새 불리언 경로나 PerfectVector 변환 결과가 아닙니다.

## 칠과 형상을 별도로 비교하기

이 페이지를 그대로 한 Chrome 환경에서 `120 × 80`과 `240 × 160`으로 실행하고, SVG 18개를 모두 저장한 뒤 같은 브라우저에서 이미지로 다시 열었습니다. 표시 크기는 명시적인 입력이며 서로 다른 물리 기기 두 대에 대한 주장이 아닙니다.

내부 표본 위치 세 곳은 원래 좌표계를 사용합니다. A만 있는 곳은 `(40,40)`, B만 있는 곳은 `(200,84)`, 겹친 곳은 `(120,84)`입니다. 불투명한 피연산자에서는 두 크기 모두 다음 알파 값이 관찰되었습니다.

| 필터 입력과 연산자 | A만 있는 곳의 알파 | B만 있는 곳의 알파 | 겹친 곳의 알파 |
| ---------- | ----------- | ----------- | -------- |
| A `in` B   | 0           | 0           | 255      |
| A `out` B  | 255         | 0           | 0        |
| A `xor` B  | 255         | 255         | 0        |
| B `in` A   | 0           | 0           | 255      |
| B `out` A  | 0           | 255         | 0        |
| B `xor` A  | 255         | 255         | 0        |

`in` 행은 같은 불투명 겹침 영역을 보이지만 색상은 다릅니다. A가 먼저면 청록색 `(23,107,91,255)`, B가 먼저면 황토색 `(212,165,58,255)`이었습니다. 바깥쪽 표본 `(230,150)`은 계속 투명했습니다. 선택한 내부 픽셀의 결과이며 전체 가장자리 커버리지나 호환성 테스트가 아닙니다.

필터가 적용된 모든 파일은 A의 정확한 원본 `d` 문자열과 같은 SHA-256을 유지했습니다. 로컬 기하학적 `getBBox()`도 `x=24`, `y=24`, `width=168`, `height=112` 그대로였습니다. 이 박스는 원본 경로를 나타내며 보이는 필터 픽셀의 바운딩 박스가 아닙니다. 인코딩된 B 경로도 바뀌지 않았습니다. 따라서 직접 만든 두 윤곽을 그대로 유지하면서도 필터 결과는 다르게 보일 수 있습니다.

저장한 원본에는 직접 경로 하나가 있고 필터 이미지는 없습니다. 각 필터 파일에는 직접 A 경로 하나와 B의 별도 한 경로 SVG를 담은 `feImage` 하나가 있습니다. 일반적인 래스터 `image` 요소는 추가되지 않습니다. 렌더링 시의 이미지 공간 연산과 벡터 원본 피연산자는 함께 존재할 수 있습니다. 원본 요소 목록만으로 결과가 편집 가능한 결합 형상이 되었다고 볼 수는 없습니다.

## 부분 불투명도는 구멍의 의미를 바꿉니다

마지막 사례에서는 A를 불투명하게 유지하면서 B를 절반만 불투명하게 만든 다음 A `out` B를 적용합니다. 겹친 부분은 비지 않았습니다. 두 크기에서 관찰된 RGBA는 `(22,106,90,127)`이었습니다. 렌더링과 읽기 과정의 반올림이 포함된, 부분적으로 투명한 청록색입니다.

`out`의 출력 알파는 첫 번째 알파에 1에서 두 번째 알파를 뺀 값을 곱한 것입니다. `in`은 두 알파의 곱입니다. [Filter Effects 합성 정의](https://www.w3.org/TR/filter-effects-1/#feCompositeElement)는 사전 곱셈 색상과 알파로 이 연산들을 정의합니다. 불투명한 B는 A의 겹친 칠을 제거할 수 있지만, 부분적으로 투명한 B는 일부를 남깁니다.

실제 배경 위에서 결과를 확인하세요. 흰색 위의 옅은 영역은 완전히 투명한 구멍이 아니라 반투명한 칠일 수 있습니다. 안티앨리어싱된 가장자리 픽셀도 눈으로 확인해야 합니다. 내부 표본이 모든 경계 픽셀의 알파가 0이라고 보장하지는 않습니다. 불투명도를 재매핑하는 별도 작업은 [SVG 알파 임계값 가이드](https://perfectvector.com/ko/blog/svg-alpha-threshold)를 참고하세요.

## 필터 효과를 전달할지 실제 윤곽을 만들지 결정하기

받는 쪽 렌더러가 시각적 합성을 재현해야 한다면 이 필터를 유지하세요. 원본 A와 B 파일을 보관하고 필터 SVG를 실제 사용처에서 다시 열어 확인합니다. 브라우저에서 다시 열기에 성공했다고 다른 렌더러나 생산 장비까지 보장되지는 않습니다.

선택 가능한 결합 윤곽이나 커팅 준비가 된 구멍을 전달해야 한다면 예정된 작업 흐름에서 실제 경로 편집 또는 불리언 연산을 수행하고 확인하세요. 여기서 바뀐 시각적인 칠은 그 형상을 만들지 않습니다. [clipPath와 mask 비교 가이드](https://perfectvector.com/ko/blog/svg-clippath-vs-mask)는 관련된 가시성 차이를 설명합니다. [SVG 그룹 해제 가이드](https://perfectvector.com/ko/blog/svg-wont-ungroup)는 화면의 아트워크가 예상한 편집 가능 객체가 아닐 때 도움이 됩니다.

클립아트가 픽셀로만 존재하고 편집 가능한 원본 도형이 필요하다면 [PerfectVector로 클립아트 SVG를 준비](https://perfectvector.com/ko/clipart-to-svg)하고 윤곽과 구멍을 확인한 뒤 마스터를 보관하세요. 원하는 필터나 경로 연산은 별도로 적용하고 검증합니다. 벡터 변환이 이 필터의 시각적인 겹침을 검증된 제조용 윤곽으로 바꾸지는 않습니다.

## 자주 묻는 질문

**feComposite out은 겹친 경로 형상을 삭제하나요?**
아닙니다. 두 번째 입력의 알파를 사용해 렌더링된 칠을 바꿉니다. 이 필터 파일에서는 겹친 칠이 투명해진 곳에서도 원본 A의 경로 데이터와 로컬 형상 범위가 바뀌지 않았습니다.

**in과 in2를 바꾸면 결과가 달라지는 이유는 무엇인가요?**
첫 번째와 두 번째 입력을 지정하기 때문입니다. In은 두 번째 입력의 불투명도 안에 첫 번째 입력의 칠을 남기고, out은 그 바깥에 첫 번째 입력을 남깁니다. 뒤집은 in 사례는 같은 불투명 겹침 영역을 보이면서도 색상이 다르고, 뒤집은 out 사례는 서로 다른 영역을 남깁니다.

**xor는 항상 겹친 부분을 완전히 비우나요?**
완전히 불투명한 내부 겹침은 이 xor 사례에서 비어 있었습니다. 부분 알파와 안티앨리어싱된 가장자리는 별도로 확인해야 합니다. 불투명 도형의 집합 도식은 모든 반투명 픽셀을 설명하지 않습니다.

**필터로 만든 구멍을 편집 가능한 커팅 경로로 바로 전달해도 되나요?**
시각적인 필터 결과는 새로 만든 불리언 형상이 아닙니다. 원본 도형을 보관하고, 예정된 경로 작업에서 필요한 윤곽을 만들고 확인한 다음, 받는 애플리케이션과 생산 요구 사항을 별도로 검증하세요.

## 출처

1. [MDN: feComposite](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/feComposite) — 이미지 공간 입력의 역할, 연산자 설명, 기본 필터 색 공간.
2. [MDN: operator](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/operator) — 합성 연산자 값과 별도의 형태학 맥락.
3. [MDN: filterUnits](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/filterUnits) — 필터 영역 좌표계.
4. [MDN: feImage](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/feImage) — 필터 파이프라인으로 전달하는 이미지 입력.
5. [W3C: Filter Effects 합성](https://www.w3.org/TR/filter-effects-1/#feCompositeElement) — 사전 곱셈 색상과 알파의 합성 정의.

***

자신의 래스터 클립아트에서 시작하나요? [편집 가능한 SVG 마스터를 만들고](https://perfectvector.com/ko/clipart-to-svg) 윤곽과 구멍을 확인하세요. 그 마스터를 보관하면서 선택한 시각적 합성이나 별도로 만든 경로 결과를 실제 사용처에서 확인합니다.

## Explore

- [Read the HTML article](https://perfectvector.com/ko/blog/svg-composite-artwork)
- [숨은 경로 없이 SVG를 영구적으로 자르기](https://perfectvector.com/ko/blog/permanent-crop-svg.md)
- [Inkscape Object to Path와 Stroke to Path의 차이](https://perfectvector.com/ko/blog/inkscape-stroke-vs-object-to-path.md)
- [Product facts](https://perfectvector.com/product-facts.md)
- [FAQ](https://perfectvector.com/faq.md)
- [Pricing and credits](https://perfectvector.com/pricing.md)
