최근에 Chrome DevTools의 스타일 탭에 있는 CSS 속성이 좀 더 세련되어 보이는 것을 발견하셨나요? Chrome 121과 128 사이에서 출시된 이러한 업데이트는 CSS 값을 파싱하고 표시하는 방식을 크게 개선한 결과입니다. 이 도움말에서는 정규식 일치 시스템에서 더 강력한 파서로 전환하는 이 변환의 기술적 세부정보를 설명합니다.
현재 DevTools를 이전 버전과 비교해 보겠습니다.
상당한 차이가 있죠? 다음은 주요 개선사항입니다.
color-mix
.color-mix
함수 내의 두 색상 인수를 시각적으로 나타내는 편리한 미리보기입니다.pink
. 이름이 지정된 색상pink
의 클릭 가능한 색상 미리보기입니다. 클릭하면 색상 선택 도구가 열려 쉽게 조정할 수 있습니다.var(--undefined, [fallback value])
. 정의되지 않은 변수를 비활성화하고 활성 대체 값 (이 경우 HSL 색상)을 클릭 가능한 색상 미리보기와 함께 표시하여 정의되지 않은 변수의 처리를 개선했습니다.hsl(…)
:hsl
색상 함수의 또 다른 클릭 가능한 색상 미리보기로, 색상 선택 도구에 빠르게 액세스할 수 있습니다.177deg
: 클릭 가능한 각도 시계로, 각도 값을 상호작용 방식으로 드래그하고 수정할 수 있습니다.var(--saturation, …)
: 맞춤 속성 정의로 연결되는 클릭 가능한 링크로, 관련 선언으로 쉽게 이동할 수 있습니다.
차이가 상당합니다. 이를 위해 DevTools가 이전보다 CSS 속성 값을 훨씬 더 잘 이해하도록 가르쳐야 했습니다.
이러한 미리보기는 이미 제공되지 않았나요?
이러한 미리보기 아이콘은 익숙해 보이지만, 특히 위의 예와 같이 복잡한 CSS 문법에서는 항상 일관되게 표시되지는 않았습니다. 실제로 작동하더라도 제대로 작동하려면 많은 노력이 필요한 경우가 많았습니다.
이는 값을 분석하는 시스템이 DevTools 초기부터 유기적으로 성장해 왔기 때문입니다. 그러나 CSS에서 최근에 도입한 놀라운 새 기능과 그에 따른 언어 복잡성 증가를 따라잡지 못했습니다. 진화에 발맞추기 위해 시스템을 완전히 재설계해야 했고 이것이 바로 우리가 한 일이었습니다.
CSS 속성 값이 처리되는 방식
DevTools에서 스타일 탭에서 속성 선언을 렌더링하고 장식하는 프로세스는 두 가지 고유한 단계로 나뉩니다.
- 구조 분석 이 초기 단계에서는 속성 선언을 분석하여 기본 구성요소와 그 관계를 식별합니다. 예를 들어 선언
border: 1px solid red
에서1px
는 길이로,solid
는 문자열로,red
는 색상으로 인식됩니다. - 렌더링 렌더링 단계는 구조 분석을 기반으로 이러한 구성요소를 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%))
입니다. 이는 완전히 의미가 없습니다.
YouTube는 이 문제가 있다는 것을 알고 있었습니다. 결국 공식 언어로서 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의 Lezer는 Sources 패널에 있는 편집기인 CodeMirror의 구문 강조 표시의 기반이 되는 예입니다. Lezer의 CSS 파서를 사용하면 CSS 규칙의 (추상적이지 않은) 문법 트리를 생성할 수 있으며 바로 사용할 수 있습니다. 승리.
단, 정규식 기반 일치에서 파서 기반 일치로 직접 이전하는 것은 불가능합니다. 두 접근 방식은 서로 반대 방향으로 작동하기 때문입니다. 값을 정규 표현식과 일치시킬 때 DevTools는 입력을 왼쪽에서 오른쪽으로 스캔하여 순서가 지정된 패턴 목록에서 가장 일찍 일치하는 항목을 반복적으로 찾으려고 시도했습니다. 구문 트리를 사용하면 매칭이 아래에서부터 시작됩니다. 예를 들어 함수 호출을 일치시키려고 시도하기 전에 먼저 호출의 인수를 분석합니다. 산술식을 평가하는 것과 같이 생각하면 됩니다. 먼저 괄호로 묶인 표현식을 고려한 다음 곱셈 연산자를 고려한 다음 더하기 연산자를 고려합니다. 이 프레임워크에서 정규식 기반 일치는 산술식의 왼쪽에서 오른쪽으로 평가하는 것에 해당합니다. 전체 일치 시스템을 처음부터 다시 작성하고 싶지는 않았습니다. 수천 줄의 코드가 포함된 15개의 서로 다른 매처와 렌더러 쌍이 있었기 때문에 단일 마일스톤으로 출시할 가능성은 낮았습니다.
이에 따라 점진적으로 변경할 수 있는 솔루션을 마련했습니다. 자세한 내용은 아래를 참고하세요. 간단히 말해 두 단계 접근 방식을 유지했지만 첫 번째 단계에서는 하위 표현식을 하향식 방식으로 일치시키려고 시도하여 정규식 흐름을 중단하고 두 번째 단계에서는 위에서 아래로 렌더링합니다. 두 단계 모두 기존 정규식 기반 매처와 렌더를 거의 변경하지 않고 사용할 수 있었으므로 하나씩 이전할 수 있었습니다.
1단계: 하향식 일치
첫 번째 단계는 표지에 표시된 내용을 거의 정확하고 배타적으로 실행합니다. 트리를 아래에서 위로 순서대로 탐색하고 방문하는 각 문법 트리 노드에서 하위 표현식을 일치시키려고 시도합니다. 특정 하위 표현식을 일치시키기 위해 일치 항목은 기존 시스템에서와 마찬가지로 정규식을 사용할 수 있습니다. 버전 128 현재 길이 일치와 같은 일부 사례에서 실제로 이 작업을 계속합니다. 또는 매처가 현재 노드에 루팅된 하위 트리의 구조를 분석할 수 있습니다. 이를 통해 구문 오류를 포착하고 구조 정보를 동시에 기록할 수 있습니다.
위의 구문 트리 예시를 살펴보세요.
이 트리의 경우 매처는 다음 순서로 적용됩니다.
hsl(
177deg
var(--saturation, 100%) 50%)
: 먼저hsl
함수 호출의 첫 번째 인수인 색조 각도를 찾습니다. 각도 값을 각도 아이콘으로 장식할 수 있도록 각도 매처와 일치시킵니다.hsl(177deg
var(--saturation, 100%)
50%)
: 두 번째로, var 매처를 사용하여var
함수 호출을 찾습니다. 이러한 통화의 경우 주로 다음 두 가지 작업을 실행합니다.- 변수의 선언을 조회하고 값을 계산한 다음 변수 이름에 링크와 팝오버를 각각 추가하여 연결합니다.
- 계산된 값이 색상인 경우 호출을 색상 아이콘으로 장식합니다. 사실 세 번째 사항이 있지만 나중에 말씀드리겠습니다.
hsl(177deg var(--saturation, 100%) 50%)
: 마지막으로hsl
함수의 호출 표현식을 일치시켜 색상 아이콘으로 장식합니다.
장식할 하위 표현식을 검색하는 것 외에도 일치 프로세스의 일부로 실행되는 두 번째 기능이 있습니다. 2단계에서 변수 이름의 계산된 값을 조회한다고 했습니다. 실제로 한 걸음 더 나아가 결과를 트리 위로 전파합니다. 변수뿐만 아니라 대체 값에도 적용됩니다. var
함수 노드를 방문할 때는 그 하위 요소가 이미 방문되었으므로 대체 값에 표시될 수 있는 모든 var
함수의 결과를 이미 알고 있습니다. 따라서 var
함수를 결과로 실시간으로 쉽고 저렴하게 대체할 수 있으므로 2단계에서와 같이 '이 var
호출의 결과가 색상인가요?'와 같은 질문에 간단히 답변할 수 있습니다.
2단계: 하향식 렌더링
두 번째 단계에서는 방향을 반대로 합니다. 1단계의 일치 결과를 바탕으로 트리를 위에서 아래로 순서대로 순회하여 HTML로 렌더링합니다. 방문한 각 노드에 대해 노드가 일치하는지 확인하고, 일치하면 매처의 상응하는 렌더기를 호출합니다. 텍스트 노드의 기본 일치자와 렌더러를 포함하여 텍스트만 포함된 노드 (예: NumberLiteral
'50%')에 대한 특수 처리가 필요하지 않습니다. 렌더러는 HTML 노드를 출력하기만 하면 됩니다. 이러한 노드를 조합하면 장식을 포함한 속성 값의 표현이 생성됩니다.
예시 트리의 경우 속성 값이 렌더링되는 순서는 다음과 같습니다.
hsl
함수 호출을 방문합니다. 일치하므로 색상 함수 렌더러를 호출합니다. 다음 두 가지 작업을 실행합니다.- 모든
var
인수에 대한 실시간 대체 메커니즘을 사용하여 실제 색상 값을 계산한 다음 색상 아이콘을 그립니다. CallExpression
의 하위 요소를 재귀적으로 렌더링합니다. 이 함수는 텍스트인 함수 이름, 괄호, 쉼표의 렌더링을 자동으로 처리합니다.
- 모든
hsl
호출의 첫 번째 인수를 방문합니다. 일치하므로 각도 아이콘과 각도 텍스트를 그리는 각도 렌더러를 호출합니다.- 두 번째 인수인
var
호출을 방문합니다. 일치하므로 var renderer를 호출하면 다음이 출력됩니다.- 시작 부분의 텍스트
var(
- 변수 이름을 지정하고, 변수의 정의에 대한 링크 또는 정의되지 않은 것을 나타내는 회색 텍스트로 장식합니다. 또한 변수에 팝오버를 추가하여 값에 대한 정보를 표시합니다.
- 쉼표 뒤에 대체 값이 재귀적으로 렌더링됩니다.
- 닫는 괄호.
- 시작 부분의 텍스트
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 동영상에 댓글을 남겨주세요.