blog

SSR 정적 스타일 내보내기

기존의 js + css 웹사이트에서는 일반적으로 첫 번째 렌더링에 워터링만 처리하면 됩니다. CSS-in-JS 기술이 도입되면 개발자는 첫 번째 렌더링의 정확성을 보장하기 위해 스...

Oct 21, 2025 · 5 min. read
シェア
  • 제목: SSR 정적 스타일 내보내기
  • date: 2023-04-25
  • author: zombieJ

기존의 js + css 웹사이트에서는 일반적으로 첫 번째 렌더링에 워터링만 처리하면 됩니다. 그리고 CSS-in-JS 기술을 도입할 때 개발자는 첫 번째 렌더링의 정확성을 보장하기 위해 스타일을 HTML로 내보내는 방법에 더욱 주의를 기울여야 합니다. 매우 많은 구현이 제공되며 관련된 아이디어에 대해 이야기할 수 있는 좋은 기회이기도 합니다. 전체 문서나 예제가 필요하다면 참조하세요.

Inline Style

가장 쉬운 방법은 스타일을 HTML에 직접 인라인 처리하여 추가 요청이 필요하지 않도록 하는 것입니다. 이 방법의 단점은 브라우저에서 스타일을 캐시할 수 없으며 요청할 때마다 스타일을 다시 다운로드해야 한다는 것입니다. 또한 스타일이 너무 많으면 HTML 파일이 너무 커져 첫 번째 렌더링 속도에 영향을 미칩니다.

<div>
 <style>
 :where(.css-bAmBOo).ant-btn {
 // ...
 }
 </style>
 <button className="ant-btn css-bAmBOo">Hello World</button>
</div>

이것은 간단하고 효과적인 구현이며, 유일한 단점은 :n 번째 선택에 대한 스타일 오염입니다. 하지만 이 스타일을 사용하는 경우가 거의 없다는 점을 고려하면 부작용은 거의 없습니다.

처음에는 잘 작동했고, antd 웹사이트는 약간의 수정만으로 SEO 목적의 SSR 스타일을 지원했습니다. 그러나 구성 요소가 점차 CSS-in-JS 버전으로 마이그레이션됨에 따라 사이트의 빌드 제품이 방대하고 서서히 사용할 수 없게 되었다는 것이 분명해졌습니다. HTML을 살펴본 결과 기본 인라인이 제대로 작동하지 않아 페이지에 버튼이 3개 있으면 인라인이 3개가 되는 등 스타일이 기하급수적으로 인라인되는 원인이 되고 있다는 것이 분명해졌습니다:

<div>
 <style>
 :where(.css-bAmBOo).ant-btn {
 // ...
 }
 </style>
 <button className="ant-btn css-bAmBOo">Hello World 1</button>
 <style>
 :where(.css-bAmBOo).ant-btn {
 // ...
 }
 </style>
 <button className="ant-btn css-bAmBOo">Hello World 2</button>
 <style>
 :where(.css-bAmBOo).ant-btn {
 // ...
 }
 </style>
 <button className="ant-btn css-bAmBOo">Hello World 3</button>
</div>

그리고 대부분의 컴포넌트가 CSS-in-JS로 변환되면 인라인 스타일이 방대해질 것입니다. 그래서 이후 단계에서 자동 인라인을 제거하고 수동으로 수집해야 하는 양식으로 변환했습니다:

import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
import { renderToString } from 'react-dom/server';
const cache = createCache();
// HTML Content
const html = renderToString(
 <StyleProvider cache={cache}>
 <MyApp />
 </StyleProvider>,
);
// Style Content
const styleText = extractStyle(cache);

이것이 전통적인 CSS-in-JS 인젝션 구현 방식입니다. 소개에서 언급했듯이 인라인 스타일은 캐싱할 수 없기 때문에 추가적인 로딩 오버헤드가 발생합니다. 따라서 네이티브 CSS와 유사한 로딩 경험을 얻으려면 새로운 구현을 시도하는 것이 중요합니다.

Static Extract Style

v4 버전처럼 프론트엔드에서 사용할 컴포넌트 스타일을 미리 굽는 것이 가능할지 고민하다가 모든 컴포넌트를 미리 한 번 렌더링하여 캐시에서 전체 스타일을 가져온 다음 CSS 파일에 쓰는 매우 간단한 아이디어인 [RFC] 정적 추출 스타일을 생각해 냈습니다.

const cache = createCache();
// HTML Content
renderToString(
 <StyleProvider cache={cache}>
 <Button />
 <Switch />
 <Input />
 {/* Rest antd components */}
 </StyleProvider>,
);
// Style Content
const styleText = extractStyle(cache);

물론 개발자에게는 다소 번거로운 작업입니다. 그래서 이 요구 사항을 충족하기 위해 3방향 패키지를 추출했습니다:

import { extractStyle } from '@ant-design/static-style-extract';
import fs from 'fs';
// `extractStyle` containers all the antd component
// excludes popup like component which is no need in ssr: Modal, message, notification, etc.
const css = extractStyle();
fs.writeFile(...);

개발자가 하이브리드 테마를 사용하는 경우 하이브리드 요구 사항을 직접 구현할 수도 있습니다:

// `node` is the components set we prepared
const css = extractStyle((node) => (
 <>
 <ConfigProvider theme={theme1}>{node}</ConfigProvider>
 <ConfigProvider theme={theme2}>{node}</ConfigProvider>
 <ConfigProvider theme={theme3}>{node}</ConfigProvider>
 </>
));

파트 정적 추출 스타일

대부분의 경우 위의 사용법으로 충분합니다. 하지만 CSS-in-JS의 유연성과 정적 파일 캐싱의 이점을 결합하고 싶은 경우가 있습니다. 이는 인라인 스타일과 달리 원하는 콘텐츠를 렌더링하여 파일로 저장하는 방식으로 애플리케이션 수준에서 수행됩니다. 파일 캐싱은 간단한 해시로 달성할 수 있습니다:

import { createHash } from 'crypto';
// Get Style content like above
const styleText = extractStyle(cache);
const hash = createHash('md5').update(styleText).digest('hex');
const cssFileName = `css-${hash.substring(0, 8)}.css`;
if (!fs.existsSync(cssFileName)) {
 fs.writeFileSync(cssFileName, styleText);
}

그런 다음 해당 CSS 파일을 HTML 템플릿 쪽에 추가합니다:

<!doctype html>
<html>
 <head>
 <link rel="stylesheet" href="${hashCssFileUrl}" />
 </head>
 <body>
 <div id="root">${html}</div>
 </body>
</html>

전체 내용은 확인할 수 있습니다.

다른 페이지를 방문하면 해당 CSS가 생성되고 각 CSS에는 해당 해시값이 있습니다. 해시에 도달하면 CSS 파일이 삭제되어 바로 사용할 수 있습니다. 그러면 클라이언트는 캐싱 기능을 사용하는 정상적인 CSS 파일에 액세스할 수 있습니다.

해시는 서로 다른 스타일 또는 사용자 정의 테마로 같은 페이지에 액세스하는 여러 사용자를 구분하는 데 사용할 수 있습니다.

요약

이전의 정적 추출 스타일은 덜 복잡한 애플리케이션에 권장되며, 충분히 간단하지만 더 나은 액세스 속도 경험을 위해 더 세밀하게 SSR 스타일 렌더링을 제어하려는 개발자는 부분적으로 정적화하는 기능을 사용해 보세요. 위.

Read next

테스트 팀을 위한 일반 JMeter 스크립트를 작성하는 방법

일반적으로 연구, 작업 프로세스, 일부 JMeter 스크립트 작성, 나는 대부분이이 문제에 직면했다고 생각합니다. 즉, 실행할 다른 컴퓨터가 파일 경로가 동일하지 않으면 실행 실패로 이어질 수 있습니다.

Oct 21, 2025 · 4 min read