반응형

웹페이지를 열었을 때 잠깐 기본 스타일로 보이다가, 곧바로 디자인이 바뀌는 장면을 본 적이 있을 겁니다. 버튼 크기가 달라지고, 배경색이 바뀌고, 글꼴이나 레이아웃이 뒤늦게 정리되는 현상입니다.

이런 현상을 보통 FOUC, 즉 Flash of Unstyled Content라고 부릅니다. 말 그대로 스타일이 적용되지 않은 콘텐츠가 순간적으로 보이는 문제입니다.

단순히 “화면이 예쁘지 않다”의 문제가 아닙니다. 브라우저가 첫 화면을 그리기 전에 필요한 스타일을 제때 확보하지 못했고, 그 중간 상태가 사용자에게 노출됐다는 뜻입니다.

브라우저는 어떻게 첫 화면을 그릴까

브라우저는 HTML을 받으면 먼저 문서를 파싱해서 DOM을 만듭니다. 동시에 CSS를 읽어 CSSOM을 구성합니다. 이후 DOM과 CSSOM을 조합해 렌더 트리를 만들고, 레이아웃 계산과 페인트 과정을 거쳐 화면을 보여줍니다.

문제는 CSS가 화면 표시와 매우 밀접하게 연결되어 있다는 점입니다. 브라우저는 스타일 정보 없이 최종 화면을 정확히 계산할 수 없습니다. 그래서 중요한 CSS가 늦게 도착하면 첫 페인트가 늦어지거나, 일단 보인 뒤 다시 그려지는 일이 생깁니다.

FOUC는 바로 이 지점에서 발생합니다.

FOUC가 생기는 대표적인 이유

FOUC의 핵심 원인은 스타일 확정 시점이 늦어지는 것입니다. 특히 다음과 같은 경우에 자주 발생합니다.

첫째, CSS를 늦게 발견하는 구조입니다. 예를 들어 CSS 안에서 다시 다른 CSS를 불러오는 @import를 사용하면, 브라우저가 HTML을 읽는 단계에서 모든 스타일 의존성을 바로 알기 어렵습니다. CSS 파일을 받은 뒤에야 다음 CSS를 발견하는 식으로 연결되면, 첫 화면에 필요한 스타일 적용이 순차적으로 밀릴 수 있습니다.

둘째, 동기 JavaScript가 HTML 파싱을 막는 경우입니다. 일반적인 <script>는 실행되는 동안 HTML 파싱을 멈출 수 있습니다. 그러면 뒤쪽 DOM이나 리소스 발견도 늦어지고, 결과적으로 첫 화면 준비가 지연될 수 있습니다.

셋째, 첫 화면에 필요하지 않은 리소스까지 초반에 많이 싣는 경우입니다. 실제로는 나중에 필요한 스타일과 스크립트인데도 모두 앞단에서 처리하면, 중요한 렌더링 경로가 불필요하게 길어집니다.

@import보다 link가 유리한 이유

CSS를 불러오는 방법에는 여러 가지가 있지만, 첫 화면에 꼭 필요한 스타일이라면 보통 HTML의 <link rel="stylesheet">가 더 유리합니다.

<link rel="stylesheet" href="./critical.css" />

이 방식은 브라우저가 HTML을 파싱하는 초기에 스타일시트를 발견할 수 있습니다. 반면 @import는 CSS 파일 내부에서 또 다른 CSS를 불러오는 구조라, 의존성 발견이 한 단계 늦어질 수 있습니다.

즉 차이는 단순히 문법 취향이 아닙니다. 브라우저가 “무엇을 빨리 알아차릴 수 있느냐”의 차이입니다.

JavaScript는 defer로 파싱 방해를 줄인다

JavaScript도 렌더링에 영향을 줍니다. 특히 동기 스크립트는 HTML 파싱을 멈추게 만들 수 있기 때문에, 초기 렌더링 흐름을 방해할 수 있습니다.

이럴 때 자주 사용하는 선택지가 defer입니다.

<script defer src="./app.js"></script>

defer가 붙은 스크립트는 HTML 파싱을 막지 않고 다운로드됩니다. 실행은 문서 파싱이 끝난 뒤에 이루어집니다. 그래서 초기 HTML 구조를 만드는 흐름을 덜 방해합니다.

다만 defer가 모든 렌더링 문제를 해결해 주는 것은 아닙니다. 핵심은 “이 스크립트가 첫 화면 전에 반드시 실행되어야 하는가”를 판단하는 것입니다. 필요하지 않다면 뒤로 미루고, 꼭 필요하다면 크기를 줄여야 합니다.

예제 코드로 보는 FOUC 감각

이번 실습 파일의 index.html은 1초 뒤에 ready 클래스를 붙여 화면 스타일이 바뀌는 모습을 보여줍니다.

<script>
  window.addEventListener('DOMContentLoaded', () => {
    setTimeout(() => {
      document.documentElement.classList.add('ready');
    }, 1000);
  });
</script>

처음에는 흰색 카드 형태로 보이다가, 1초 뒤에는 어두운 그라디언트 배경과 흰색 글자로 바뀝니다. 실제 서비스에서 CSS가 늦게 적용되면 이와 비슷하게 사용자가 중간 상태를 보게 됩니다.

실무에서는 이런 변화가 의도된 애니메이션인지, 아니면 스타일 로딩 지연으로 생긴 흔들림인지 구분해야 합니다.

실무에서 점검할 것

FOUC를 줄이려면 먼저 첫 화면 기준으로 리소스를 나누는 습관이 필요합니다.

첫 화면에 꼭 필요한 CSS는 최대한 일찍 로드합니다. 가능하다면 critical CSS로 분리하고, 불필요한 스타일은 뒤로 미룹니다.

CSS 안에서 이어지는 @import 체인은 피하는 편이 좋습니다. 번들링 과정에서 합치거나, HTML에서 link로 명확히 선언하는 구조가 더 예측 가능합니다.

JavaScript는 기본적으로 defer나 모듈 스크립트를 고려합니다. 꼭 초기에 실행되어야 하는 코드가 아니라면 HTML 파싱을 막지 않도록 배치하는 것이 좋습니다.

그리고 DevTools의 Network, Performance 탭을 통해 CSS와 JS가 언제 발견되고, 언제 다운로드되며, 첫 페인트 전에 어떤 리소스가 필요한지 확인해야 합니다.

정리

FOUC는 단순한 시각적 깜빡임이 아니라, 브라우저의 렌더링 준비가 끝나기 전에 사용자에게 중간 상태가 노출되는 문제입니다.

  1. 첫 화면에 필요한 CSS는 빨리 발견되게 한다.
  2. @import처럼 의존성 발견이 늦어지는 구조를 조심한다.
  3. 초기 실행이 필요 없는 JavaScript는 defer 등으로 파싱 방해를 줄인다.

웹 성능을 볼 때는 “파일이 작다”만으로 충분하지 않습니다. 브라우저가 그 파일을 언제 발견하고, 언제 적용할 수 있는지가 더 중요합니다.

참고 자료

반응형