AI가 UI 코드를 뽑을 때, 개발자가 직접 알아야 할 CSS·DOM 세 가지

AI가 UI 코드를 뽑을 때, 개발자가 직접 알아야 할 CSS·DOM 세 가지

글래스모피즘 필터 수식부터 Shadow DOM 격리, getComputedStyle까지—AI가 생성한 코드가 '그럴듯하게 틀리는' 지점은 항상 같은 곳에 있다.

getComputedStyle Shadow DOM 글래스모피즘 backdrop-filter CSS 격리 Claude Code SVG 필터 프론트엔드 디버깅
광고

AI 코딩 에이전트가 UI 코드를 생성하는 속도는 이미 사람을 압도한다. 문제는 속도가 아니다. AI가 뽑아낸 코드는 에러를 던지지 않는다. 그냥 조용히 틀린다. 오버레이가 스크롤하면 밀려나고, 글래스 패널이 회색 상자처럼 보이고, 스타일 계산값이 빈 문자열을 반환한다. 이 '그럴듯한 오답'의 영역은 AI가 아직 채우지 못하는 곳이고, 개발자가 직접 쥐고 있어야 할 지식의 경계선이다.

세 가지 실전 사례가 그 경계를 정확히 가리킨다.


1. 글래스모피즘: backdrop-filter: blur()는 유리가 아니다

backdrop-filter: blur()에 반투명 흰색 오버레이를 얹는 것, 우리가 흔히 '글래스모피즘'이라고 부르는 그 조합은 사실 유리가 아니라 뿌연 사각형이다. 실제 유리는 빛을 굴절시킨다. 특히 엣지에서 가장 강하게 뒤틀린다. 이 굴절(refraction)이 빠진 순간 유리는 그냥 흐릿한 레이어로 전락한다.

dev.to에 올라온 LiquidGlassSkill 구현 사례는 이 굴절을 SVG 필터로 직접 구현한다. 핵심 구조는 세 단계다: 캔버스에 변위 맵(displacement map)을 굽고(bake), feImage → feDisplacementMap → feGaussianBlur 파이프라인을 연결한 뒤, backdrop-filter: url(#filter)로 요소에 걸어준다. backdrop-filter의 입력은 요소 뒤에 있는 라이브 페이지 자체이기 때문에, 스크롤이나 테마 변경, 콘텐츠 변화를 별도 무효화(invalidation) 없이 자동 추적한다.

Claude Code 스킬로 패키징된 이유도 여기에 있다. AI가 이 코드를 생성할 때 '그럴듯하게 실패하는' 패턴이 네 가지나 존재하기 때문이다. 배경이 단순한 평면 페이지면 굴절 효과가 회색 박스처럼 읽힌다. 틴트에 색조(hue)를 섞으면 투과되는 색과 충돌해 탁해진다. 글래스 패널의 자식 요소가 불투명 배경을 가지면 굴절된 백드롭 자체를 덮어버린다. 그리고 가장 까다로운 함정: @supports로 기능 쿼리를 해도 Safari는 backdrop-filter: url(#…)을 파싱은 하되 아무것도 그리지 않기 때문에 @supports가 true를 반환하면서 패널이 공백으로 남는다. 브라우저 엔진 기반 분기가 필요하다.

변위 수식의 세부사항도 놓치기 쉽다. 128/255가 0.5와 다르다는 사실—이 오프셋 처리를 잘못 이해하면 굴절 방향이 뒤틀린다. CSS 200줄짜리 구현에서 이 수식 하나가 전체 효과의 진짜 여부를 결정한다. AI는 코드 구조를 생성하지만, 이 수학적 의미를 이해하고 검증하는 것은 개발자의 몫이다.


2. 디자인 오버레이: 조용히 틀리는 네 가지 DOM 함정

라이브 페이지 위에 디자인 프레임을 올리는 Chrome 익스텐션을 개발하며 정리된 시행착오(dev.to, WideEye 개발기)는 AI가 자주 놓치는 DOM/CSS 구조 지식을 정면으로 가리킨다. 공통점은 하나다: 에러 없이 실행되고, 눈으로 보면 맞는 것 같고, 측정하면 틀리다.

뷰포트 공간 vs 문서 공간. position: fixed로 오버레이를 고정하면 당장은 정확해 보인다. 200px 스크롤하는 순간 오버레이는 콘텐츠 대비 200px 밀려난다. 오버레이는 뷰포트 좌표계에 살고, 비교 대상인 페이지 콘텐츠는 문서 좌표계에 살기 때문이다. 해법은 position: absolute로 문서 원점에 고정하고, transform: translate3d(x, y, 0)으로 오프셋을 주는 것이다. 브라우저가 스크롤 동기화를 무료로 처리해준다. 레이어 생성 시점의 앵커도 중요하다. window.scrollX / scrollY를 초기값으로 잡지 않으면, 스크롤된 상태에서 추가한 레이어가 화면 밖 어딘가에 나타난다.

이미지의 실제 크기와 표시 크기. 2x 익스포트된 1440px 디자인 파일은 PNG의 naturalWidth가 2880이다. 이걸 CSS width로 그대로 사용하면 오버레이가 두 배 크기로 렌더링되고, 반투명 상태에서는 그게 맞는 비율처럼 보인다. 디자인 API에서 scale 파라미터를 받아 나누거나, 사용자가 직접 드롭한 파일은 devicePixelRatio를 활용한 휴리스틱으로 스크린샷 케이스만 자동 처리하고 나머지는 수동 컨트롤을 노출하는 것이 현실적이다. 스케일 조정 시에는 원본 치수를 항상 소스로 유지해야 누적 반올림 오차를 피할 수 있다.

Shadow DOM 격리와 그 한계. 임의의 페이지에 UI를 주입하면 호스트 페이지의 전역 스타일, 리셋, 다른 익스텐션 스타일이 모두 주입된 요소에 적용된다. attachShadow({ mode: 'closed' })로 닫힌 섀도우 루트를 만들면 양방향 격리가 된다. 단, 오버레이 이미지는 문서 좌표계에 있어야 하므로 document.body에 직접 붙여야 하고, 이 요소들은 호스트 페이지 캐스케이드의 영향을 받는다. 이때 opacity 같은 핵심 속성은 setProperty('opacity', value, 'important')로 명시적으로 방어해야 한다. 섀도우 루트 내 상대 경로 에셋은 호스트 페이지 기준으로 해석되므로 chrome.runtime.getURL()과 web_accessible_resources 등록이 모두 필요하고, 레이어 순서는 DOM 순서가 아닌 명시적 z-index와 isolation: isolate로 제어해야 한다.


3. getComputedStyle: AI가 모르는 '계산된 값'의 의미

getComputedStyle은 단순히 스타일을 읽는 API가 아니다. 브라우저가 렌더링을 위해 최종 계산한 실제 픽셀값을 반환한다. CSS에서 width: 50%로 설정해도 getComputedStyle은 200px를 돌려준다. 이 차이를 모르면 AI가 생성한 동적 스타일 조작 코드가 특정 조건에서 빈 문자열을 반환하는 이유를 이해할 수 없다.

AI가 자주 생성하는 element.style.width 접근법은 인라인 스타일만 읽는다. <style> 태그나 외부 CSS 파일에 정의된 값은 이 방식으로 접근하면 빈 문자열이 반환된다. getComputedStyle(element).getPropertyValue('width')는 소스를 불문하고 최종 렌더링 픽셀값을 가져온다. 이 값은 "200px" 형태의 문자열이므로 애니메이션이나 좌표 계산에 쓰려면 parseFloat()으로 단위를 제거해야 한다.

getPropertyValue()가 점 표기법보다 반드시 필요한 순간이 있다. CSS 커스텀 프로퍼티(변수)를 읽을 때다. computed['--main-color']는 JavaScript가 해석할 수 없어 동작하지 않지만, computed.getPropertyValue('--main-color')는 정확히 작동한다. 디자인 토큰 시스템이나 다이나믹 테마를 다루는 코드에서 AI가 생성한 점 표기법이 CSS 변수 접근에 실패하는 이유가 여기에 있다.


AI가 생성하는 코드의 '조용한 실패' 패턴

세 사례를 관통하는 공통 구조가 있다. AI는 코드의 형태를 만들지만, 브라우저가 렌더링 파이프라인에서 어떤 결정을 내리는지—뷰포트 좌표계와 문서 좌표계의 분리, SVG 필터 수식의 수학적 의미, 캐스케이드와 인라인 스타일의 우선순위 구조—를 모델이 항상 정확히 반영하지는 않는다. 그 결과는 예외가 아니라 '그럴듯하게 틀린 UI'다.

이 지점이 개발자가 직접 쥐고 있어야 할 영역이다. AI 에이전트가 200줄의 글래스 CSS를 순식간에 생성하더라도, 128/255 ≠ 0.5라는 변위 맵 수식이 왜 중요한지, Shadow DOM 격리의 경계가 어디서 끊기는지, getComputedStyle이 반환하는 값이 CSS에 쓴 값과 왜 다른지—이 이해가 없으면 AI가 뽑은 코드의 진짜 품질을 판단할 수 없다. 생성은 에이전트에게, 검증은 개발자에게. 그 경계는 아직 여기에 있다.

출처

더 많은 AI 트렌드를 Seedora 앱에서 확인하세요