개발

페이지마다 레이아웃이 좌우로 밀릴 때 html 에 scrollbar-gutter: stable 로 스크롤바 자리 미리 잡기

  • 4th October 2026
  • 5 min read

본문이 짧은 페이지에서 긴 페이지로 넘어가면 가운데 정렬된 레이아웃 전체가 왼쪽으로 몇 픽셀 밀린다. 다시 짧은 페이지로 가면 오른쪽으로 돌아온다. 메뉴를 오갈 때마다 로고와 본문이 좌우로 흔들린다. 모달을 열 때도 같은 일이 생긴다. 배경 스크롤을 막으려고 overflow: hidden을 걸면 스크롤바가 사라지고, 그만큼 페이지가 오른쪽으로 튄다.

원인은 같다. 세로 스크롤바가 화면 폭을 차지했다가 내놓기 때문이다. scrollbar-gutter: stable은 스크롤바가 있든 없든 그 자리를 미리 비워 두게 하는 속성이다. 2024년 12월 사파리 18.2가 합류하면서 Baseline이 됐다(크롬 94, 파이어폭스 97).

그런데 이 속성은 스크롤바가 화면 폭을 차지하는 환경에서만 의미가 있다. 어디서 효과가 있고 어디서 없는지, 모달과 같이 쓸 때 무엇이 남는지를 크롬에서 재 봤다.

폰과 맥에서는 처음부터 문제가 없다

스크롤바에는 두 종류가 있다.

오버레이 스크롤바클래식 스크롤바
모양스크롤할 때만 내용 위에 떠서 나타남오른쪽에 항상 자리를 차지하는 막대
화면 폭차지하지 않음차지함
쓰는 곳안드로이드, iOS, 맥 기본 설정(트랙패드 사용 시)윈도우, 맥에서 '스크롤 막대 보기: 항상'

오버레이 스크롤바는 화면 폭을 차지하지 않으니 레이아웃이 밀릴 일이 없고, scrollbar-gutter도 비워 둘 자리가 없다. 크롬의 모바일 에뮬레이션(390px)에서 재 보니 stable을 줘도 문서 폭은 그대로였다. 폰에서 생기는 레이아웃 흔들림은 이 속성으로 고칠 수 없다는 뜻이다. 거꾸로 말하면 넣어 둬도 폰에서 손해 볼 것은 없다.

개발자가 이 문제를 놓치기 쉬운 이유도 여기 있다. 맥에서 트랙패드로 작업하면 스크롤바가 오버레이라서 아무것도 밀리지 않는다. 윈도우를 쓰는 방문자만 보고 있다.

클래식 스크롤바에서 잰 결과

크롬을 --disable-features=OverlayScrollbar로 띄우면 맥에서도 클래식 스크롤바가 나온다. 이 상태에서 스크롤바 폭은 15px이었다. 폭 800px 창에 max-width: 600px; margin: 0 auto인 상자를 놓고 상자의 왼쪽 위치를 쟀다.

기본 상태에서는 스크롤바가 생긴 긴 페이지의 상자가 짧은 페이지보다 왼쪽으로 치우치고, scrollbar-gutter: stable 을 주면 짧은 페이지에도 오른쪽에 빈 자리가 생겨 두 페이지의 상자 위치가 같아진다
빨간 점선은 창의 가운데. 기본 상태에서는 스크롤바가 생기면 상자가 왼쪽으로 밀린다
상황기본html { scrollbar-gutter: stable }
짧은 페이지100px92.5px
긴 페이지92.5px92.5px
긴 페이지 + overflow: hidden (모달 열림)100px92.5px
position: fixed; left: 0; right: 0 헤더 폭800 ↔ 785px항상 785px

기본 상태에서는 스크롤바가 생기고 사라질 때마다 상자가 7.5px(스크롤바 폭의 절반) 움직인다. stable을 주면 세 경우 모두 92.5px로 고정된다. 짧은 페이지에서는 스크롤바가 없는데도 오른쪽 15px이 빈 채로 남는다. 고정 헤더의 폭도 흔들리지 않는다.

body가 아니라 html에 준다

html {
  scrollbar-gutter: stable;
}

페이지 전체의 스크롤바는 루트 요소인 html의 것이다. overflow는 body에 줘도 뷰포트로 넘어가는 오래된 예외가 있어서 습관적으로 body에 쓰는 경우가 많다. 하지만 scrollbar-gutter에는 그런 예외가 없다. body에 줬더니 기본 상태와 똑같이 100px과 92.5px 사이를 오갔다.

반대로 overflow: hidden은 body에 걸든 html에 걸든 결과가 같았다. html에 stable이 있으면 스크롤이 막혀도 자리가 유지된다. 명세도 stable은 overflow가 hidden, scroll, auto일 때 자리를 비워 두도록 정해 두었다.

모달 보정 스크립트와 같이 쓰면 반대로 튄다

모달을 열 때 생기는 흔들림은 원래 스크립트로 막아 왔다. 부트스트랩 5.1의 모달 코드는 이렇게 동작한다.

// 스크롤바 폭 = 창 폭 - 문서 폭
getWidth() {
  const t = document.documentElement.clientWidth;
  return Math.abs(window.innerWidth - t);
}
// 열 때: body 에 overflow: hidden, padding-right 에 위 값을 더함
// .fixed-top, .sticky-top 같은 고정 요소에도 padding-right 를 더함

스크롤바가 사라지는 만큼 오른쪽 여백을 채워 넣는 방식이다. 문제는 scrollbar-gutter: stable을 추가하고 이 스크립트를 그대로 두는 경우다. 스크롤바 자리는 이미 CSS가 지키고 있는데 스크립트가 15px을 또 넣는다.

모달 열기 방식열기 전연 뒤
스크립트 보정만92.5px92.5px
stable만92.5px92.5px
stable + 스크립트 보정92.5px85px

둘을 같이 쓰면 모달을 열 때 페이지가 이번에는 왼쪽으로 7.5px 튄다. 고치려던 증상이 방향만 바꿔서 그대로 남는다. stable을 넣는다면 모달 쪽 보정은 꺼야 한다. 부트스트랩이라면 보정 대상이 body와 고정 요소의 padding-right이니, 그 부분이 적용되지 않는지 실제로 열어서 확인할 것.

스크립트로 폭을 잴 때 하나 더 주의할 것이 있다. stable로 비워 둔 자리는 clientWidth에서 빠지지 않는다. 짧은 페이지나 overflow: hidden 상태에서 innerWidth - clientWidth는 0이다. 오른쪽 15px이 비어 있는데도 그렇다.

모달 배경막은 그 자리를 덮지 못한다

stable로 바꿨을 때 눈에 보이는 차이가 하나 있다. 모달을 열면 화면 오른쪽 끝에 어두워지지 않는 띠가 남는다.

스크립트 보정 방식은 어두운 배경막이 창 전체를 덮지만, scrollbar-gutter: stable 에서는 오른쪽 스크롤바 자리에 흰 띠가 남고, 배경막 폭을 100vw 로 줘도 띠가 그대로다
오른쪽 두 칸의 흰 띠가 비워 둔 스크롤바 자리

position: fixed; inset: 0으로 만든 배경막도, <dialog>의 ::backdrop도 폭이 785px이었다. 고정 위치 요소의 기준은 스크롤바 자리를 뺀 영역이기 때문이다. width: 100vw로 넓혀 봐도 그 자리에는 그려지지 않았다. 스크롤바가 사라진 자리에 아무것도 없는 띠가 남는 셈이다.

배경막이 연할수록 덜 눈에 띄고, 짙을수록 띠가 또렷해진다. 이 띠가 거슬리는 디자인이라면 모달이 많은 화면에서는 기존 스크립트 보정을 유지하는 것도 방법이다. 둘 중 하나만 고르면 된다.

both-edges는 양쪽을 다 비운다

html {
  scrollbar-gutter: stable both-edges;
}

오른쪽뿐 아니라 왼쪽에도 같은 폭의 자리를 비운다. 재 보니 상자의 왼쪽 위치가 짧은 페이지와 긴 페이지 모두 100px으로, 스크롤바가 없을 때와 같았다. 창의 정중앙에 맞춘다는 뜻이다. 대신 쓸 수 있는 폭이 스크롤바 두 개만큼 줄고, 꽉 찬 고정 헤더의 폭이 770px으로 줄면서 왼쪽에도 15px 틈이 생긴다. 페이지 전체보다는 대칭이 중요한 스크롤 영역 안쪽에 어울린다.

정리

  • 스크롤바가 생기고 사라질 때 가운데 정렬 레이아웃이 밀리는 문제는 html { scrollbar-gutter: stable } 한 줄로 막힌다. body에 주면 효과가 없다.
  • 윈도우처럼 클래식 스크롤바를 쓰는 환경에서만 의미가 있다. 폰과 맥 기본 설정은 오버레이 스크롤바라 원래 밀리지 않고, 이 속성도 아무 일을 하지 않는다.
  • 모달을 열 때 padding-right를 더하는 스크립트와 같이 쓰면 반대 방향으로 튄다. 둘 중 하나만 쓴다.
  • 비워 둔 자리는 모달 배경막이 덮지 못해 띠로 남는다.

크롬 154에서 잰 결과다. 파이어폭스와 사파리(클래식 스크롤바 설정)는 이번에 재지 못했다.

함께 읽으면 좋은 글