CSS-in-JS의 장단점

Artem Bali의 사진

최근에 CSS-in-JS에 대한 높은 수준의 개요를 작성했으며 주로이 접근법으로 해결하려는 문제에 대해 이야기했습니다. 라이브러리 작성자는 솔루션의 장단점을 설명하는 데 거의 시간을 투자하지 않습니다. 때로는 너무 편향되어 있기 때문에 사용자가 도구를 어떻게 적용하는지 모르는 경우가 있습니다. 이것이 지금까지 본 트레이드 오프를 설명하려는 시도입니다. JSS의 저자라고 언급하는 것이 중요하므로 편견으로 간주해야합니다.

사회적 영향

웹 플랫폼에서 일하고 JavaScript를 모르는 사람들이 있습니다. 그 사람들은 HTML과 CSS를 작성하기 위해 돈을 받고 있습니다. CSS-in-JS는 개발자의 작업 흐름에 큰 영향을 미쳤습니다. 일부 사람들이 남지 않으면 진정한 변화를 가져올 수 없습니다. CSS-in-JS가 유일한 방법인지는 모르겠지만, 대량 적용은 현대적인 응용 프로그램에서 CSS를 사용할 때 발생하는 문제의 명백한 징후입니다.

문제의 큰 부분은 CSS-in-JS가 빛나는 유스 케이스와 태스크에 올바르게 사용하는 방법을 정확하게 전달할 수 없다는 것입니다. 많은 CSS-in-JS 애호가들이이 기술을 홍보하는 데 성공했지만, 많은 비평가들이 도구에서 값싼 스윙을하지 않고 건설적인 방식으로 트레이드 오프에 대해 이야기하지 않았습니다. 결과적으로 많은 장단점을 숨기고 설명과 해결 방법을 제공하기 위해 많은 노력을 기울이지 않았습니다.

CSS-in-JS는 복잡한 사용 사례를보다 쉽게 ​​처리하기위한 시도이므로 필요하지 않은 곳에 밀어 넣지 마십시오!

런타임 비용

브라우저에서 런타임시 JavaScript에서 CSS가 생성되면 고유 한 오버 헤드가 있습니다. 런타임 오버 헤드는 라이브러리마다 다릅니다. 이것은 일반적인 벤치 마크이지만 자체 테스트를 수행해야합니다. 템플릿 문자열의 전체 CSS 구문 분석, 최적화 양, 동적 스타일 구현 세부 정보, 해싱 알고리즘 및 프레임 워크 통합 비용의 필요성에 따라 런타임시 주요 차이점이 나타납니다. *

잠재적 인 런타임 오버 헤드 외에, 일부 CSS-in-JS 라이브러리는 여러 전략을 지원하고 적용하는 것은 사용자의 몫이므로 4 가지 번들링 전략을 고려해야합니다. *

전략 1 : 런타임 생성 만

런타임 CSS 생성은 JavaScript로 CSS 문자열을 생성 한 다음 스타일 태그를 사용하여 해당 문자열을 문서에 삽입하는 기술입니다. 이 기술은 인라인 스타일이 아닌 스타일 시트를 생성합니다.

런타임 생성의 장단점은 문서가로드되기 시작할 때 초기 단계에서 스타일이 지정된 컨텐츠를 제공 할 수 없다는 것입니다. 이 방법은 일반적으로 즉시 유용 할 수있는 컨텐츠가없는 응용 프로그램에 적합합니다. 일반적으로 이러한 응용 프로그램은 사용자에게 실제로 유용 해지기 전에 사용자 상호 작용이 필요합니다. 이러한 응용 프로그램은 동적으로로드 된 컨텐츠가로드되는 즉시 구식이되는 컨텐츠로 작동하는 경우가 많으므로, 예를 들어 Twitter와 같은 초기 업데이트 파이프 라인을 설정해야합니다. 또한 사용자가 로그인 한 경우 SEO를 위해 HTML을 제공 할 필요가 없습니다.

상호 작용에 JavaScript가 필요한 경우 앱을 준비하기 전에 번들을로드해야합니다. 예를 들어, 문서에 슬랙을로드 할 때 기본 채널의 내용을 표시 할 수 있지만 그 직후 사용자가 채널을 변경하려고 할 수 있습니다. 따라서 초기 내용물을로드하면 즉시 버릴 수 있습니다.

이러한 응용 프로그램의 인식 된 성능은 자리 표시 자 및 기타 트릭으로 향상되어 응용 프로그램이 실제보다 더 즉각적인 느낌을 줄 수 있습니다. 이러한 응용 프로그램은 일반적으로 데이터가 많기 때문에 기사만큼 빨리 유용하지 않습니다.

전략 2 : 중요 CSS를 사용한 런타임 생성

중요 CSS는 페이지를 초기 상태로 스타일을 지정하는 데 필요한 최소량의 CSS입니다. 문서 헤드에 스타일 태그를 사용하여 렌더링됩니다. 이 기술은 CSS-in-JS와 함께 또는 CSS없이 널리 사용됩니다. 두 경우 모두 중요 CSS의 일부와 JavaScript 또는 CSS 번들의 일부로 CSS 규칙을 두 번로드 할 수 있습니다. 중요 CSS의 크기는 내용의 양에 따라 상당히 클 수 있습니다. 일반적으로 문서는 캐시되지 않습니다.

Critical CSS가 없으면 런타임 CSS-JS가있는 정적 컨텐츠가 많은 단일 페이지 애플리케이션은 컨텐츠 대신 자리 표시자를 표시해야합니다. 이는 사용자에게 훨씬 이전에 유용 할 수 있었기 때문에 저급 장치 및 저 대역폭 연결에 대한 접근성을 향상 시켰기 때문에 나쁩니다.

중요한 CSS를 사용하면 초기 단계에서 UI를 차단하지 않고 런타임 CSS 생성을 나중 단계에서 수행 할 수 있습니다. 그러나 약 5 세 이상인 저사양 모바일 장치에서 JavaScript의 CSS 생성은 성능에 부정적인 영향을 줄 수 있습니다. 생성되는 CSS의 양과 사용 된 라이브러리에 따라 크게 달라 지므로 일반화 할 수 없습니다.

이 전략의 단점은 중요 CSS 추출 비용과 런타임 CSS 생성 비용입니다.

전략 3 : 빌드 타임 추출 만

이 전략은 CSS-in-JS가없는 웹의 기본 전략입니다. 일부 CSS-in-JS 라이브러리를 사용하면 빌드시 정적 CSS를 추출 할 수 있습니다. *이 경우 런타임 오버 헤드가 발생하지 않으며 링크 태그를 사용하여 페이지에 CSS가 렌더링됩니다. CSS 생성 비용은 미리 한 번 지불됩니다.

여기에는 2 가지 주요 트레이드 오프가 있습니다.

  1. 상태에 액세스 할 수 없기 때문에 런타임시 CSS-in-JS가 제공하는 일부 동적 API를 사용할 수 없습니다. CSS 맞춤 속성은 모든 브라우저에서 지원되지 않으며 빌드시 본질적으로 폴리 필 할 수 없기 때문에 여전히 CSS 맞춤 속성을 사용할 수 없습니다. 이 경우 동적 테마 및 상태 기반 스타일링을위한 해결 방법을 수행해야합니다. *
  2. 중요 CSS가없고 캐시가 비어 있으면 CSS 번들이로드 될 때까지 첫 번째 페인트를 차단합니다. 문서 헤드의 링크 요소는 HTML 렌더링을 차단합니다.
  3. 단일 페이지 애플리케이션에서 페이지 기반 번들 분할을 통한 비 결정적 특이성. *

전략 4 : 중요 CSS를 사용한 빌드 타임 추출

이 전략은 CSS-in-JS에만 고유하지 않습니다. 중요한 CSS를 사용한 완전 정적 추출은보다 정적 응용 프로그램으로 작업 할 때 최상의 성능을 제공합니다. 이 방법은 차단 링크 태그를 문서의 맨 아래로 이동할 수 있다는 점을 제외하고는 앞서 언급 한 정적 CSS와의 상충 관계를 유지합니다.

CSS 렌더링 전략에는 4 가지가 있습니다. 이 중 2 개만 CSS-JS에 고유하며 모든 라이브러리에 적용되는 것은 없습니다.

접근성

CSS-in-JS는 잘못된 방식으로 사용하면 접근성을 줄일 수 있습니다. JavaScript 번들을로드하고 평가하기 전에 HTML을 채색 할 수 없도록 중요 CSS 추출없이 매우 정적 인 콘텐츠 사이트를 구현할 때 발생합니다. 이는 문서 헤드에 차단 링크 태그를 사용하여 거대한 CSS 파일을 렌더링 할 때 발생할 수 있습니다. 이는 CSS에 특화되지 않고 기존의 임베딩에서 가장 인기있는 현재 문제입니다.

개발자는 접근성을 책임 져야합니다. 인터넷 연결이 불안정하면 경제적으로 취약한 국가의 문제라는 잘못된 생각이 여전히 남아 있습니다. 우리는 지하 철도 시스템이나 대형 건물에 들어갈 때 매일 연결 문제가 있다는 것을 잊어 버리는 경향이 있습니다. 케이블이없는 안정적인 모바일 연결은 신화입니다. 예를 들어 2.4GHz WI-FI 네트워크가 전자 레인지에서 간섭을받을 수있는 등 안정적인 WiFi 연결은 쉽지 않습니다.

서버 측 렌더링을 통한 중요 CSS 비용

CSS-in-JS에 대한 중요한 CSS 추출을 위해서는 SSR이 필요합니다. SSR은 서버에서 애플리케이션의 주어진 상태에 대한 최종 HTML을 생성하는 프로세스입니다. 실제로, 그것은 매우 복잡하고 비싼 프로세스 일 수 있습니다. 각 HTTP 요청에 대해 서버에서 특정 양의 CPU주기가 필요합니다.

CSS-in-JS는 일반적으로 HTML 렌더링 파이프 라인에 연결되어 있다는 사실을 이용합니다. * HTML을 렌더링하고 필요한 CSS를 알고 최소량의 HTML을 생성 할 수 있습니다. CSS는 최종 CSS 문자열로 컴파일되어야하기 때문에 중요 CSS는 서버에서 HTML 렌더링에 추가 오버 헤드를 추가합니다. 일부 시나리오에서는 서버에서 캐시하기가 어렵거나 불가능합니다.

블랙 박스 렌더링

사용중인 CSS-in-JS 라이브러리가 CSS를 렌더링하는 방법을 알고 있어야합니다. 예를 들어 사람들은 종종 스타일 구성 요소 및 감정이 동적 스타일을 구현하는 방법을 인식하지 못합니다. 동적 스타일은 스타일 선언 내에서 JavaScript 함수를 사용할 수있는 구문입니다. 이러한 함수는 소품을 받아 CSS 블록을 반환합니다.

소스 순서 특이성을 일관되게 유지하기 위해 위에 명명 된 라이브러리 모두 동적 선언을 포함하고 새로운 소품으로 구성 요소를 업데이트하는 경우 새로운 CSS 규칙을 생성합니다. 무슨 뜻인지 설명하기 위해이 샌드 박스를 만들었습니다. JSS에서 우리는 새로운 트레이드 규칙을 생성하지 않고도 동적 속성을 업데이트 할 수있는 다른 트레이드 오프를 결정했습니다. *

가파른 학습 곡선

CSS에 익숙하지만 JavaScript에 익숙하지 않은 사람들의 경우 CSS-in-JS로 빠르게 작업하기위한 초기 작업량이 상당히 클 수 있습니다.

복잡한 논리가 관여 될 때까지 CSS-in-JS를 작성하기 위해 전문 JavaScript 개발자 일 필요는 없습니다. 사용 사례에 따라 스타일링의 복잡성을 일반화 할 수 없습니다. CSS-in-JS가 복잡 해지는 경우 바닐라 CSS를 사용한 구현은 훨씬 더 복잡 할 수 있습니다.

기본 CSS-in-JS 스타일링을 위해서는 변수를 선언하는 방법, 템플릿 문자열을 사용하는 방법 및 JavaScript 값을 보간하는 방법을 알아야합니다. 객체 표기법을 사용하는 경우 JavaScript 객체 및 라이브러리 별 객체 기반 구문으로 작업하는 방법을 알아야합니다. 동적 스타일링이 관련된 경우 JavaScript 함수 및 조건을 사용하는 방법을 알아야합니다.

전반적으로 학습 곡선이 있으며 거부 할 수 없습니다. 그러나이 학습 곡선은 일반적으로 Sass를 배우는 것보다 크게 크지 않습니다. 사실 저는 이것을 설명하기 위해이 난두 코스를 만들었습니다.

상호 운용성 없음

대부분의 CSS-in-JS 라이브러리는 상호 운용성이 없습니다. 즉, 하나의 라이브러리를 사용하여 작성된 스타일은 다른 라이브러리를 사용하여 렌더링 할 수 없습니다. 실제로는 전체 애플리케이션을 한 구현에서 다른 구현으로 쉽게 전환 할 수 없음을 의미합니다. 또한 CSS에 대한 빌드 타임 정적 추출이 없으면 선택한 CSS-JS 라이브러리를 소비자 번들로 가져 오지 않고 NPM에서 UI를 쉽게 공유 할 수 없습니다.

이 문제를 해결하기 위해 ISTF 형식으로 작업을 시작했지만 불행히도 아직 프로덕션 준비 상태로 전환 할 시간이 없었습니다. *

공용 도메인에서 재사용 가능한 프레임 워크 불가지론 적 UI 구성 요소를 공유하는 것은 여전히 ​​일반적으로 해결하기 어려운 문제라고 생각합니다.

보안 위험

CSS-in-JS로 보안 누출이 발생할 수 있습니다. 클라이언트 측 응용 프로그램과 마찬가지로 항상 렌더링하기 전에 사용자 입력을 이스케이프해야합니다.

이 기사는 더 많은 통찰력과 몇 가지 예를 보여줍니다.

읽을 수없는 클래스 이름

일부 사람들은 여전히 ​​웹에서 읽을 수있는 클래스 이름을 의미있게 유지하는 것이 중요하다고 생각합니다. 현재 많은 CSS-in-JS 라이브러리는 개발 모드에서 선언 이름 또는 구성 요소 이름을 기반으로 의미있는 클래스 이름을 제공합니다. 그들 중 일부는 클래스 이름 생성기 기능을 사용자 정의 할 수도 있습니다.

그러나 프로덕션 모드에서는 대부분 작은 페이로드에 대해 짧은 이름을 생성합니다. 이것은 라이브러리 사용자가 필요한 경우 라이브러리를 만들고 사용자 정의해야하는 절충안입니다.

결론

트레이드 오프가 존재하며 아마도 그것들을 모두 언급하지는 않았을 것입니다. 그러나 대부분 CSS에 모든 CSS에 적용되는 것은 아닙니다. 사용하는 라이브러리와 사용 방법에 따라 다릅니다.

*이 문장을 설명하기 위해 전용 기사가 필요합니다. 더 읽고 싶은 트위터에 대해 트위터 (@ oleg008)로 알려주십시오.