현대 웹 환경에서 사용자들은 페이지 로딩이 느리거나, 화면 전환이 매끄럽지 않다면 사용자는 몇 초만에 웹페이지를 이탈할 가능성이 높아진다. 실제로 Google에 따르면 로딩 시간이 1초에서 3초로 증가할 경우 이탈률이 32% 증가한다고 한다. 이처럼 성능은 사용자 경험을 좌우하는 요소이며, 성공적인 웹 서비스를 구축하는데 필수적인 요소이다.
성능 최적화를 위해서는 무엇이 중요한지 명확히 이해해야 한다. 단순히 코드를 최적화하거나 이미지를 압축하는 것만으로는 충분하지 않다. 현대 웹에서는 “핵심 웹 지표”를 기반으로 사용자 경험을 측정하고 개선하는 접근이 필요하다. 이 지표들은 페이지 로드 속도부터 사용자 인터페이스와의 상호작용 속도까지 다양한 요소를 표괄하며 서비스의 성능을 데이터로 이해하고 최적화 방향을 설정하는데 중요한 기준이 된다.
이번 포스팅에서는 웹 프론트엔드 성능 최적화의 기초가 되는 주요 웹 지표를 소개하고 이를 어떻게 활용하여 사용자 경험을 개선할 수 있는지에 대해 알아보고자 한다.
01. 핵심 웹 지표란?
핵심 웹 지표(Core Web Vital)란 구글에서 만든 지표로, 웹사이트에서 뛰어난 사용자 경험을 제공하는데 필수적인 지표를 일컫는 용어이다. 구글에서 핵심 웹 지표로 꼽는 지표는 다음과 같다.
최대 콘텐츠풀 페인트(LCP: Largest Contentful Paint)
최초 입력 지연(FID: First Input Delay)
누적 레이아웃 이동(CLS: Cumulative Layout Shift)
그리고 다음 두 지표는 핵심까지는 아니지만 특정 문제를 진단하는데 사용될 수 있다.
최초 바이트까지의 시간(TTFB: Time To First Byte)
최초 콘텐츠풀 시간(FCP: First Contentful Paint)
02. LCP (Largest Contentful Paint) : 최대 콘텐츠풀 페인트
LCP는 페이지가 처음으로 로드를 시작한 시점부터 뷰포트 내부에서 가장 큰 이미지 또는 텍스트를 렌더링하는데 걸리는 시간을 의미한다.
Google은 LCP를 기준으로 성능을 3가지로 분류한다.
2.5초 이내: 좋음
2.5초 ~ 4초: 보통
4초 이상: 나쁨
💡 LCP를 개선하기 위한 방법
◼︎ 텍스트를 우선적으로 사용하기
텍스트는 이미지보다 빠르게 렌더링되므로 가능하다면 주요 콘텐츠를 텍스트로 제공하는 것이 좋다.
◼︎ 이미지 로딩 방식 최적화
img: 브라우저의 프리로드(preload) 스캐너에 의해 빠르게 요청. 가장 권장되는 방식
svg 내부 image: 프리로드되지 않아 병렬 다운로드가 불가능하므로 사용하지 않는 것이 좋음
video.poster: 프리로드가 가능하여 성능이 좋음
background-image: CSS에 정의된 리소스는 요청이 지연되므로 피하는 것이 좋음
◼︎ 이미지 무손실 압축
무손실 압축을 통해 최소한의 용량으로 이미지를 제공하는 것이 좋다.
◼︎ loading=lazy 속성 주의
loading=lazy는 리소스를 중요하지 않음으로 표시하고 필요할 때만 로드하는 전략으로 <img>, <iframe> 등에 적용할 수 있지만 최대 콘텐츠풀 콘텐츠의 이미지는 중요하지 않은 리소스로 분류해서는 안된다는 것이다. 이는 그저 로딩 속도만 늦출 뿐 지표 점수에는 도움이 되지 않는다. 상대적으로 중요하지 않은 이미지에서는 사용해도 좋지만 최대 콘텐츠풀 콘텐츠의 이미지에는 사용하지 않는 것이 좋다.
◼︎ fadein과 같은 각종 애니메이션 자제
이미지가 그냥 뜨는 것 보다 fadeIn ease 10s와 같이 처리한다면 그만큼 LCP 시간도 증가한다.
◼︎ 클라이언트 사이즈 렌더링 최소화
클라이언트에서 데이터를 로드한 후 이미지를 렌더링하는 방식은 LCP를 지연시킨다. 서버에서 데이터를 미리 렌더링하는 SSR을 활용하는 것이 좋다.
◼︎ LCP 리소스 직접 호스팅
LCP 리소스를 같은 도메인에서 제공하는 것이다. 다른 출처에서 이미지를 가져오면 새로운 네트워크 연결이 필요해 LCP 성능에 부정적인 영향을 미칠 수 있다.
03. FID (First Input Delay) : 최초 입력 지연
FID는 사용자가 웹페이지와 처음 상호작용을 시도한 순간부터, 브라우저가 해당 상호작용에 대한 이벤트 핸들러를 실제로 실행하기 시작할 때까지의 지연 시간을 측정하는 지표이다. 이 지표는 사용자의 초기 입력 경험과 직접적으로 연관되며, 페이지가 얼마나 빠르게 응답하는지 판단하는데 핵심적인 역할을 한다.
만약 웹페이지의 응답성이 떨어지면 사용자는 클릭이나 입력에 대해 즉각적인 피드백을 받지 못하고 이는 부정적인 사용자 경험으로 이어질 수 있다. 특히, 메인 스레드가 다른 작업(대규모 렌더링, JS 분석 및 실행 등)에 바쁘게 점유될 경우 사용자 입력에 대한 처리가 지연되면서 FID 시간이 길어질 수 있다.
Google이 제시한 FID 기준은 다음과 같다.
100ms 이하: 좋음
100ms ~ 300ms: 보통
300ms 이상: 나쁨
🎯 FID와 RAIL 모델
Google의 RAIL 모델은 사용자 경험을 Response(응답성), Animation(애니메이션), Idle(유휴 상태), Load(로드)로 나눈다. 이 중 Response(응답성)에 해당하는 부분이 바로 FID와 관련 있다.
Response: 사용자 입력에 50ms 미만으로 반응해야 함
Animation: 각 프레임을 10ms 이내에 생성
Idle: 유휴 상태에서 50ms 이내에 작업을 완료
Load: 5초 이내에 인터랙션이 가능한 상태를 준비
💡FID를 개선하기 위한 방법
◼︎ 메인 스레드 작업 최적화
긴 작업을 분리하여 작업 부담을 줄여야 한다. 긴 작업은 작은 단위로 나누어 처리 시간을 최소화해야 한다.
◼︎JavaScript 코드 최소화
사용하지 않는 JavaScript를 제거하면 FID를 개선할 수 있다.
크롬 개발자 도구의 Coverage 탭을 활용해 비사용 코드량을 확인할 수 있다. 폴리필 사용시, 필요없는 코드가 포함되지 않도록 해야 한다. Next.js의 SWC를 사용하고 있다면 이미 SWC 내부에 구현되어 있기 때문에 별도로 처리하지 않아도 될 것이다.
◼︎타사 JavaScript 코드 실행의 지연
Google Analytics나 Firebase와 같이 웹 페이지의 통계 집계를 위해 제3자가 만든 타사 스크립트를 넣는 경우가 많다. 이러한 타사 자바스크립트 코드는 통계를 구축하는데 중요한 부분이긴 하지만 이 코드의 실행으로 인해 메인 스레드가 잠시 점유되고, 사용자에게 안좋은 반응성을 제공할 수 있다.
이러한 타사 스크립트는 대부분 웹페이지 로드에 중요한 자원이 아니므로 <script>의 async와 defer를 이용해 지연 불러오기를 하는 것이 좋다.
defer: 병렬로 다운로드하고 페이지 로딩 이후 실행
async: 병렬로 다운로드 후, 준비되면 즉시 실행
권장 순서: defer → async → 없음
🔥 FID의 한계점
2020년 FID는 Web Core Vitals의 3대 지표로 도입되었지만, 다음과 같은 한계가 있었다.
첫 번째 입력값만 반영:
페이지 전체의 입력 반응성을 고려하지 못함.
응답성 평가 범위 부족:
전체 상호작용(클릭, 탭, 키 입력)의 지연 시간을 포괄하지 못함.
🎯 INP(Interaction to Next Paint): 새로운 지표의 도입
2024년 3월부터 Google은 FID를 대체하는 INP (Interaction to Next Paint) 지표를 도입하였다.
INP는 사용자가 페이지를 방문한 전체 기간 동안 발생하는 모든 상호작용(클릭, 탭, 키 입력)의 응답성을 측정하는 지표이다.
FID와 달리, 첫 번째 입력값뿐 아니라 전체 상호작용 반응성을 평가하여 사용자 경험을 더 정확히 반영한다.
측정 대상: 클릭, 터치, 키 입력과 같은 사용자 상호작용.
측정 범위: 이벤트 입력 지연뿐만 아니라 엔드투엔드 반응 시간까지 포함.
기준:
200ms 이하: 우수
200ms 초과: 개선 필요
FID는 사용자 첫인상의 중요성을 반영한 지표였지만, INP는 전체 상호작용 경험을 평가함으로써 성능 지표의 신뢰성을 높인다.
INP 도입에 대비해, 메인 스레드 최적화와 타사 코드 처리 방식을 점검하여 성능을 향상시키는 것이 중요하다.
04. CLS (Cumulative Layout Shift) : 누적 레이아웃 이동
CLS는 웹페이지의 예기치 않은 레이아웃 이동이 사용자 경험에 미치는 영향을 측정하는 지표이다. 이는 페이지 로드 동안 발생하는 모든 시각적 이동을 합산하여 계산된다.
CLS 점수는 아래 두 가지 요소를 곱하여 계산한다.
영향분율(Impact Fraction) : 레이아웃 이동이 영향을 미친 요소의 높이와 뷰포트 높이의 비율
거리분율(Distance Fraction) : 레이아웃 이동으로 인해 요소가 이동한 거리의 뷰포트 대비 비율
CLS 기준은 다음과 같다.
0.1 이하: 좋음
0.1 ~ 0.25: 보통
0.25 이상: 나쁨
💡CLS를 개선하기 위한 방법
◼︎ 삽입이 예상되는 요소를 위한 추가적인 공간 확보
useEffect의 내부에서 요소에 영향을 미치는 작업, 뷰포트 내부에서 노출된 확률이 높은 작업을 최소하는 것이 좋다. useEffect 사용이 불가피하다면 useLayoutEffect 훅을 사용하는 것도 좋다. useEffect는 DOM 업데이트 이후 실행되므로, 레이아웃 변경이 화면에 표시된 후 이동이 발생할 수 있다. 반면 useLayoutEffect는 DOM 업데이트 전에 실행되므로 이동이 사용자에게 표시되기 전에 처리된다.
또는 스켈리톤 UI로 미리 무언가가 동적으로 뜰 것으로 예상되는 공간을 미리 확보해두는 것도 좋은 방안이다.
하지만 가장 좋은 방안은 SSR로, 클라이언트 렌더링 전에 완성된 HTML을 전달하여 초기 콘텐츠 로딩 속도를 높이고 레이아웃 이동을 최소화할 수 있다.
◼︎ 폰트 로딩 최적화
폰트를 로딩하는 과정에서 다음과 같은 2가지 문제가 발생할 수 있다.
FOUT (Flash of Unstyled Text): 기본 폰트가 표시되었다가 나중에 지정된 폰트로 바뀌는 현상.
FOIT (Flash of Invisible Text): 폰트가 로드될 때까지 텍스트가 보이지 않는 현상.
웹 프론트엔드 성능 최적화에 알아야 할 핵심 웹 지표
현대 웹 환경에서 사용자들은 페이지 로딩이 느리거나, 화면 전환이 매끄럽지 않다면 사용자는 몇 초만에 웹페이지를 이탈할 가능성이 높아진다. 실제로 Google에 따르면 로딩 시간이 1초에서 3초로 증가할 경우 이탈률이 32% 증가한다고 한다. 이처럼 성능은 사용자 경험을 좌우하는 요소이며, 성공적인 웹 서비스를 구축하는데 필수적인 요소이다.
성능 최적화를 위해서는 무엇이 중요한지 명확히 이해해야 한다. 단순히 코드를 최적화하거나 이미지를 압축하는 것만으로는 충분하지 않다. 현대 웹에서는 “핵심 웹 지표”를 기반으로 사용자 경험을 측정하고 개선하는 접근이 필요하다. 이 지표들은 페이지 로드 속도부터 사용자 인터페이스와의 상호작용 속도까지 다양한 요소를 표괄하며 서비스의 성능을 데이터로 이해하고 최적화 방향을 설정하는데 중요한 기준이 된다.
이번 포스팅에서는 웹 프론트엔드 성능 최적화의 기초가 되는 주요 웹 지표를 소개하고 이를 어떻게 활용하여 사용자 경험을 개선할 수 있는지에 대해 알아보고자 한다.
01. 핵심 웹 지표란?
핵심 웹 지표(Core Web Vital)란 구글에서 만든 지표로, 웹사이트에서 뛰어난 사용자 경험을 제공하는데 필수적인 지표를 일컫는 용어이다. 구글에서 핵심 웹 지표로 꼽는 지표는 다음과 같다.
그리고 다음 두 지표는 핵심까지는 아니지만 특정 문제를 진단하는데 사용될 수 있다.
02. LCP (Largest Contentful Paint) : 최대 콘텐츠풀 페인트
LCP는 페이지가 처음으로 로드를 시작한 시점부터 뷰포트 내부에서 가장 큰 이미지 또는 텍스트를 렌더링하는데 걸리는 시간을 의미한다.
Google은 LCP를 기준으로 성능을 3가지로 분류한다.
💡 LCP를 개선하기 위한 방법
◼︎ 텍스트를 우선적으로 사용하기
텍스트는 이미지보다 빠르게 렌더링되므로 가능하다면 주요 콘텐츠를 텍스트로 제공하는 것이 좋다.
◼︎ 이미지 로딩 방식 최적화
◼︎ 이미지 무손실 압축
무손실 압축을 통해 최소한의 용량으로 이미지를 제공하는 것이 좋다.
◼︎ loading=lazy 속성 주의
loading=lazy는 리소스를 중요하지 않음으로 표시하고 필요할 때만 로드하는 전략으로 <img>, <iframe> 등에 적용할 수 있지만 최대 콘텐츠풀 콘텐츠의 이미지는 중요하지 않은 리소스로 분류해서는 안된다는 것이다. 이는 그저 로딩 속도만 늦출 뿐 지표 점수에는 도움이 되지 않는다. 상대적으로 중요하지 않은 이미지에서는 사용해도 좋지만 최대 콘텐츠풀 콘텐츠의 이미지에는 사용하지 않는 것이 좋다.
◼︎ fadein과 같은 각종 애니메이션 자제
이미지가 그냥 뜨는 것 보다 fadeIn ease 10s와 같이 처리한다면 그만큼 LCP 시간도 증가한다.
◼︎ 클라이언트 사이즈 렌더링 최소화
클라이언트에서 데이터를 로드한 후 이미지를 렌더링하는 방식은 LCP를 지연시킨다. 서버에서 데이터를 미리 렌더링하는 SSR을 활용하는 것이 좋다.
◼︎ LCP 리소스 직접 호스팅
LCP 리소스를 같은 도메인에서 제공하는 것이다. 다른 출처에서 이미지를 가져오면 새로운 네트워크 연결이 필요해 LCP 성능에 부정적인 영향을 미칠 수 있다.
03. FID (First Input Delay) : 최초 입력 지연
FID는 사용자가 웹페이지와 처음 상호작용을 시도한 순간부터, 브라우저가 해당 상호작용에 대한 이벤트 핸들러를 실제로 실행하기 시작할 때까지의 지연 시간을 측정하는 지표이다. 이 지표는 사용자의 초기 입력 경험과 직접적으로 연관되며, 페이지가 얼마나 빠르게 응답하는지 판단하는데 핵심적인 역할을 한다.
만약 웹페이지의 응답성이 떨어지면 사용자는 클릭이나 입력에 대해 즉각적인 피드백을 받지 못하고 이는 부정적인 사용자 경험으로 이어질 수 있다. 특히, 메인 스레드가 다른 작업(대규모 렌더링, JS 분석 및 실행 등)에 바쁘게 점유될 경우 사용자 입력에 대한 처리가 지연되면서 FID 시간이 길어질 수 있다.
Google이 제시한 FID 기준은 다음과 같다.
🎯 FID와 RAIL 모델
Google의 RAIL 모델은 사용자 경험을 Response(응답성), Animation(애니메이션), Idle(유휴 상태), Load(로드)로 나눈다. 이 중 Response(응답성)에 해당하는 부분이 바로 FID와 관련 있다.
💡FID를 개선하기 위한 방법
◼︎ 메인 스레드 작업 최적화
긴 작업을 분리하여 작업 부담을 줄여야 한다. 긴 작업은 작은 단위로 나누어 처리 시간을 최소화해야 한다.
◼︎ JavaScript 코드 최소화
사용하지 않는 JavaScript를 제거하면 FID를 개선할 수 있다.
크롬 개발자 도구의 Coverage 탭을 활용해 비사용 코드량을 확인할 수 있다. 폴리필 사용시, 필요없는 코드가 포함되지 않도록 해야 한다. Next.js의 SWC를 사용하고 있다면 이미 SWC 내부에 구현되어 있기 때문에 별도로 처리하지 않아도 될 것이다.
◼︎ 타사 JavaScript 코드 실행의 지연
Google Analytics나 Firebase와 같이 웹 페이지의 통계 집계를 위해 제3자가 만든 타사 스크립트를 넣는 경우가 많다. 이러한 타사 자바스크립트 코드는 통계를 구축하는데 중요한 부분이긴 하지만 이 코드의 실행으로 인해 메인 스레드가 잠시 점유되고, 사용자에게 안좋은 반응성을 제공할 수 있다.
이러한 타사 스크립트는 대부분 웹페이지 로드에 중요한 자원이 아니므로 <script>의 async와 defer를 이용해 지연 불러오기를 하는 것이 좋다.
권장 순서: defer → async → 없음
🔥 FID의 한계점
2020년 FID는 Web Core Vitals의 3대 지표로 도입되었지만, 다음과 같은 한계가 있었다.
🎯 INP(Interaction to Next Paint): 새로운 지표의 도입
2024년 3월부터 Google은 FID를 대체하는 INP (Interaction to Next Paint) 지표를 도입하였다.
INP는 사용자가 페이지를 방문한 전체 기간 동안 발생하는 모든 상호작용(클릭, 탭, 키 입력)의 응답성을 측정하는 지표이다.
FID와 달리, 첫 번째 입력값뿐 아니라 전체 상호작용 반응성을 평가하여 사용자 경험을 더 정확히 반영한다.
FID는 사용자 첫인상의 중요성을 반영한 지표였지만, INP는 전체 상호작용 경험을 평가함으로써 성능 지표의 신뢰성을 높인다.
INP 도입에 대비해, 메인 스레드 최적화와 타사 코드 처리 방식을 점검하여 성능을 향상시키는 것이 중요하다.
04. CLS (Cumulative Layout Shift) : 누적 레이아웃 이동
CLS는 웹페이지의 예기치 않은 레이아웃 이동이 사용자 경험에 미치는 영향을 측정하는 지표이다. 이는 페이지 로드 동안 발생하는 모든 시각적 이동을 합산하여 계산된다.
CLS 점수는 아래 두 가지 요소를 곱하여 계산한다.
CLS 기준은 다음과 같다.
💡CLS를 개선하기 위한 방법
◼︎ 삽입이 예상되는 요소를 위한 추가적인 공간 확보
useEffect의 내부에서 요소에 영향을 미치는 작업, 뷰포트 내부에서 노출된 확률이 높은 작업을 최소하는 것이 좋다. useEffect 사용이 불가피하다면 useLayoutEffect 훅을 사용하는 것도 좋다. useEffect는 DOM 업데이트 이후 실행되므로, 레이아웃 변경이 화면에 표시된 후 이동이 발생할 수 있다. 반면 useLayoutEffect는 DOM 업데이트 전에 실행되므로 이동이 사용자에게 표시되기 전에 처리된다.
또는 스켈리톤 UI로 미리 무언가가 동적으로 뜰 것으로 예상되는 공간을 미리 확보해두는 것도 좋은 방안이다.
하지만 가장 좋은 방안은 SSR로, 클라이언트 렌더링 전에 완성된 HTML을 전달하여 초기 콘텐츠 로딩 속도를 높이고 레이아웃 이동을 최소화할 수 있다.
◼︎ 폰트 로딩 최적화
폰트를 로딩하는 과정에서 다음과 같은 2가지 문제가 발생할 수 있다.
이러한 문제를 해결하기 위해 다음과 같은 방법으로 해결할 수 있다.
1️⃣ preload 사용
폰트를 사전 로드하여 필요한 리소스를 빠르게 가져온다.
2️⃣ font-display: optional 속성 사용
브라우저가 폰트를 일정 시간 내에 로드하지 못하면 기본 폰트를 사용하도록 설정한다.
◼︎ 적절한 이미지 크기 설정
위와 같은 코드는 원본 이미지의 비율을 유지하며 반응형 디자인에 적합하지만, 이미지가 로드되기 전가지 높이를 계산할 수 없어 레이아웃 이동이 발생한다. 이로 인해 CLS 점수가 낮아진다.
따라서 이와 같은 문제를 해결하기 위해 다음과 같은 방법을 사용할 수 있다.
1️⃣ width와 height 속성 명시
브라우저가 이미지 로드 전 크기를 예측해 레이아웃 이동을 방지한다.
2️⃣ CSS aspect-ratios 속성 활용
이미지 로딩 전 고정 비율로 공간을 예약한다.
3️⃣ srcset 속성으로 반응형 이미지 제공
사용자 디바이스 크기에 따라 적절한 해상도의 이미지를 로드하고 싶다면 srcset 속성을 사용하는 것도 좋은 방안이다.
05. TTFB & FCP
TTFB(Time To First Byte) : 최초 바이트까지의 시간
TTFB는 브라우저가 웹페이지의 첫 번재 바이트를 수신하는데 걸리는 시간을 의미한다. 이 지표는 600ms 이하가 적합하며 이를 초과하면 개선이 필요하다. TTFB는 서버 응답 속도와 연결되며, 특히 SSR 환경에서는 주요 성능 지표로 간주된다.
💡 TTFB 개선하기 위한 방법
◼︎ 서버 위치 최적화
주 방문객의 국적과 가까운 지역에 서버를 배치하여 네트워크 지연을 최소화한다. Content Delivery Network(CDN) 활용으로 정적 리소스의 전송 속도 향상할 수 있다.
◼︎ React SSR이면 스트리밍 API 사용하기
React 18의 Server Components와 Streaming Rendering API를 사용하면 TTFB를 크게 단축할 수 있다.
FCP(First Contentful Paint) : 최초 콘텐츠풀 페인트
FCP는 페이지가 로드되기 시작한 시점부터 콘텐츠가 화면에 처음 렌더링 될 때까지의 시간이다.
FCP의 기준은 다음과 같다.
💡 FCP 개선하기 위한 방법
◼︎ 렌더링을 방해하는 리소스 최소화
CSS 및 JavaScript 파일을 비동기 로드하거나 중요하지 않은 스크립트를 지연 로드하는 방법이다.
async, defer 속성을 HTML 스크립트 태그에 추가하여 리소스를 최소화할 수 있다.
◼︎ Above-the-Fold(초기 화면 영역) 최적화
초기 화면에 보여야 하는 콘텐츠만 SSR로 처리하고, 나머지는 Lazy Loading으로 최적화하는 방법이다.
◼︎ DOM 크기 최소화
DOM이 복잡하고 크면 렌더링 시간이 오래 걸릴 것이다. DOM 중첩을 최소화하고 DOM 노드 개수를 줄여야 한다.
◼︎ 페이지 리다이렉트 최소화
한 번의 요청으로 필요한 페이지에 도달하도록 리다이렉션 체인을 제거해야 한다.
References
https://devocean.sk.com/blog/techBoardDetail.do?ID=165334&boardType=techBlog#none
웹 프론트엔드 성능 최적화에 알아야 할 측정지표 : Web Core Vitals의 INP
devocean.sk.com
'Front-End' 카테고리의 다른 글