CLS
Cumulative Layout Shift
지표
페이지를 보는 동안 화면 요소가 예고 없이 밀려난 정도를 하나의 값으로 누적해 나타낸 지표입니다.
등장 배경
웹 성능을 재는 숫자는 오랫동안 시간만 셌습니다. 몇 초 만에 떴고 몇 초 만에 눌 수 있게 됐는지를 봤습니다. 그런데 사용자가 화면을 닫아 버리는 순간은 느릴 때만 오는 게 아니었습니다. 읽던 문장이 아래로 훅 내려갈 때, 누르려던 버튼 자리에 다른 버튼이 들어왔을 때도 그랬습니다.
밀림은 늦게 도착하는 것들 때문에 생깁니다. 글이 먼저 그려지고 이미지가 나중에 도착하면, 브라우저는 그제야 이미지 자리를 확보하느라 아래 내용을 밀어냅니다. 광고나 알림 띠도 같은 일을 합니다. 문제는 이 현상이 빠른 페이지에서 오히려 잘 생긴다는 점이었습니다. 뼈대를 먼저 보여 주고 내용을 나중에 채우는 방식이 뜨는 시간을 줄이는 대신 밀림을 만듭니다. 시간만 재는 지표로는 이 거래가 보이지 않았습니다.
페이지덱에서도 문의가 들어왔습니다. 요금제 화면에서 결제 버튼을 누르려는 순간 상단에 공지 띠가 내려오면서 화면 전체가 아래로 밀렸고, 누르려던 자리에 들어와 있던 것은 플랜 해지 버튼이었습니다. 민서가 재현해 보니 공지를 가져오는 요청이 평균 0.4초 뒤에 도착했습니다. 접속이 느릴수록 더 늦게 도착했고 밀림도 더 크게 겪었습니다. CLS 는 이런 사고를 숫자로 잡으려고 만든 값입니다.
계산 방법
밀림 한 번의 점수는 영향 비율과 거리 비율을 곱해 구합니다. 영향 비율은 밀린 요소가 화면에서 차지한 면적의 비율이고, 거리 비율은 그 요소가 움직인 거리를 화면의 긴 쪽 길이로 나눈 값입니다.
숫자로 보면 이렇습니다. 화면 높이의 절반을 차지하던 글 덩어리가 화면 높이의 4분의 1만큼 아래로 밀렸다고 하겠습니다. 이 덩어리가 밀리기 전과 후에 걸쳐 있던 영역을 합하면 화면의 75퍼센트이므로 영향 비율은 0.75입니다. 움직인 거리는 화면 높이의 25퍼센트이므로 거리 비율은 0.25입니다. 곱하면 0.1875가 이번 밀림의 점수입니다.
이 점수들을 모으는 방식은 두 가지를 기억하면 됩니다.
- 합은 창 단위로 봅니다. 페이지를 보는 내내 벌어진 밀림을 전부 더하는 게 아니라, 연달아 일어난 밀림을 최대 5초짜리 묶음으로 자르고 그중 합이 가장 큰 묶음의 값을 씁니다. 그래서 오래 보는 페이지라고 해서 값이 계속 올라가지는 않습니다.
- 사용자가 만든 변화는 빼 줍니다. 더보기 버튼을 눌러 내용이 펼쳐지면서 아래가 밀리는 것은 사용자가 예상한 변화입니다. 클릭이나 키 입력이 있고 0.5초 안에 일어난 변화는 점수에서 제외됩니다.
이 값은 실제 사용자의 브라우저에서 측정됩니다. 그래서 같은 페이지라도 접속 환경에 따라 다른 값이 나옵니다. LCP가 가장 큰 콘텐츠가 그려지기까지 걸린 시간을 재듯이, CLS 는 그 과정에서 자리가 얼마나 흔들렸는지를 잽니다.
좋은 수준의 기준
0.1 이하를 좋은 수준으로 봅니다. 0.25를 넘으면 나쁜 수준이고, 그 사이는 개선이 필요한 구간입니다. 앞의 예시에서 나온 0.1875는 한 번의 밀림만으로 이미 중간 구간에 들어가는 값입니다.
판정은 평균이 아니라 75번째 백분위수로 합니다. 접속한 사람들의 값을 낮은 쪽부터 줄 세웠을 때 4분의 3 지점에 있는 값을 씁니다. 평균을 쓰면 빠른 기기에서 잘 나온 값들이 느린 기기의 나쁜 값을 덮어 버립니다. 사용자 100명 중 75명이 0.05를 겪고 25명이 0.4를 겪는 페이지는 평균이 0.14로 나와 괜찮아 보이지만, 백분위수로 보면 0.4에 가까운 값이 잡혀 문제가 드러납니다.
값을 낮추는 방법은 대개 자리를 미리 잡아 두는 것입니다.
- 이미지와 동영상에 크기를 지정합니다. 가로세로 값이 적혀 있으면 브라우저가 파일이 도착하기 전에 그만큼 빈칸을 비워 둡니다. 이미지 최적화에서 크기 지정이 함께 다뤄지는 이유입니다.
- 나중에 들어올 것의 자리를 비워 둡니다. 광고나 공지 띠가 들어갈 영역은 내용이 없을 때도 높이를 유지하게 만듭니다.
- 이미 보이는 내용 위에 무언가를 끼워 넣지 않습니다. 넣어야 한다면 화면 아래쪽에 띄우거나 기존 내용 위에 겹치게 합니다.
실무 용례
"이번 배포에서 CLS 가 0.08에서 0.21로 뛰었어요. 새로 붙인 배너 때문 같습니다."
밀림이 나쁜 구간으로 올라갔고 원인으로 짚이는 변경이 있다는 보고입니다.
"그 이미지에 width 랑 height 는 넣어 주세요. 안 그러면 CLS 가 또 올라갑니다."
크기를 적어 두지 않으면 브라우저가 자리를 못 비워 밀림이 생긴다는 뜻입니다.
"로컬에서는 0인데 실제 사용자 값은 0.3이에요. 느린 회선에서만 나는 것 같습니다."
개발 환경 측정과 실제 사용자 측정이 다르다는 말입니다.
페이지덱에서 이 숫자를 가장 자주 보는 사람은 태오와 민서입니다. 태오는 배포 전 점검 목록에 이 값을 넣어 두고, 민서는 새 화면을 그릴 때부터 나중에 들어올 요소의 자리를 시안에 미리 표시합니다. 앞의 공지 띠 사고 뒤에 정한 방식입니다. 시안에 빈칸이 그려져 있으면 만드는 사람이 높이를 지정하게 되고, 그러면 공지가 늦게 도착해도 화면이 움직이지 않습니다.
숫자를 고칠 때 먼저 할 일은 어디가 밀렸는지 찾는 것입니다. 브라우저 개발자 도구가 밀린 요소를 하나씩 짚어 주고 각각의 점수까지 보여 줍니다. 대개 한두 개가 전체 값의 대부분을 만들어서, 그것만 고쳐도 값이 절반 아래로 떨어집니다.
흔한 오해
CLS 를 속도 지표로 읽는 경우가 가장 많습니다. 웹 성능 이야기에 같이 나오니 자연스러운 오해입니다. 그런데 이 값은 시간을 재지 않습니다. 1초 만에 뜨는 페이지가 0.4를 기록하고 4초 걸리는 페이지가 0을 기록하는 일이 흔합니다. 오히려 뜨는 시간을 줄이려고 내용을 나눠 보내는 방식이 밀림을 늘립니다. 속도를 올렸으니 이 값도 좋아졌으려니 하고 확인하지 않으면, 사용자는 더 빨리 뜨는 대신 더 자주 잘못 누르는 화면을 받습니다.
개발 환경에서 0이면 됐다고 보는 것도 흔합니다. 노트북에서 새로고침해 보면 밀림이 안 보이는 것이 보통입니다. 파일이 캐시에 남아 있고 회선이 빠르니 늦게 도착하는 것이 없기 때문입니다. 실제 사용자 값은 느린 회선과 오래된 기기에서 만들어지고, 광고나 외부 스크립트처럼 개발 환경에서 안 뜨는 것들이 끼어듭니다. 그래서 이 값은 실사용자 측정으로 확인해야 하고, 로딩·반응·안정성을 한 묶음으로 보는 코어 웹 바이탈 보고서가 그 자리를 맡습니다.
같이 보면 좋은 페이지
마지막 업데이트 2026-09-16