Waterfall 현상 톺아보기!💧
웹 퍼포먼스 최적화 이야기에서 종종 등장하는 “Waterfall 현상”은 네트워크 요청이 마치 폭포처럼 순차적으로 이루어지는 현상을 가리킵니다.
웹 페이지가 로드될 때, 각 자원(이미지, 스크립트, 스타일시트 등)의 요청이 발생하는 방식은 사용자 경험에 큰 영향을 미칠 수 있어요.
이것이 무조건 나쁜 건 아니지만, 잘못 관리되면 성능 저하와 사용자 경험 악화로 이어질 수 있습니다!
이 현상이 발생하는 이유는 여러 가지가 있지만, 주로 요청 간의 의존성과 브라우저의 요청 처리 방식이 원인입니다.
특히, 동기적 요청이 많거나 자원이 서로 의존적인 경우, 특정 자원이 로드될 때까지 다음 요청이 지연될 수 있습니다.
이로 인해 페이지 로딩 시간이 늘어나고, 사용자는 지루함을 느낄 수 있게되는 겁니다.
🌊 Waterfall 현상이란?
Waterfall 현상은 네트워크 요청들이 직렬(순차)로 이루어질 때 발생합니다.
브라우저는 HTML을 다운로드하고 파싱하며, 그 과정에서 CSS, JavaScript, 이미지 등의 추가 리소스를 순서대로 요청합니다.
이때, 각 요청은 병렬로 처리되지 않고, 한 요청이 완료되어야만 다음 요청이 시작되는 형태를 취합니다.
이러한 방식이 바로 “Waterfall”이라고 불립니다.
이 현상은 브라우저의 요청 처리 방식과 자원의 의존성에 크게 영향을 받습니다.
예를 들어, CSS 파일이 로드되지 않으면 해당 스타일이 적용된 HTML 요소가 제대로 표시되지 않을 수 있고, JavaScript 파일이 로드되지 않으면 스크립트에 의존하는 기능이 작동하지 않을 수 있습니다.
따라서, 이러한 의존성으로 인해 요청이 순차적으로 이루어지는 경우가 많아지게 됩니다.
Waterfall의 시각적 표현
Chrome DevTools의 “Network” 탭에서 Waterfall 형식으로 각 리소스 요청의 타임라인을 시각적으로 확인할 수 있습니다.
각 리소스가 요청된 시간과 소요된 시간을 바 그래프 형태로 보여주고, 요청 간의 관계와 병목 현상을 쉽게 파악할 수 있습니다.
❓ Waterfall 현상이 왜 발생할까?
1️⃣ HTTP/1.1의 한계
HTTP/1.1은 연결당 최대 6개의 병렬 요청을 허용합니다.
이 제한으로 인해 동시에 처리할 수 있는 요청 수가 한정되며, 사용자가 요청하는 리소스가 많아질 경우 대기열이 형성됩니다
이로 인해 요청이 순차적으로 처리되면서, 페이지 로딩 속도가 느려지게 됩니다.
특히, 많은 리소스가 필요한 현대의 웹 애플리케이션에서는 이 한계가 성능 저하의 주요 원인이 될 수 있습니다.
2️⃣ 동기적 리소스 로드
JavaScript와 같은 블로킹 리소스는 HTML 파싱을 중단시키는 특성을 가지고 있습니다.
브라우저는 JavaScript 파일을 다운로드하고 실행하기 전까지 다른 작업을 수행하지 않으며, 이로 인해 페이지 로딩이 지연될 수 있습니다.
이러한 동기적 로딩 방식은 페이지의 초기 렌더링을 방해하여 사용자 경험을 저하시킬 수 있어요!
3️⃣ 종속성 문제
특정 JavaScript 파일이 DOM이나 다른 스크립트에 의존적일 경우, 필수 리소스가 먼저 로드되지 않으면 뒤따르는 요청이 지연됩니다.
예를 들어, 특정 스크립트가 DOM 요소에 접근해야 한다면, 해당 요소가 로드되기 전까지 스크립트 실행이 불가능하므로 요청이 지연됩니다.
이처럼 자원 간의 종속성은 Waterfall 현상을 더욱 악화시킬 수 있습니다.
4️⃣ 파일 크기 및 로드 시간
큰 파일이나 최적화되지 않은 리소스는 다운로드 시간이 길어져 다음 요청을 지연시킵니다.
예를 들어, 고해상도 이미지나 대량의 데이터를 포함한 스크립트는 전송 속도를 늦추고, 이를 처리하는 동안 다른 요청이 대기하게 됩니다. 따라서, 파일 크기를 줄이고 최적화를 수행하는 것이 중요합니다.
5️⃣ DNS 및 서버 응답 지연
DNS 조회 속도와 서버의 응답 속도가 느릴 경우, 첫 번째 요청이 완료되기까지의 시간이 늘어납니다.
DNS 조회는 도메인 이름을 IP 주소로 변환하는 과정으로, 이 과정이 지연되면 웹 페이지의 초기 로드를 느리게 하는 원인이 될 수 있습니다.
또한, 서버가 요청에 대한 응답을 보내는 속도가 느리면, 전체적인 로딩 시간이 늘어나게 됩니다.
🚦 Waterfall 현상이 주는 문제점
1️⃣ 렌더링 지연
Waterfall 현상으로 인해 사용자가 웹페이지를 볼 수 있는 시간이 길어집니다.
이는 특히 First Contentful Paint(FCP)와 Time to Interactive(TTI)와 같은 중요한 성능 지표에 부정적인 영향을 미칩니다.
(FCP는 사용자가 첫 번째 콘텐츠를 시각적으로 확인할 수 있는 시간을 나타내며, TTI는 페이지가 완전히 상호작용 가능해지는 시간을 의미합니다.)
2️⃣ 페이지 로딩 속도 저하
대기 중인 요청이 많아질수록 전체 페이지 로딩 시간이 늘어나게 됩니다. 이는 특히 제한된 환경인 모바일 환경에서 더욱 두드러집니다.
3️⃣ 브라우저 리소스 낭비
브라우저는 한 번에 처리할 수 있는 작업이 제한되어 있기 때문에, Waterfall 현상으로 인해 효율적으로 리소스를 활용하지 못하게 됩니다.
네트워크 요청이 병렬로 이루어지지 않으면, 브라우저는 대기 중인 요청을 처리하는 데 필요한 리소스를 낭비하게 됩니다.
이로 인해 CPU와 메모리와 같은 시스템 자원이 비효율적으로 사용되며, 전체적인 성능이 저하될 수 있습니다.
또한, 이러한 리소스 낭비는 2와 마찬가지로 모바일 기기에서 더 심각한 영향을 받습니다.
🛠 Waterfall 현상을 해결하는 방법
1️⃣ HTTP/2나 HTTP/3로 업그레이드
- HTTP/2는 멀티플렉싱을 지원하여 하나의 연결에서 여러 요청을 동시에 처리할 수 있습니다. 병렬 요청이 가능해져 Waterfall 현상을 크게 줄일 수 있습니다.
- HTTP/3는 UDP 기반의 전송 프로토콜로, 더 빠르고 안정적인 연결을 제공해 성능을 크게 향상시킵니다. 이러한 프로토콜로의 업그레이드는 웹 페이지의 로딩 속도를 개선하고, 사용자 경험을 향상시키는 데 큰 도움이 될 수 있습니다.
2️⃣ JavaScript 비동기 로드
JavaScript를 비동기적으로 로드하는 방법은 두 가지가 있습니다:
async
- 스크립트가 다운로드되는 즉시 실행되며, HTML 파싱을 중단하지 않습니다.
- (페이지가 로드되는 동안 다른 작업을 할 수 있음)
defer
- HTML 파싱이 완료된 후 JavaScript를 실행합니다.
- (DOM이 완전히 로드된 후에 스크립트가 실행되어, 페이지의 렌더링에 영향 ❌)
<script src="script.js" async></script>
<script src="script.js" defer></script>
3️⃣ CSS와 JavaScript 최적화
- 사용하지 않는 CSS를 제거해 파일 크키를 줄이면 렌더링 성능이 올라갑니다.
- JavaScript와 CSS를 압축(minify)하여 파일 크기 축소하면 로딩 속도가 올라갑니다.
- 중요한 CSS는 HTML에 인라인으로 삽입해 Critical CSS 전략 적하면 페이지 초기 로딩 시간이 단축됩니다.
4️⃣ 리소스 병합 및 코드 스플리팅
- 여러 개의 작은 CSS 및 JavaScript 파일을 하나로 병합하여 요청 수를 줄이기
- 코드 스플리팅으로 사용자가 필요할 때만 특정 파일을 로드하도록 설정하면, 초기 로딩 시간⬇️ 성능⬆️ !
5️⃣ Lazy Loading
이미지, 비디오 등 무거운 리소스를 사용자가 필요로 할 때만 로드하도록 설정하면 초기 로딩 시 불필요한 리소스를 줄일 수 있습니다.
<img src="image.jpg" loading="lazy" alt="Lazy-loaded image">
6️⃣ 캐싱 활용
CDN(Content Delivery Network)을 사용하여 정적 리소스를 캐싱하면, 사용자에게 더 가까운 서버에서 리소스를 제공할 수 있어 로딩 속도가 올라갑니다.
또한, 브라우저 캐싱 설정을 활용하여 중복 다운로드를 방지하고, 사용자 경험을 개선할 수 있답니다.
Cache-Control: max-age=31536000
(이 설정은 리소스가 1년 동안 캐시될 수 있도록 하여, 동일한 리소스를 다시 요청할 필요가 없도록 한 예시입니다.)
💡 실전 예제: React 애플리케이션 최적화
Before: Waterfall 현상 발생
import React, { useEffect, useState } from 'react';
function App() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data')
.then((res) => res.json())
.then((result) => setData(result));
}, []);
if (!data) return <p>Loading...</p>;
return <div>{data.message}</div>;
}
export default App;
👉 JavaScript와 API 요청이 순차적으로 이루어져 렌더링 지연 발생.
After: Waterfall 해결
import React, { Suspense } from 'react';
const dataFetcher = fetch('/api/data').then((res) => res.json());
const DataComponent = () => {
const data = React.use(dataFetcher); // Suspense로 관리
return <div>{data.message}</div>;
};
function App() {
return (
<Suspense fallback={<p>Loading...</p>}>
<DataComponent />
</Suspense>
);
}
export default App;
👉 React Suspense를 사용하여 API 요청과 렌더링을 병렬로 수행.
🌟 마무리: Waterfall 현상, 해결은 가능하다!
Waterfall 현상은 브라우저와 네트워크 요청 간의 구조적 한계로 발생하지만, 이를 이해하고 최적화 기술을 적용하면 해결할 수 있답니다.
브라우저의 DevTools에서 네트워크 요청을 분석하며, 위의 전략들을 단계적으로 적용해보세용!
🌷전설의 개발자가 되어봅시당! 🌷
'Web' 카테고리의 다른 글
쿼리 스트링(Query String): 웹의 데이터 전달자 완벽 분석 (0) | 2025.02.14 |
---|---|
Full Reload와 관련된 비슷한 용어 정리 🌐 (1) | 2025.02.11 |
[WEB] Content-Type: 웹 개발자가 꼭 알아야 할 모든 것! 📚 (1) | 2025.01.19 |
[WEB] Preflight 요청: 브라우저의 안전장치 🛡️ (1) | 2025.01.18 |
[HTTP] HTTP Content Negotiation: 서버와 클라이언트의 대화 기술 🗣️ (0) | 2025.01.12 |