본문 바로가기
개발/개발지식

CSS blur 한 줄 뒤에 숨은 가우시안 블러의 세계

by 핸디(Handy) 2025. 8. 19.

들어가며

유튜브 숏츠를 보다가 가우시안 블러(Gaussian Blur)에 대한 영상을 보게 되었습니다.

그리고 갑자기 궁금해졌죠. CSS의 blur는 어떻게 되는가?

이번 글에서는 가우시안 블러와 CSS의 blur의 관계와 이를 비교해보는 글을 작성해보겠습니다.

그럼 시작.

블러라고 다 같은 블러가 아니다.

일반적으로 디자인 작업을 하다보면 자주 시각적 효과가 있습니다. 바로 blur죠. 흐름효과라고도 하는데요.

이 용어는 개발뿐만 아니라 디자인 영역에서도 자주 사용되는 용어인듯 합니다. 그리고 이는 CSS로 아주 간단히 구현이 가능합니다.

.my-image {
  filter: blur(8px);
}

그런데 이 blur가 단순히 '대충 흐려지는 것'은 아닙니다. 사실 내부적으로는 가우시안 블러(Gaussian Blur)라는 꽤 정교한 수학적 연산을 기반으로 하고 있습니다.

그리고 이 Blur는 크게 2가지 방식이 유명합니다.

Box Blur = 거친 브러시

박스 블러(Box Blur)는 가장 단순한 방법입니다. 픽셀 주변 일정 영역을 잡고, 그 영역 내 모든 픽셀 값을 단순히 평균 내서 새로운 픽셀로 만드는 방식이죠.

장점은 계산이 단순하다는 것. 하지만 단점은 경계가 뭉툭하고, 결과물이 조금 투박하게 흐려진 느낌을 준다는 것입니다.

Gaussian Blur = 부드러운 브러시

가우시안 브러는 조금 더 고급스러운 방법입니다. 픽셀 주변을 다 똑같이 평균하는 대신, 가까운 픽셀일수록 더 큰 가중치를, 먼 픽셀일수록 더 작은 가중치를 주어 계산합니다. 이때 사용되는 수학적 분포가 바로 "가우스 곡선"이이서서 가우시안 블러라고 부릅니다.

웹 브라우저에서 제공하는 CSS blur() 함수가 내부적으로 가우시안 블러를 사용합니다.

즉, 우리가 blur(5px)이라고 쓰면, 브라우저는 해당 요소를 비트맵으로 변환한 뒤 가우시안 커널을 적용해 픽셀을 흐리게 만듭니다. 5px이라는 값은 사실상 가우시안 분포의 표준 편차와 연결된다고 합니다.

.blur-demo {
  filter: blur(12px);
}

이 코드는 요소를 중심으로 약 12px 반경의 가우시안 블러를 적용합니다. 값이 커질수록 더 크게 퍼져 보이죠.

 

이거 보시면 정말 1분안에 이해가 가능합니다. 이제 이 이해를 바탕으로 실제 코드로 어떻게 구현되는지 한번 살펴보러 가시죠.

스펙 확인

스펙 문서를 확인해보고 정리해보면 다음과 같습니다.

blur(<length>)가 호출되면, 스펙상으로는 SVG의 feGaussianBlur와 stdDeviation 속성에 매핍됩니다. 이때 edgeMode는 duplicate로 고정되고, 가장자리에 black halo가 생기지 않고 원본 픽섹을 복제하여 자연스러운 블러 결과를 만들게 됩니다.

 

 

Filter Effects Module Level 1

 

www.w3.org

실제 코드를 보기전에 black halo가 어떤것인지 한번 보시죠.

black halo

https://community.adobe.com/t5/photoshop-ecosystem-discussions/problem-with-halo-in-gaussian-blur/m-p/9162554

이미지를 보게 되면 실제 사람의 윤곽선 너머로 흐릿한 영역이 있는 것을 보실수 있습니다. 이것이 바로 가우시안 블러 자체의 특징 때문에 피사체 가장 자리 주변의 픽셀이 배경 색상과 섞이는 현상입니다.

그리고 이 부분이 후광처럼 보인다고 하여 halo라고 합니다. 그리고 black halo라고 이름붙여진 이유는 피사체의 피부톤이나 밝은 색계열일 떄, 배경 블러 과정에서 경계 주변 픽셀이 상대적으로 어두워지면더 눈에 잘 띄게 되는데 일반적으로 halo보다 '검은 윤곽'이 강조되어 보이게 되고 이를 따로 black halo라고 불렀다고 합니다.

Fast Gaussian Blur = Multiple Box Blur

앞서 이야기했듯, 가우시안 블러는 아름답지만 계산 비용이 만만치 않습니다. 브라우저는 GPU를 활용해 이 연산을 빠르게 실행하긴 하지만, 어디까지나 '빠르게 돌리는 것'일 뿐, 연산량 자체를 줄여주는 것은 아닙니다.

그렇다면 우리의 개발자 형님들은 어떤 방법을 사용했을까요? 바로 빠른것을 여러번 돌리는 식의 최적화를 사용합니다.

여러 번의 박스 블러를 연속 적용하여 가우시안 블러를 근사하는 최적화를 사용한다

수학의 중심극한정리에 따르면(많은 분들이 배웠지만 기억나지 않죠?), 독립적인 확률 분포를 여러 번 평균하면 그 결과가 정규분포에 가까워집니다. 그리고 이 정규분포가 바로 가우시안이죠.

그래서 이 아이디어를 그래픽에 적용하면 다음과 같은 사실이 나옵니다.

  • 한 번의 박스 블러 - 단순한 직사각형 분포 (투박함)
  • 여러 번의 박스 블러 - 점점 가우시안 곡선에 가까워짐 (부드러움)

즉 박스블러를 3~4회 연속 적용하면 가우시안 블러와 거의 동일한 효과를 얻을 수 있습니다. 이 방식은 연산량 자체가 가우시안 블러에 비해 적다고 할순 없지만, 가우디안의 복잡한 곱셈 대신에 덧셈/평균만 반복하면 되므로 훨씬 빠릅니다. 그래서 실제로 많은 그래픽 엔진에서 이 최적화를 사용합니다.

그리고 이 최적화 기법을 "Fast Gaussian Blur" 혹은 "Stack Blur"라고 부릅니다.

 

코드로 보는 블러

이제 실제 블러 코드를 작성해보도록 하겠습니다. 여기에선 3가지 방법으로 살펴보죠.

  • Box Blur
  • Gaussian Blur
  • Css Blur

그리고 블러 효과를 확인해보기 위해 SVG 컴포넌트를 만들고 그 위에 필터나 CSS를 통해 결과를 확인해보도록 하겠습니다.

이제 여기에 필터를 입혀봅시다.

Box Blur 

feConvolveMatrix를 사용하여 여러번 반복할수 있는 블러를 만듭니다.

<filter
  id="boxBlur"
  filterUnits="objectBoundingBox"
  x="-20%"
  y="-20%"
  width="140%"
  height="140%"
>
  {Array.from({ length: boxPasses }).map((_, i) => (
    <feConvolveMatrix
      key={i}
      order={`${boxOrder} ${boxOrder}`}
      kernelMatrix={kernelMatrix}
      edgeMode="duplicate"
      divisor={divisor}
    />
  ))}
</filter>;

여러번 블러를 적용하기 위해 boxPasses를 통해 반복문으로 feConvolveMatrix를 중첩하는 코드입니다.

Gaussian Blur

가우시안블러는 간단합니다. feGaussianBlur를 통해서 구현하면 되죠. 그리고 stdDeviation이 표준편차입니다. 일반적으로 css의 blur(<length>)에 해당하는 값에 gaussPx에 넣는다고 생각하면 됩니다.(실제 구현은 아주 동일한 값은 아니라고도 하는데 실제 코드로 구현체를 보진 못했습니다)

<filter
  id="gaussianBlur"
  filterUnits="objectBoundingBox"
  x="-20%"
  y="-20%"
  width="140%"
  height="140%"
>
  <feGaussianBlur stdDeviation={gaussPx} />
</filter>;

Css Blur 

ngth>)에 해당하는 값에 gaussPx에 넣는다고 생각하면 됩니다.(실제 구현은 아주 동일한 값은 아니라고도 하는데 실제 코드로 구현체를 보진 못했습니다)마지막으로 Css블러는 

style={{ filter: `blur(${gaussPx}px)` }}

이렇게 됩니다. 깔끔하죠?

구현 화면

이제 실제 구현된 코드로 살펴보겠습니다.

이렇게 3가지 코드를 구현해보았습니다.

여기서 눈여겨 볼점은 크게 2가지일듯합니다.

  • 왼쪽의 Box Blur를 10회하면 css Blur 필터의 값과 비슷해진다는 점
  • 직접 만든 Gaussian Blur와 css Blur와 같은 값(5px)이라고 결과가 완벽히 동일하지 않다는 점

움직이는 영상

마무리

처음엔 “CSS blur는 그냥 흐려주는 효과일 뿐”이라고 생각하기 쉽지만, 실제로는 수학, 그래픽스, 브라우저 렌더링 엔진까지 연결되는 깊은 세계가 있었습니다.

“좋은 건 비싸다”는 말처럼, 가우시안 블러는 분명 아름답지만 비용이 큰 연산입니다. 그래서 우리는 더 가볍게, 더 똑똑하게 다루는 방법을 고민해야 하죠. 이번 글이 그 이해의 첫걸음이 되었길 바랍니다.

코드 - https://github.com/gyeongseokKang/blur-demo
배포 - https://blur-demo-iota.vercel.app/

 

참고자료

 

반응형

댓글