이 도움말에서는 Chrome 85부터 제공된 DevTools의 CSS-in-JS 지원과 일반적으로 CSS-in-JS의 의미, 오랫동안 DevTools에서 지원해 온 일반 CSS와의 차이점에 대해 설명합니다.
CSS-in-JS란 무엇인가요?
CSS-in-JS의 정의는 다소 모호합니다. 넓은 의미에서 JavaScript를 사용하여 CSS 코드를 관리하는 접근 방식입니다. 예를 들어 CSS 콘텐츠가 JavaScript를 사용하여 정의되고 최종 CSS 출력이 앱에서 즉시 생성된다는 의미일 수 있습니다.
DevTools의 맥락에서 CSS-in-JS는 CSS 콘텐츠가 CSSOM API를 사용하여 페이지에 삽입된다는 것을 의미합니다. 일반 CSS는 <style>
또는 <link>
요소를 사용하여 삽입되며 정적 소스 (예: DOM 노드 또는 네트워크 리소스)가 있습니다. 반면 CSS-in-JS에는 정적 소스가 없는 경우가 많습니다. 여기서 특별한 경우는 <style>
요소의 콘텐츠가 CSSOM API를 사용하여 업데이트될 수 있으므로 소스가 실제 CSS 스타일시트와 동기화되지 않을 수 있다는 점입니다.
CSS-in-JS 라이브러리 (예: styled-component, Emotion, JSS)를 사용하는 경우 개발 모드와 브라우저에 따라 라이브러리가 내부적으로 CSSOM API를 사용하여 스타일을 삽입할 수 있습니다.
CSS-in-JS 라이브러리에서 하는 것과 유사하게 CSSOM API를 사용하여 스타일시트를 삽입하는 방법을 보여주는 몇 가지 예를 살펴보겠습니다.
// Insert new rule to an existing CSS stylesheet
const element = document.querySelector('style');
const stylesheet = element.sheet;
stylesheet.replaceSync('.some { color: blue; }');
stylesheet.insertRule('.some { color: green; }');
완전히 새로운 스타일시트를 만들 수도 있습니다.
// Create a completely new stylesheet
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync('.some { color: blue; }');
stylesheet.insertRule('.some { color: green; }');
// Apply constructed stylesheet to the document
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
DevTools의 CSS 지원
DevTools에서 CSS를 처리할 때 가장 일반적으로 사용되는 기능은 스타일 창입니다. 스타일 창에서 특정 요소에 적용되는 규칙을 보고, 규칙을 수정하고, 페이지의 변경사항을 실시간으로 확인할 수 있습니다.
작년 이전에는 CSSOM API를 사용하여 수정된 CSS 규칙에 대한 지원이 제한적이었으며 적용된 규칙만 볼 수 있고 수정할 수는 없었습니다. 작년에는 스타일 창을 사용하여 CSS-in-JS 규칙을 편집할 수 있도록 하는 것이 주요 목표였습니다. CSS-in-JS 스타일 'builted'를 호출하여 웹 API를 사용해 생성되었음을 나타내기도 합니다.
DevTools에서 스타일 수정 작업의 세부정보를 자세히 살펴보겠습니다.
DevTools의 스타일 수정 메커니즘
DevTools에서 요소를 선택하면 Styles 창이 표시됩니다. 스타일 창은 CSS.getMatchedStylesForNode라는 CDP 명령어를 실행하여 요소에 적용되는 CSS 규칙을 가져옵니다. CDP는 Chrome DevTools 프로토콜의 약자이며 DevTools 프런트엔드가 검사된 페이지에 관한 추가 정보를 가져올 수 있는 API입니다.
호출되면 CSS.getMatchedStylesForNode
는 문서의 모든 스타일시트를 식별하고 브라우저의 CSS 파서를 사용하여 이를 파싱합니다. 그런 다음 모든 CSS 규칙을 스타일시트 소스의 위치와 연결하는 색인을 빌드합니다.
CSS를 다시 파싱해야 하는 이유는 무엇일까요? 여기서 문제는 성능상의 이유로 브라우저 자체가 CSS 규칙의 소스 위치에 관심이 없으므로 이를 저장하지 않는다는 점입니다. 하지만 DevTools는 CSS 편집을 지원하기 위해 소스 위치가 필요합니다. 우리는 일반 Chrome 사용자가 성능 저하를 겪지 않기를 바라지만, DevTools 사용자가 소스 위치에 액세스할 수 있기를 바랍니다. 이 재파싱 접근 방식은 두 가지 사용 사례를 모두 최소한의 단점으로 해결합니다.
그런 다음 CSS.getMatchedStylesForNode
구현은 브라우저의 스타일 엔진에 지정된 요소와 일치하는 CSS 규칙을 제공하도록 요청합니다. 마지막으로 이 메서드는 스타일 엔진에서 반환한 규칙을 소스 코드와 연결하고 CSS 규칙에 관한 구조화된 응답을 제공하여 DevTools가 규칙의 어느 부분이 선택기인지 또는 속성인지 알 수 있도록 합니다. 이를 통해 DevTools에서 선택기와 속성을 독립적으로 수정할 수 있습니다.
이제 수정 기능을 살펴보겠습니다. CSS.getMatchedStylesForNode
는 모든 규칙의 소스 위치를 반환한다는 것을 기억하세요. 이는 수정 작업에 매우 중요합니다. 규칙을 변경하면 DevTools가 실제로 페이지를 업데이트하는 다른 CDP 명령어를 실행합니다. 이 명령어에는 업데이트 중인 규칙의 프래그먼트의 원래 위치와 프래그먼트를 업데이트해야 하는 새 텍스트가 포함됩니다.
백엔드에서 수정 호출을 처리할 때 DevTools는 대상 스타일시트를 업데이트합니다. 또한 유지 관리하는 스타일시트 소스의 사본을 업데이트하고 업데이트된 규칙의 소스 위치를 업데이트합니다. 수정 호출에 대한 응답으로 DevTools 프런트엔드는 방금 업데이트된 텍스트 프래그먼트의 업데이트된 위치를 다시 가져옵니다.
이로 인해 DevTools에서 CSS-in-JS를 수정하는 것이 즉시 작동하지 않았습니다. CSS-in-JS에는 저장된 실제 소스가 없으며 CSS 규칙은 CSSOM 데이터 구조의 브라우저 메모리에 있습니다.
CSS-in-JS 지원을 추가한 방법
따라서 CSS-in-JS 규칙의 편집을 지원하기 위해 최선의 해결책은 위에서 설명한 기존 메커니즘을 사용하여 편집할 수 있는 구성된 스타일시트의 소스를 만드는 것이라고 결정했습니다.
첫 번째 단계는 소스 텍스트를 빌드하는 것입니다. 브라우저의 스타일 엔진은 CSS 규칙을 CSSStyleSheet
클래스에 저장합니다. 이 클래스는 앞에서 설명한 대로 JavaScript에서 인스턴스를 만들 수 있는 클래스입니다. 소스 텍스트를 빌드하는 코드는 다음과 같습니다.
String InspectorStyleSheet::CollectStyleSheetRules() {
StringBuilder builder;
for (unsigned i = 0; i < page_style_sheet_->length(); i++) {
builder.Append(page_style_sheet_->item(i)->cssText());
builder.Append('\n');
}
return builder.ToString();
}
CSSStyleSheet 인스턴스에서 찾은 규칙을 반복하고 이를 사용하여 단일 문자열을 빌드합니다. 이 메서드는 InspectorStyleSheet 클래스의 인스턴스가 생성될 때 호출됩니다. InspectorStyleSheet 클래스는 CSSStyleSheet 인스턴스를 래핑하고 DevTools에 필요한 추가 메타데이터를 추출합니다.
void InspectorStyleSheet::UpdateText() {
String text;
bool success = InspectorStyleSheetText(&text);
if (!success)
success = InlineStyleSheetText(&text);
if (!success)
success = ResourceStyleSheetText(&text);
if (!success)
success = CSSOMStyleSheetText(&text);
if (success)
InnerSetText(text, false);
}
이 스니펫에는 내부적으로 CollectStyleSheetRules
를 호출하는 CSSOMStyleSheetText
가 있습니다. CSSOMStyleSheetText
는 스타일시트가 인라인이 아니거나 리소스 스타일시트가 아닌 경우 호출됩니다. 기본적으로 이 두 스니펫을 사용하면 이미 new CSSStyleSheet()
생성자를 사용하여 생성된 스타일시트를 기본적으로 수정할 수 있습니다.
CSSOM API를 사용하여 변경된 <style>
태그와 연결된 스타일시트는 특별한 경우가 있습니다. 이 경우 스타일시트에는 소스 텍스트와 소스에 없는 추가 규칙이 포함됩니다. 이 사례를 처리하기 위해 이러한 추가 규칙을 소스 텍스트에 병합하는 메서드를 소개합니다. 여기서 순서는 중요합니다. CSS 규칙이 원본 소스 텍스트 중간에 삽입될 수 있기 때문입니다. 예를 들어 원본 <style>
요소에 다음 텍스트가 포함되어 있다고 가정해 보겠습니다.
/* comment */
.rule1 {}
.rule3 {}
그런 다음 페이지에서 JS API를 사용하여 몇 가지 새 규칙을 삽입하여 다음과 같은 규칙 순서를 생성했습니다. .rule0, .rule1, .rule2, .rule3, .rule4 병합 작업 후 결과 소스 텍스트는 다음과 같아야 합니다.
.rule0 {}
/* comment */
.rule1 {}
.rule2 {}
.rule3 {}
.rule4 {}
규칙의 소스 텍스트 위치가 정확해야 하므로 원래 주석과 들여쓰기를 보존하는 것이 편집 과정에서 중요합니다.
CSS-in-JS 스타일시트의 또 다른 특징은 페이지에서 언제든지 변경할 수 있다는 점입니다. 실제 CSSOM 규칙이 텍스트 버전과 동기화되지 않으면 수정이 작동하지 않습니다. 이를 위해 스타일시트가 변경될 때 브라우저가 DevTools의 백엔드 부분에 알릴 수 있는 프로브를 도입했습니다. 그러면 다음에 CSS.getMatchedStylesForNode를 호출할 때 변형된 스타일시트가 동기화됩니다.
이러한 모든 부분이 있으면 JS의 CSS 편집이 이미 작동하지만 스타일시트가 생성되었는지 나타내는 UI를 개선하고자 했습니다. 프런트엔드에서 CSS 규칙의 소스를 올바르게 표시하는 데 사용하는 isConstructed
라는 새 속성이 CDP의 CSS.CSSStyleSheetHeader에 추가되었습니다.
결론
지금까지의 이야기를 요약하자면, DevTools가 지원하지 않는 CSS-in-JS와 관련된 사용 사례를 살펴보고 이러한 사용 사례를 지원하는 솔루션을 살펴보았습니다. 이 구현의 흥미로운 점은 CSSOM CSS 규칙에 일반 소스 텍스트를 포함하여 기존 기능을 활용할 수 있었기 때문에 DevTools에서 스타일 편집을 완전히 재구성할 필요가 없다는 것입니다.
자세한 배경 정보는 설계 제안서 또는 모든 관련 패치를 참조하는 Chromium 추적 버그를 확인하세요.
미리보기 채널 다운로드
Chrome Canary, Dev 또는 베타를 기본 개발 브라우저로 사용하는 것이 좋습니다. 이러한 미리보기 채널을 통해 최신 DevTools 기능에 액세스할 수 있고, 최첨단 웹 플랫폼 API를 테스트할 수 있으며, 사용자보다 먼저 사이트에서 문제를 발견할 수 있습니다.
Chrome DevTools팀에 문의하기
다음 옵션을 사용하여 새로운 기능, 업데이트 또는 DevTools와 관련된 다른 내용을 논의하세요.
- crbug.com에서 의견 및 기능 요청을 제출하세요.
- DevTools에서 옵션 더보기 > 도움말 > DevTools 문제 신고를 사용하여 DevTools 문제를 신고합니다.
- @ChromeDevTools에 트윗하세요.
- DevTools의 새로운 기능 YouTube 동영상 또는 DevTools 도움말 YouTube 동영상에 댓글을 남겨주세요.