안녕하세요! 프론트엔드 개발 강사입니다. 이번엔 웹 페이지를 마치 포토샵처럼 화려하게 꾸며줄 수 있는 CSS 필터 효과(Filter Effects)에 대한 문서를 가져오셨네요!
과거에는 이미지를 흐리게(blur) 만들거나 흑백(grayscale)으로 바꾸려면 디자이너에게 부탁해서 포토샵으로 일일이 이미지를 수정한 뒤, 여러 장의 이미지를 따로따로 불러와야(HTTP 요청) 했죠. 하지만 이제는 CSS 단 한 줄만으로 이런 멋진 시각 효과들을 실시간으로 만들어낼 수 있게 되었습니다. 이번에도 딱딱한 설명 대신 실무에서 어떻게 유용하게 쓰이는지 꿀팁과 함께 구어체로 전부 번역해 드릴게요! ✨
흑백이나 세피아(sepia) 톤의 이미지에 마우스를 올렸을 때(hover) 즉시 풀 컬러 이미지로 바뀌는 걸 본 적 있으신가요? 혹은 배경 이미지의 일부분이 살짝 흐려져(blur) 있어서 그 위에 겹쳐진 글씨가 훨씬 잘 보이게 디자인된 것을 본 적은요? 과거에는 이런 효과를 내려면 이미지 편집 소프트웨어를 다룰 줄 알아야 했고, 편집할 시간도 필요했으며, 무엇보다 각각의 상태(원본/수정본)에 맞는 이미지를 따로 다운로드해야 해서 불필요한 HTTP 요청이 늘어나는 문제가 있었습니다.
CSS의 필터 효과(Filter effects) 모듈은 앞서 설명한 시각적 효과들을 포토샵을 켜거나 추가적인 이미지 다운로드(HTTP 요청) 없이 바로 적용할 수 있는 속성(properties)과 함수(functions)들을 제공합니다. 여러분에게 필요한 유일한 소프트웨어는 오직 사용자의 웹 브라우저뿐입니다. 더 나아가, 미리 만들어진 고정된(pre-set) 이미지 파일들과는 다르게, CSS 필터 효과는 반응형(responsive)으로 동작할 뿐만 아니라 애니메이션(animatable) 효과까지 줄 수 있습니다!
CSS 필터 효과 모듈은 텍스트, 이미지, 배경, 테두리 등 이 속성이 적용된 모든 요소의 렌더링에 영향을 줄 수 있는 filter 속성과 backdrop-filter 속성을 제공합니다. 이 모듈은 또한 흐림(blur) 효과나 색상 변환(color shifting) 같은 그래픽 효과를 추가할 수 있게 해주는 <filter-function>이라는 데이터 타입도 정의하고 있습니다. 이러한 필터 함수들을 사용하면 요소의 겉모습을 바꾸는 것뿐만 아니라, 여러분이 직접 만든 SVG 필터를 참조해서 완전히 새로운 효과를 만들어낼 수도 있습니다.
💡 강사의 실무 팁! "디자이너 괴롭히기 멈춰!"
실무에서 "마우스 올렸을 때 이미지가 살짝 어두워지게 해주세요", "버튼 누르면 이미지가 흐려지게 해주세요" 같은 요청을 받을 때마다 디자이너에게 이미지를 여러 장 뽑아달라고 하면 서로 피곤해지겠죠? 이제는 당당하게 "제가 CSS로 처리할게요!"라고 말씀하시면 됩니다. 파일 용량도 아끼고, 애니메이션도 훨씬 자연스럽게 넣을 수 있으니까요!
CSS 필터 효과 모듈의 다음 두 가지 속성을 사용하면 요소에 아무 효과도 주지 않거나(zero), 하나 또는 여러 개의 그래픽 효과를 동시에 적용할 수 있습니다:
filter 속성을 사용하면, 요소가 화면에 그려지기 직전에 블러(blur, 흐림), 드롭 섀도우(drop-shadow, 그림자), 세피아(sepia, 빛바랜 느낌) 같은 필터 효과를 적용할 수 있습니다. 이 필터 효과는 해당 요소의 콘텐츠, 테두리(borders), 패딩(padding) 영역을 모두 포함하여 요소 자체에 직접적으로 적용됩니다.backdrop-filter 속성을 사용하면, 요소의 뒤쪽 영역(요소의 "배경, backdrop")에 그래픽 효과를 적용할 수 있습니다. 이 backdrop-filter 속성은 주로 전경(foreground)에 있는 텍스트나 콘텐츠를 더 잘 읽히게(legible) 만들고 싶을 때 자주 사용됩니다. 콘텐츠가 놓인 넓은 배경 영역이 대비(contrast)가 부족해서 글씨가 잘 안 보일 때 특히 유용하죠. 이 필터 효과는 오직 요소의 '배경(뒤쪽 화면)'에만 적용되며, 요소 본인의 콘텐츠(글씨 등)에는 영향을 주지 않습니다. (아이폰 제어센터나 맥OS의 반투명한 유리창 효과(Glassmorphism)를 생각하시면 이해가 빠릅니다!)filter와 backdrop-filter 속성 모두 필터 함수들을 공백으로 구분한 목록(space-separated list) 형태로 여러 개 겹쳐서 받을 수 있으며, 선언된 순서대로 차례차례 효과가 적용됩니다.
CSS 필터 효과 모듈은 10개의 <filter-function> 함수들을 제공하며, 이에 더해 url() 참조를 통해 SVG 필터를 적용하여 거의 무한대에 가까운 다양한 효과를 만들어낼 수 있는 능력도 제공합니다.
다음 표는 10가지 필터 함수들의 목록과 함께, 각 함수가 어떤 값(타입)을 받는지, 허용되는 최소값(min value)은 무엇인지, 최대 효과(max effect)를 내는 값은 무엇인지, 그리고 보간(interpolation, 애니메이션 시 중간값 계산) 시 기준이 되는 기본값(default value)을 보여줍니다.
| 필터 함수 (Filter function) | 매개변수 타입 (Parameter type) | 최소값 (Min value) | 최대 효과 (Max effect) | 기본값 (Default value, 아무 효과 없음) |
|---|---|---|---|---|
blur() (흐리게) | <length> (픽셀 등 길이) | 0 | - | 0 |
brightness() (밝기) | <number> 또는 <percentage> | 0 | - | 1 또는 100% |
contrast() (대비) | <length> (보통 %나 숫자로 씀) | 0 | - | 1 또는 100% |
drop-shadow() (그림자) | <shadow> (x, y, blur, 색상) | - | - | 0 0 0 currentColor |
grayscale() (흑백) | <number> 또는 <percentage> | 0 | 100% | 0 또는 0% |
hue-rotate() (색조 회전) | <angle> (각도, 예: 90deg) | - | - | 0deg |
invert() (색상 반전) | <number> 또는 <percentage> | 0 | 100% | 0 또는 0% |
opacity() (불투명도) | <number> 또는 <percentage> | 0 | 100% | 1 또는 100% |
saturate() (채도) | <number> 또는 <percentage> | 0 | 100% | 100% |
sepia() (세피아 톤) | <number> 또는 <percentage> | 0 | 100% | 0% |
최소값(Min value)이 정해져 있는 필터 함수의 경우 그보다 작은 값을 넣으면 안 됩니다. 쉼표(콤마)로 여러 필터를 연결해 쓸 때, 단 하나의 함수라도 최소값보다 작은 잘못된 값을 사용하게 되면 해당 함수뿐만 아니라 전체 filter 속성 선언 자체가 무효화(invalidates)되어 버립니다.
최대 효과(Max effect) 값을 초과해서 적어 넣는 것은 문법적으로는 유효(valid)합니다. 하지만 표에 나와 있는 최대값을 넘겨서 적는다고 해서 효과가 그 이상으로 더 강력해지지는 않습니다. 즉, 요소에 나타나는 시각적 효과는 최대 효과 값을 설정했을 때와 완전히 똑같이 보이게 됩니다. 예를 들어, 아래의 세피아 예제에서 sepia(400%)라고 설정하더라도 최대값인 sepia(100%)로 설정한 것과 완벽히 똑같은 효과를 만들어냅니다.
기본값(Default value)은 화면에 아무런 시각적 효과도 주지 않는(no effect) 상태의 값입니다. 비록 당장 효과는 없지만, 이 값들은 애니메이션(트랜지션)이 시작될 때의 초기값을 제공해주고 속성값을 어떻게 적어야 하는지 힌트를 제공합니다. 이 기본값들은 허용되는 최소값과 최대 효과 값 사이를 오갈 때 일종의 기준점(gauge) 역할을 합니다.
filter와 backdrop-filter 속성은 하나 이상의 <filter-function>들을 담고 있는 리스트를 받을 수도 있고, 기본 상태인 none 키워드를 받을 수도 있으며, url() 값을 통해 SVG 필터를 가져와 적용할 수도 있습니다.
아래의 세피아 톤 이미지 위에 마우스를 올려보세요(hover). 즉시 원본 풀 컬러 이미지로 바뀌는 것을 볼 수 있습니다.
이미지에 세피아 효과를 주기 위해 filter 속성에 sepia() 필터 함수를 값으로 지정했습니다. 그리고 마우스가 올라갔을 때(:hover)와 키보드로 포커스 되었을 때(:focus)에는 filter: none을 설정하여 필터 효과를 제거했습니다.
(MDN Playground에서 실행해보기 (Play))
<img tabindex="0" alt="Four trans-people, circa 1912" src="activists.jpg" />
img {
filter: sepia(100%);
}
img:hover,
img:focus {
filter: none; /* 마우스가 올라가면 필터 제거! */
}
img {
max-width: 100%;
height: 100%;
}
📝 참고:
<img>태그에tabindex값을0으로 준 이유는,<img>자체는 본래 키보드의 Tab 키로 접근할 수 있는 상호작용(interactive) 요소가 아니기 때문입니다. 이렇게 하면 키보드 사용자들의 탭 순서를 망치지 않으면서도 해당 이미지에 포커스(:focus)를 줄 수 있게 됩니다. 접근성(A11y)을 고려한 아주 좋은 코드죠!
보통 filter 속성은 이미지(img)에 가장 많이 쓰인다고 생각하시겠지만, filter와 backdrop-filter 속성은 이미지뿐만 아니라 텍스트, 박스 등 HTML의 그 어떤 요소나 가상 요소(pseudo-element)에도 동일하게 적용될 수 있습니다.
이 예제에서는 3px의 블러(흐림)와 0의 오프셋(이동 거리)을 가진 drop-shadow() 필터를 사용해서 제목(h1) 텍스트 주변에 마치 네온사인처럼 예쁘게 빛나는(glow) 효과를 추가해 보았습니다.
👨🏫 강사의 실무 팁! "box-shadow와 drop-shadow의 차이점"
박스에 그림자를 줄 때box-shadow를 써야 할까요, 아니면filter: drop-shadow()를 써야 할까요?
box-shadow는 네모난 요소의 '외곽선'을 따라 그림자를 그립니다. 반면drop-shadow는 배경이 투명한 PNG 이미지나 불규칙한 모양의 텍스트 같은 것들의 '실제 형태(알파 채널)'를 인식해서 그 모양 그대로 그림자를 똑같이 복사해서 그려줍니다! 그래서 글씨나 투명한 이미지 뒤에 진짜 같은 그림자를 주고 싶다면 무조건drop-shadow를 써야 합니다.
(MDN Playground에서 실행해보기 (Play))
h1 {
color: midnightblue;
filter: drop-shadow(0 0 3px magenta); /* 글씨 모양 그대로 마젠타색 그림자가 생깁니다 */
}
h1 {
font-family: sans-serif;
font-size: 2rem;
}
<h1>Glow created with CSS filter</h1>
위의 세피아 예제에서는 필터 함수를 하나만 달랑 썼지만, 원한다면 여러 개의 필터를 동시에 설정할 수도 있습니다. filter와 backdrop-filter 속성에 필터들을 스페이스바(공백)로 띄워서 쭉 적어주면 되는데요, 이때 필터 효과들은 여러분이 적어둔 순서대로 차례차례 적용됩니다. 순서가 매우 중요하죠!
이 예제에서는 backdrop-filter 속성을 통해 hue-rotate()(색조 회전)와 blur()(흐리게) 두 개의 필터를 적용했습니다. <p> 태그 뒤에 있는 배경(backdrop) 영역의 색상이 확 돌아가고(color shift) 흐려지게(blur) 됩니다.
(MDN Playground에서 실행해보기 (Play))
.container {
background: url("/shared-assets/images/examples/listen_to_black_women.jpg")
no-repeat left / contain goldenrod;
}
p {
backdrop-filter: hue-rotate(240deg) blur(5px); /* 배경색을 확 돌려버리고 5px만큼 흐리게 만듭니다 */
background-color: rgb(255 255 255 / 10%); /* 살짝 반투명한 흰색 배경을 얹습니다 */
text-shadow: 2px 2px black;
}
.container {
padding: 3rem;
width: 30rem;
}
p {
padding: 0.5rem;
color: white;
font-size: 2rem;
font-family: sans-serif;
}
<div class="container">
<p>
Text on images can be illegible and inaccessible even with a drop shadow.
</p>
</div>
필터들은 순서대로 차곡차곡 적용되기 때문에, 같은 종류의 필터 함수를 여러 번 반복해서 쓸 수도 있습니다. 이 예제에서는 drop-shadow() 필터를 무려 네 번이나 썼는데, 매번 <shadow> 값을 다르게(색깔과 방향을 다르게) 줘서 십자가 모양의 화려한 4방향 그림자를 만들었습니다.
(MDN Playground에서 실행해보기 (Play))
<img src="mandala.svg" alt="Colorful mandala" role="img" />
<img src="mandala.svg" alt="Plain mandala" role="img" />
img {
width: 49%;
}
img {
filter: drop-shadow(2px 2px 0 magenta) drop-shadow(-2px -2px 0 royalblue)
drop-shadow(2px 2px 0 lime) drop-shadow(-2px -2px 0 darkorange);
}
img + img {
filter: none; /* 두 번째 이미지는 비교를 위해 필터를 껐습니다 */
}
첫 번째 만달라(Mandala) 예제에서는, 선으로만 그려진 흑백 SVG 이미지에 4개의 화려한 드롭 섀도우를 겹쳐서 적용했습니다. 바로 옆에는 효과가 어떻게 들어갔는지 비교할 수 있도록 filter: none을 줘서 원본 SVG를 그대로 보여주고 있습니다.
필터 효과를 여러 개 만들 때, filter나 backdrop-filter 속성에는 공백으로 구분된 필터 리스트를 적어주게 됩니다. 그리고 이 필터 효과들은 여러분이 코드를 적어놓은 순서 그대로 하나씩 차례대로 적용됩니다. 이 예제에서는 첫 번째 줄(h1) 텍스트에 magenta(자홍색) 드롭 섀도우와 180deg의 색조 회전(hue rotation) 두 가지를 모두 적용할 것입니다. 이 두 필터의 적용 순서를 바꿨을 때 결과물이 어떻게 확 달라지는지 보여드릴게요.
(MDN Playground에서 실행해보기 (Play))
h1 {
color: midnightblue;
}
#hueFirst {
filter: hue-rotate(180deg) drop-shadow(3px 3px magenta);
}
#shadowFirst {
filter: drop-shadow(3px 3px magenta) hue-rotate(180deg);
}
h1 {
font-family: sans-serif;
font-size: 2rem;
}
<h1 id="hueFirst">Hue change happens before drop shadow.</h1>
<h1 id="shadowFirst">Drop shadow applied before hue change.</h1>
<h1>No filter effects applied.</h1>
보셨나요? 위아래 텍스트에 똑같은 2개의 필터를 썼지만 순서만 다릅니다.
#hueFirst)은 먼저 원래 글씨 색(midnightblue)의 색조(hue)를 확 돌려버린 다음에 자홍색(magenta) 그림자를 적용했습니다. 그래서 그림자 색은 변함없이 magenta 그대로 칠해집니다.#shadowFirst)은 먼저 짙은 파란색(midnightblue) 글씨 뒤에 자홍색(magenta) 그림자를 추가했습니다. 그러고 나서 색조 회전(hue-rotate) 필터가 적용되니, 글씨의 색상뿐만 아니라 이미 그려진 그림자의 색상(magenta)까지 같이 돌아가버리게 됩니다!그래서 비교를 위해 아무 효과도 주지 않은 세 번째 줄의 원본 색상(midnightblue 또는 #191970)과 비교해보면, hue-rotate(180deg) 필터가 적용된 앞의 두 줄의 글씨 색상은 모두 #252500라는 칙칙한 색으로 변해버린 것을 확인할 수 있죠.
📝 참고:
midnightblue의 16진수 색상 코드인#191970은 HSL로 변환하면hsl(240deg 63.5% 26.9%)와 같습니다. 반면 변화된 색인#252500은hsl(60deg 100% 7.3%)입니다. 색상 회전(color rotation)은 sRGB 색 공간(color space)에서 이루어지기 때문에, 우리가 기대한 대로 색조(hue)만 정확히 180도 돌아가고(240 -> 60), 원래의 채도(saturation)나 밝기(lightness) 값이 동일하게 유지되지는 않는다는 점을 유의하세요.
CSS 필터 효과는 우리가 방금 살펴본 10가지 미리 정의된 <filter-function>들 외에도, url() 함수를 사용해서 완전히 커스텀된 필터를 불러올 수 있습니다. 이때 매개변수(parameter)로는 SVG 필터를 넘겨주게 되는데, 이 SVG 코드는 HTML 문서 안에 직접 박아넣거나(internal) 외부 SVG 파일(external)로 빼서 불러올 수도 있습니다.
단 하나의 SVG 코드 블록(svg 태그) 안에 여러 개의 다양한 필터들을 정의해 두고, 각각에 고유한 id를 부여해서 꺼내 쓸 수 있습니다:
<svg role="none">
<defs>
<filter id="blur1">
<feGaussianBlur stdDeviation="1" edgeMode="duplicate" />
</filter>
<filter id="blur3">
<feGaussianBlur stdDeviation="3" edgeMode="duplicate" />
</filter>
<filter id="hue-rotate90">
<feColorMatrix type="hueRotate" values="90" />
</filter>
</defs>
</svg>
이제 이 필터의 id를 CSS나 SVG의 url() 함수 안에서 참조해서 사용하면 됩니다. 내부에 정의된 필터를 쓸 수도 있고 외부 서버에 있는 걸 불러올 수도 있죠:
filter: url("#blur3"); /* HTML 내부의 SVG 필터 사용 */
filter: url("[https://example.com/svg/filters.svg#blur3](https://example.com/svg/filters.svg#blur3)"); /* 외부 URL에서 필터 가져오기 */
CSS의 blur() 필터 함수가 적용된 요소에 가우시안 블러(Gaussian blur, 뽀샤시한 흐림 효과)를 입히는 것과 완벽히 똑같이, SVG의 <feGaussianBlur> 필터 요소를 사용해서도 동일하게 콘텐츠를 흐리게 만들 수 있습니다.
이 두 가지 방법 모두, 흐려지는 정도(blur radius)를 정할 때 CSS에서는 <length>(px 등 길이 값)로, SVG에서는 픽셀에 상응하는 <number>(숫자)로 값을 줍니다. 이 값은 수학적인 가우스 함수(Gaussian function)의 '표준 편차(standard deviation)' 값을 결정합니다. 쉽게 말해, 화면의 몇 픽셀짜리 반경을 서로 뭉뚱그려 섞을(blend) 것인지를 정하는 거예요. 이 값이 커질수록 이미지는 훨씬 더 심하게 흐려집니다.
SVG의 <filter> 태그 안에서 사용하는 stdDeviation 속성은 x축, y축 두 개의 값을 받을 수 있어서 가로로만 흐리게 하거나 세로로만 흐리게 하는 등 훨씬 복잡하고 정교한 블러 효과를 만들 수 있습니다. 하지만 단순한 흐림 효과를 원한다면 stdDeviation에 하나의 값만 넣어주면 됩니다:
(MDN Playground에서 실행해보기 (Play))
<table>
<thead>
<tr>
<th>CSS example</th>
<th>SVG example</th>
<th>Original image</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<img
class="filter"
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</td>
<td>
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag"
class="svgFilter" />
</td>
<td>
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</td>
</tr>
</tbody>
</table>
<svg role="img" aria-label="Flag">
<filter id="blur">
<feGaussianBlur stdDeviation="3.5" edgeMode="duplicate" />
</filter>
<image
xlink:href="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
filter="url(#blur)" />
</svg>
이렇게 만든 SVG url() 필터 값은 SVG <image> 요소의 filter 속성 값으로 직접 집어넣을 수도 있고, 일반적인 HTML 요소의 CSS filter 나 backdrop-filter 속성 값으로 불러와서 적용할 수도 있습니다.
th,
td {
padding: 5px;
}
.filter {
filter: blur(3.5px); /* CSS 기본 함수를 이용한 블러 */
}
.svgFilter {
filter: url("#blur"); /* SVG로 만든 커스텀 블러 필터 적용! */
}
maskbackground-blend-mode, mix-blend-mode<filter> 요소와 filter 속성이 페이지가 도움이 되셨나요? [네 (Yes)] / [아니요 (No)]
기여하는 방법 알아보기 (Learn how to contribute)
이 페이지는 2025년 12월 16일에 MDN 기여자들 (MDN contributors)에 의해 마지막으로 수정되었습니다.