최근 사내에서 개발한 UI패키지를 사용하여 퍼블리싱을 진행하는 과정에서 새로고침시 스타일 지연이 많이 발생하는 문제가 있어. 이를 해결해본 경험을 공유해보고자 한다.
예시처럼 화면이 로드된 초기에 스타일 적용이 지연되면서 다소 얼빠진(?) 화면이 보이다가 의도한 화면이 그려지는 것을 확인할 수 있다. 문제의 원인은 Rollup 설정에 있었다.
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
typescript(),
postcss({
extensions: [".css"],
plugins: [tailwindcss, autoprefixer],
extract: false,
}),
CSS 인라인 포함 vs CSS 파일 추출
우선 Rollup은 자바스크립트 모듈을 번들링하는 번들러로서, 여기서 PostCSS는 롤업(Rollup)에서 CSS를 처리하기 위한 플러그인 중 하나이다. 내가 개발한 라이브러리는 tailwind로 개발이 되었기 때문에 postcss와 같은 플러그인으로 최적화하는 작업이 필요했다.
PostCSS에는 패키지에 적용될 스타일을 별도의 CSS파일로 추출하여 저장할지 JS에 인라인으로 넣을지 선택할 수 있는 옵션이 있는데, 이게 extract: true/false 옵션이다. 말 그대로 extract 옵션을 true로 주게 되면 빌드 과정 중 CSS가 별도의 파일로 생성되고, extract false로 설정하면 CSS가 자바스크립트에 포함된다.
브라우저 랜더링 과정을 잘 알아야하는 이유
브라우저는 HTML → CSS → 자바스크립트 순으로 리소스를 로드하고 렌더링하는데, 문제는 CSS가 인라인으로 포함된다면, 특히 CSS의 양이 많다면 자바스크립트 로딩이 끝나기 전가지 스타일이 적용되지 않는다는 것이다. 이것 때문에 layout shift, 또는 FOUC(Flash of Unstyled Content) 같은 문제가 발생할 수 있는 것이다.
SSR과 CSR의 환경의 차이를 잘 이해해야 한다
물론 이것이 항상 문제가 되는 것은 아니다. 예를 들어, 테스트를 위해 Vite 개발 환경에서 패키지를 다운로드하고 테스트해봤을 때는 성능이 준수했다. 이는 Vite가 무지 빠르기 때문이기도 하지만, CSR(클라이언트사이드 랜더링) 특성상 초기 HTML이 비어있고 자바스크립트가 모든 DOM을 생성하면서 스타일을 동시에 적용하기 때문이다. 즉, CSR 방식에서는 자바스크립트가 DOM을 동적으로 생성하는 동시에 인라인된 CSS를 함께 적용할 수 있기 때문에 레이아웃 시프트같은 문제가 발생할 가능성이 적었던 것이다.
하지만 SSR(서버사이드 랜더링) 환경에서는 상황이 좀 다르다. 현재 사내 프로젝트에서는 Next.js를 사용해 개발을 진행 중인데, SSR 환경에서는 HTML이 서버에서 먼저 렌더링되고, 그 후 자바스크립트가 로드되어 클라이언트 측에서 하이드레이션(hydration) 과정이 진행된다.
만약 이때 CSS가 자바스크립트에 포함되어 있다면, 하이드레이션이 완료되기 전까지 스타일이 적용되지 않아 초기 HTML이 스타일이 없는 상태로 랜더링된다. 이로 인해 화면이 깜빡이는 FOUC(Flash of Unstyled Content)와 스타일 지연 문제가 발생할 수 있는 것이다.
결론: 언제 인라인 CSS를 사용하는 것이 유리할까?
- 프로젝트의 규모가 작거나 CSS 양이 매우 적은 경우에는 패키지의 인라인 스타일을 사용하는 것이 오히려 초기 로딩 속도를 개선하는게 좋을 수 있다. 특히 클라이언트 사이드에서 자바스크립트 제어 하에 있는 스타일을 사용할 때 유리할 수 있다.
- 그러나 규모가 큰 프로젝트나 많은 양의 CSS가 필요한 경우, CSS를 별도로 추출(extract: true)하여 브라우저가 이를 캐싱하고 빠르게 로드하도록 하는 것이 스타일 지연 문제나 초기 로딩 속도 개선에 더 나은 선택일 수 있다.
'프론트엔드' 카테고리의 다른 글
[프론트엔드] dvh (CSS 단위) (0) | 2024.11.22 |
---|---|
[프론트엔드] SSR/CSR 환경별 쿠키 관리 방법 (0) | 2024.11.04 |
[프론트엔드] 왜 Vite는 Webpack보다 훨씬 빠를까? (0) | 2024.09.04 |
[프론트엔드] 리액트에서 상태관리는 어떻게 이루어질까? (Feat: useState) (2) | 2024.09.02 |
[프론트엔드] UseRef (Feat. DOM조작) (0) | 2024.08.12 |