정규 표현식 이상의 기능: Chrome DevTools에서 CSS 값 파싱 향상

Philip Pfaffe
Ergün Erdogmus
Ergün Erdogmus

Chrome DevTools의 CSS 속성을 발견하셨나요? 스타일 탭이 최근 좀 더 세련된 모습으로 바뀌었나요? Chrome 121~128에서 출시된 이러한 업데이트는 CSS 값을 파싱하고 표시하는 방식을 크게 개선한 결과입니다. 이 도움말에서는 정규 표현식 일치 시스템에서 더 강력한 파서로 전환하는 등 이러한 변환의 기술적인 세부정보를 안내합니다.

현재 DevTools를 이전 버전과 비교해 보겠습니다.

위쪽은 최신 Chrome이고, 아래쪽은 Chrome 121입니다.

상당한 차이가 있죠? 주요 개선사항은 다음과 같습니다.

  • color-mix color-mix 함수 내의 두 색상 인수를 시각적으로 나타내는 편리한 미리보기입니다.
  • pink 이름이 지정된 색상 pink의 클릭 가능한 색상 미리보기입니다. 클릭하면 색상 선택 도구가 열리고 쉽게 조정할 수 있습니다.
  • var(--undefined, [fallback value]) 정의되지 않은 변수의 처리가 개선되어, 정의되지 않은 변수가 비활성화되고 활성 대체 값 (이 경우 HSL 색상)이 클릭 가능한 색상 미리보기와 함께 표시됩니다.
  • hsl(…): hsl 색상 함수의 클릭 가능한 또 다른 색상 미리보기로 색상 선택 도구에 빠르게 액세스할 수 있습니다.
  • 177deg: 클릭 가능한 각도 시계로, 양방향으로 각도 값을 드래그하고 수정할 수 있습니다.
  • var(--saturation, …): 맞춤 속성 정의로 연결되는 클릭 가능한 링크를 통해 관련 선언으로 쉽게 이동할 수 있습니다.

확연히 다릅니다. 이를 실현하려면 DevTools가 CSS 속성 값을 이전보다 훨씬 더 잘 이해하도록 가르쳐야 했습니다.

이 미리보기가 이미 제공되지 않았나요?

이러한 미리보기 아이콘이 친숙해 보일 수 있지만, 특히 위의 예와 같은 복잡한 CSS 구문에서 항상 일관되게 표시되는 것은 아닙니다. 실제로 작동한 경우에도 제대로 기능하려면 상당한 노력이 필요했던 경우가 많았습니다.

그 이유는 DevTools를 사용한 첫날부터 값 분석 시스템이 유기적으로 성장해 왔기 때문입니다. 그러나 CSS에서 최근에 도입한 놀라운 새 기능과 그에 따른 언어 복잡성 증가를 따라잡지 못했습니다. 진화에 발맞추기 위해 시스템을 완전히 재설계해야 했고 이것이 바로 우리가 한 일이었습니다.

CSS 속성 값 처리 방식

DevTools의 Styles 탭에서 속성 선언을 렌더링하고 장식하는 프로세스는 두 단계로 나뉩니다.

  1. 구조 분석. 이 초기 단계에서는 속성 선언을 분석하여 기본 구성요소와 그 관계를 식별합니다. 예를 들어 border: 1px solid red 선언에서 1px를 길이로, solid를 문자열로, red을 색상으로 인식합니다.
  2. 렌더링 구조 분석을 기반으로 렌더링 단계에서는 이러한 구성 요소를 HTML 표현으로 변환합니다. 이렇게 하면 표시되는 속성 텍스트가 상호작용 요소와 시각적 단서로 보강됩니다. 예를 들어 색상 값 red은 클릭 가능한 색상 아이콘으로 렌더링됩니다. 클릭하면 쉽게 수정할 수 있도록 색상 선택 도구가 표시됩니다.

정규 표현식

이전에는 구조 분석을 위해 정규 표현식 (정규식)을 사용하여 속성 값을 분석했습니다. 우리는 데코레이션이라고 생각하는 속성 값의 비트와 일치하도록 정규 표현식 목록을 유지했습니다. 예를 들어 CSS 색상, 길이, 각도, 더 복잡한 하위 표현식(예: var 함수 호출 등)과 일치하는 표현식이 있었습니다. 텍스트를 왼쪽에서 오른쪽으로 스캔하여 가치 분석을 수행했으며, 목록에서 텍스트의 다음 부분과 일치하는 첫 번째 표현식을 계속 찾습니다.

대부분의 경우 문제가 해결되지 않았지만 계속 증가하지 않은 사례의 수는 늘었습니다. 지난 몇 년 동안 제대로 일치하지 않는 버그 신고가 많이 접수되었습니다. 문제를 해결하는 동안 기술적 부채를 줄이기 위해 접근 방식을 다시 생각해봐야 했습니다. 몇 가지 문제를 살펴보겠습니다.

color-mix() 일치

color-mix() 함수에 사용한 정규식은 다음과 같았습니다.

/color-mix\(.*,\s*(?<firstColor>.+)\s*,\s*(?<secondColor>.+)\s*\)/g

구문과 일치하는 항목은 다음과 같습니다.

color-mix(<color-interpolation-method>, [<color> && <percentage [0,100]>?]#{2})

다음 예를 실행하여 일치 항목을 시각화해 보세요.

const re = /color-mix\(.*,\s*(?<firstColor>.+)\s*,\s*(?<secondColor>.+)\s*\)/g;

// it works - simpler example
const simpler = re.exec('color-mix(in srgb, pink, hsl(127deg 100% 50%))');
console.table(simpler.groups);

re.exec('');

// it doesn't work - complex example
const complex = re.exec('color-mix(in srgb, pink, var(--undefined, hsl(127deg var(--saturation, 100%) 50%)))');
console.table(complex.groups);

색상 혼합 함수의 일치 결과입니다.

더 간단한 예가 적합합니다. 그러나 더 복잡한 예에서 <firstColor> 일치는 hsl(177deg var(--saturation이고 <secondColor> 일치는 100%) 50%))입니다. 이는 완전히 의미가 없습니다.

문제가 발생한다는 것을 알고 있었습니다. 공식 언어인 CSS는 일반적이 아니므로 var 함수와 같은 더 복잡한 함수 인수를 처리하기 위한 특수 처리를 이미 포함했습니다. 하지만 첫 번째 스크린샷에서 볼 수 있듯이 모든 경우에 작동하지는 않았습니다.

tan() 일치

신고된 가장 웃긴 버그 중 하나는 삼각함수 tan() 함수에 관한 것이었습니다 . 색상 일치를 위해 사용한 정규식에는 red 키워드와 같이 이름이 지정된 색상을 일치시키기 위한 하위 표현식 \b[a-zA-Z]+\b(?!-)가 포함되었습니다. 그런 다음 일치하는 부분이 실제로 이름이 지정된 색상인지 확인하고 tan도 이름이 지정된 색상임을 추측했습니다. 따라서 tan() 표현식을 색상으로 잘못 해석했습니다.

var() 일치

다른 var() 참조(var(--non-existent, var(--margin-vertical)))가 포함된 대체가 있는 var() 함수라는 또 다른 예시를 살펴보겠습니다.

var()의 정규식은 이 값과 일치하게 됩니다. 단, 첫 번째 닫는 괄호에서는 일치가 중지됩니다. 따라서 위 텍스트는 var(--non-existent, var(--margin-vertical)과 일치합니다. 이는 교과서에서 정규 표현식 일치의 제한사항입니다. 일치하는 괄호가 필요한 언어는 기본적으로 일반적이지 않습니다.

CSS 파서로 전환

분석된 언어가 정규 표현식이 아니기 때문에 정규 표현식을 사용한 텍스트 분석이 작동하지 않으면 표준 다음 단계가 있습니다. 상위 유형의 문법에 파서를 사용하는 것입니다. CSS의 경우 이는 컨텍스트 없는 언어를 위한 파서를 의미합니다. 실제로 이러한 파서 시스템은 DevTools 코드베이스에 이미 존재했습니다. CodeMirror의 LezerSources 패널에서 찾을 수 있는 편집기인 CodeMirror의 구문 강조표시에 기반합니다. Lezer의 CSS 파서를 사용하면 CSS 규칙의 (추상적이지 않은) 구문 트리를 생성할 수 있으므로 바로 사용할 수 있었습니다. 승리.

속성 값 `hsl(177deg var(--saturation, 100%) 50%)`의 구문 트리입니다. 이는 Lezer 파서가 생성한 결과의 단순화된 버전으로, 쉼표와 괄호에 대한 구문 노드만 남겨둡니다.

그 밖에도, 우리는 정규 표현식 기반 일치에서 파서 기반 일치로 직접 마이그레이션하는 것이 현실적으로 불가능하다는 사실을 발견했습니다. 두 접근 방식은 반대 방향에서 작동합니다. 정규 표현식으로 값 조각을 일치시킬 때 DevTools는 입력을 왼쪽에서 오른쪽으로 스캔하고, 순서가 지정된 패턴 목록에서 가장 오래된 일치 항목을 반복적으로 찾으려고 시도합니다. 구문 트리를 사용하면 매칭이 아래에서부터 시작됩니다. 예를 들어 함수 호출을 일치시키려고 시도하기 전에 먼저 호출의 인수를 분석합니다. 이를 산술식 평가라고 생각하세요. 먼저 괄호로 묶은 표현식, 곱셈 연산자, 덧셈 연산자를 사용합니다. 이 프레이밍에서 정규식 기반 일치는 왼쪽에서 오른쪽으로 산술식을 평가하는 것에 해당합니다. 전체 매칭 시스템을 처음부터 다시 작성하고 싶지는 않았습니다. 수천 줄의 코드가 포함된 15개의 서로 다른 매처 및 렌더러 쌍이 있었기 때문에 단일 마일스톤 내에 출시할 수 없게 되었습니다.

그래서 점진적 변경이 가능한 솔루션을 고안했으며 아래에 더 자세히 설명하겠습니다. 요약하자면, 우리는 2단계 접근 방식을 유지했지만, 첫 번째 단계에서는 하위 표현식을 상향식으로 일치시키려고 시도하고 (따라서 정규식 흐름과 중단) 두 번째 단계에서는 하향식을 렌더링합니다. 두 단계 모두 사실상 변경되지 않은 기존 정규식 기반 매처와 렌더를 사용할 수 있었기 때문에 이를 하나씩 마이그레이션할 수 있었습니다.

1단계: 상향식 매칭

1단계에서는 표지에 나온 내용을 거의 정확하게 또는 그다지 배타적으로 다룹니다. 트리를 아래에서 위로 순서대로 순회하며 방문하는 각 구문 트리 노드에서 하위 표현식을 일치시키려고 합니다. 특정 하위 표현식을 일치시키기 위해 매처는 기존 시스템에서와 마찬가지로 정규식을 사용할 수 있습니다. 버전 128 현재 길이 일치와 같은 일부 사례에서 실제로 이 작업을 계속합니다. 또는 매처가 현재 노드에 루팅된 하위 트리의 구조를 분석할 수 있습니다. 이를 통해 구문 오류를 포착하면서 동시에 구조 정보를 기록할 수 있습니다.

위의 구문 트리 예를 생각해 보세요.

1단계: 구문 트리에서 상향식 일치

이 트리의 경우 매처는 다음 순서로 적용됩니다.

  1. hsl(177degvar(--saturation, 100%) 50%): 먼저 hsl 함수 호출의 첫 번째 인수인 색조 각도를 찾습니다. angle 값을 각도 아이콘으로 장식할 수 있도록 각도 일치자와 매칭합니다.
  2. hsl(177degvar(--saturation, 100%)50%): 둘째, var 일치자를 사용하여 var 함수 호출을 찾습니다. 이러한 통화의 경우 주로 다음 두 가지 작업을 실행합니다. <ph type="x-smartling-placeholder">
      </ph>
    • 변수 선언을 조회하여 값을 계산한 다음 변수 이름에 링크와 팝오버를 추가하여 각각 연결합니다.
    • 계산된 값이 색상인 경우 호출을 색상 아이콘으로 장식합니다. 실제로 세 번째가 있지만 그것에 대해 나중에 이야기하겠습니다.
  3. hsl(177deg var(--saturation, 100%) 50%): 마지막으로 hsl 함수의 호출 표현식을 일치시키므로 색상 아이콘으로 장식할 수 있습니다.

데코레이션할 하위 표현식을 검색하는 것 외에도 실제로 일치 프로세스의 일부로 실행 중인 두 번째 기능이 있습니다. 2단계에서 변수 이름에 대해 계산된 값을 조회한다고 했습니다. 실제로 한 걸음 더 나아가 결과를 트리 위로 전파합니다. 변수뿐만 아니라 대체 값에도 적용됩니다. var 함수 노드를 방문할 때 하위 요소를 미리 방문한 적이 있으므로 대체 값에 표시될 수 있는 var 함수의 결과를 이미 알고 있습니다. 따라서 쉽고 저렴하게 var 함수를 결과로 즉시 대체할 수 있으므로 2단계에서 했던 것처럼 '이 var의 결과가 색상을 호출하나요?'와 같은 질문에 쉽게 답할 수 있습니다.

2단계: 하향식 렌더링

두 번째 단계에서는 방향을 반대로 합니다. 1단계의 일치 결과를 바탕으로 트리를 위에서 아래로 순서대로 순회하여 HTML로 렌더링합니다. 방문한 각 노드에 대해 노드가 일치하는지 확인하고, 일치하면 매처의 상응하는 렌더기를 호출합니다. 텍스트 노드의 기본 매처와 렌더기를 포함하기 때문에 텍스트만 포함된 노드 (예: NumberLiteral '50%')를 특별히 처리할 필요가 없습니다. 렌더기는 단순히 HTML 노드를 출력하며, 합쳐지면 장식을 포함한 속성 값의 표현을 생성합니다.

2단계: 구문 트리에서 하향식 렌더링

예시 트리의 경우 속성 값이 렌더링되는 순서는 다음과 같습니다.

  1. hsl 함수 호출을 방문합니다. 일치했으므로 색상 함수 렌더기를 호출합니다. 다음 두 가지 작업을 합니다. <ph type="x-smartling-placeholder">
      </ph>
    • 모든 var 인수에 관한 즉석 대체 메커니즘을 사용하여 실제 색상 값을 계산한 다음 색상 아이콘을 그립니다.
    • CallExpression의 하위 요소를 재귀적으로 렌더링합니다. 이 함수는 텍스트인 함수 이름, 괄호, 쉼표의 렌더링을 자동으로 처리합니다.
  2. hsl 호출의 첫 번째 인수를 방문합니다. 일치하는 것이므로 각도 아이콘과 각도의 텍스트를 그리는 angle 렌더기를 호출합니다.
  3. 두 번째 인수인 var 호출을 방문합니다. 일치하므로 다음과 같이 출력되는 var renderer를 호출합니다. <ph type="x-smartling-placeholder">
      </ph>
    • 시작 부분에 있는 텍스트 var(입니다.
    • 변수 이름을 지정하고, 변수의 정의에 대한 링크 또는 정의되지 않은 것을 나타내는 회색 텍스트로 장식합니다. 또한 변수에 팝오버를 추가하여 값에 대한 정보를 표시합니다.
    • 그런 다음 쉼표는 대체 값을 재귀적으로 렌더링합니다.
    • 닫는 괄호.
  4. hsl 호출의 마지막 인수를 방문합니다. 일치하지 않으므로 텍스트 콘텐츠만 출력합니다.

이 알고리즘에서 렌더는 일치하는 노드의 하위 요소가 렌더링되는 방식을 완전히 제어합니다. 하위 요소를 재귀적으로 렌더링하는 것은 사전 예방적입니다. 이 트릭을 통해 정규식 기반 렌더링에서 구문 트리 기반 렌더링으로 단계별 이전이 가능해졌습니다. 기존 정규식 일치자와 일치하는 노드의 경우, 해당 렌더기를 원래 형식으로 사용할 수 있습니다. 구문 트리 측면에서는 전체 하위 트리를 렌더링하는 책임을 지며 그 결과 (HTML 노드)는 주변 렌더링 프로세스에 깔끔하게 삽입될 수 있습니다. 이를 통해 매처와 렌더러를 쌍으로 포팅하고 하나씩 교체할 수 있었습니다.

일치하는 노드의 자식 렌더링을 제어하는 렌더러의 또 다른 멋진 기능은 우리가 추가하는 아이콘 간의 종속성에 대해 추론할 수 있는 기능을 제공한다는 것입니다. 위의 예에서 hsl 함수로 생성된 색상은 색조 값에 따라 분명히 달라집니다. 즉, 색상 아이콘에 표시되는 색상이 각도 아이콘이 표시되는 각도에 따라 다릅니다. 사용자가 이 아이콘을 통해 각도 편집기를 열고 각도를 수정하면 이제 색상 아이콘의 색상을 실시간으로 업데이트할 수 있습니다.

위의 예에서 알 수 있듯이 이 메커니즘을 다른 아이콘 쌍(예: color-mix() 및 두 색상 채널) 또는 대체에서 색상을 반환하는 var 함수에도 사용합니다.

성능 영향

신뢰성을 개선하고 오래된 문제를 해결하기 위해 이 문제를 자세히 살펴볼 때, 완벽한 파서를 실행하기 시작했다는 점을 고려할 때 어느 정도의 성능 회귀가 예상되었습니다. 이를 테스트하기 위해 약 3.5k 속성 선언을 렌더링하는 벤치마크를 만들고 M1 머신에서 6배 제한으로 정규식 기반 버전과 파서 기반 버전을 모두 프로파일링했습니다.

예상했던 대로, 파싱 기반 접근 방식이 정규식 기반 접근 방식보다 27% 느린 것으로 나타났습니다. 정규식 기반 접근 방식은 렌더링하는 데 11초가 걸렸고 파서 기반 접근 방식은 렌더링하는 데 15초가 걸렸습니다.

새로운 접근 방식이 가져다주는 이점을 고려하여 이 전략을 추진하기로 했습니다.

감사의 말씀

이 게시물 편집에 도움을 주신 소피아 에멜리아노바와 제슬린 옌에게 진심으로 감사드립니다.

미리보기 채널 다운로드

Chrome Canary, Dev 또는 베타를 기본 개발 브라우저로 사용해 보세요. 이러한 미리보기 채널을 통해 최신 DevTools 기능에 액세스하고, 최첨단 웹 플랫폼 API를 테스트하고, 사용자보다 먼저 사이트에서 문제를 발견할 수 있습니다.

Chrome DevTools 팀에 문의하기

다음 옵션을 사용하여 게시물의 새로운 기능과 변경 사항 또는 DevTools와 관련된 다른 사항에 대해 논의하세요.

  • crbug.com을 통해 제안이나 의견을 보내주세요.
  • 옵션 더보기   더보기 >를 사용하여 DevTools 문제 신고 도움말 > DevTools에서 DevTools 문제를 신고합니다.
  • @ChromeDevTools에서 트윗하세요.
  • DevTools의 새로운 기능 YouTube 동영상 또는 DevTools 도움말 YouTube 동영상에 의견을 남겨주세요.