SVG를 키워도 작게 나오는 이유: viewBox가 무엇인가

SVG의 viewBox가 여백을 만들어 그림이 작아 보이는 상황을 나타낸 대표 이미지
상자가 그림보다 클 때 생깁니다.

SVG 이미지를 키웠는데 그림은 계속 작게 나올 때가 있습니다. 파일 안에 적힌 viewBox 때문입니다. 실제로 겪고 나서야 이해한 내용을 정리합니다. 이 글에서는 viewBox 숫자 네 개가 무엇인지, 왜 여백이 생기는지, 여백을 재서 줄이는 방법, 그리고 줄인 뒤에도 모양이 이상할 때 확인할 속성까지 순서대로 다룹니다. 설명은 MDN과 W3C 명세에 적힌 내용만 사용했습니다.

viewBox는 ‘어디를 보여줄지’다

SVG 파일 맨 앞에 이런 게 있습니다.

<svg viewBox="0 0 400 300">

MDN은 viewBox를 “사용자 공간(user space)에서 SVG 뷰포트의 위치와 크기를 정하는 속성”이라고 설명합니다. 값은 공백이나 쉼표로 구분한 숫자 네 개이고, 순서는 min-x, min-y, width, height입니다. 쉽게 풀면 “이 그림 좌표에서 (0,0)부터 400×300 범위를 잘라 화면 상자에 맞춰 보여줘라”는 뜻입니다.

여기서 중요한 게 있습니다. 이 숫자는 화면의 픽셀이 아닙니다. 그림 안에서만 쓰는 좌표입니다. 그래서 SVG는 아무리 키워도 안 깨집니다. 정해진 크기가 아니라 그리는 방법이 적혀 있기 때문입니다.

viewBox 범위와 실제 그림이 차지하는 범위가 다른 상황을 나타낸 이미지
상자를 줄이면 그림이 커집니다.

네 숫자를 하나씩 읽는 법

자리이름뜻예: 0 0 400 300
1번째min-x보여줄 범위의 왼쪽 시작 좌표0
2번째min-y보여줄 범위의 위쪽 시작 좌표0
3번째width범위의 가로 길이(그림 좌표 단위)400
4번째height범위의 세로 길이(그림 좌표 단위)300

시작 좌표가 0이 아니어도 됩니다. viewBox="76 73 248 154"처럼 쓰면 그림 좌표 (76, 73)에서 시작해 248×154 범위만 보여줍니다. 아래에서 여백을 줄일 때 바로 이 방식을 씁니다.

같은 원인데 viewBox 값에 따라 달라진다

MDN 예제는 같은 사각형과 원을 넣고 viewBox만 다르게 한 SVG 세 개를 보여줍니다. 원의 반지름 r 값은 세 개 모두 같은데도 결과 크기가 다릅니다. 반지름이 화면 픽셀이 아니라 viewBox가 정한 좌표 크기를 기준으로 계산되기 때문입니다.

숫자로 따라가 보겠습니다. 반지름이 40인 원이 있고 화면 상자의 폭이 400픽셀이라고 합니다.

viewBox 폭400픽셀 상자에 들어갈 때의 배율반지름 40인 원의 화면 크기
100400 ÷ 100 = 4배반지름 160픽셀(상자 폭을 가득 채움)
400400 ÷ 400 = 1배반지름 40픽셀(상자 폭의 1/5)
800400 ÷ 800 = 0.5배반지름 20픽셀(상자 폭의 1/20)

이 표가 이 글의 핵심입니다. viewBox의 폭이 클수록 같은 그림이 작게 보입니다. 그림이 실제로 차지하는 범위보다 viewBox가 훨씬 크면, 그만큼 그림이 작아 보이는 겁니다. (위 계산은 폭만 비교한 단순 예이며, 상자 높이가 다르면 아래 preserveAspectRatio 규칙이 같이 적용됩니다.)

그림이 상자를 다 안 채운다

문제는 여기서 생깁니다. viewBox가 400×300인데 실제 그림은 그 안쪽 244×150만 차지하고 있으면, 나머지는 전부 빈 공간입니다.

가로로 따지면 그림은 상자의 244 ÷ 400 = 61%만 쓰고 있습니다. 이 상태로 화면에서 크기를 키우면 빈 공간까지 같이 커집니다. 그림은 상대적으로 계속 작아 보입니다. 제가 겪은 게 정확히 이 경우였습니다.

실제 범위를 재서 줄인다

해결은 viewBox를 실제 그림 크기에 맞게 줄이는 것입니다. 문제는 그 크기를 눈으로는 알 수 없다는 점입니다.

브라우저에 재 주는 기능이 있습니다. MDN은 getBBox()를 “요소를 포함하는 가장 작은 사각형을 돌려주는 메서드”라고 설명합니다.

// 그림이 실제로 차지하는 범위를 알려준다
var box = document.querySelector('svg').getBBox();
console.log(box.x, box.y, box.width, box.height);
// → 78 75 244 150

이 값으로 viewBox를 다시 쓰면 여백이 사라집니다.

<svg viewBox="76 73 248 154">   <!-- 여유 2씩만 남긴다 -->

계산은 간단합니다. 시작 좌표는 잰 값에서 2를 빼고(78→76, 75→73), 폭과 높이는 양쪽 여유 4를 더합니다(244→248, 150→154).

viewBox를 다룰 때 알아둘 세 가지를 정리한 설명 이미지
한 번 재면 값은 안 바뀝니다.

여유를 남기는 이유: 선 굵기

딱 맞게 자르면 테두리 선의 바깥쪽 절반이 잘립니다. MDN 설명에 근거가 있습니다. getBBox()는 기본값으로 선(stroke)을 범위에 넣지 않습니다. 선을 포함하려면 getBBox({ stroke: true })처럼 옵션을 줘야 합니다. 같은 문서에 따르면 fill이 none이어도 모양은 범위에 들어가고, 마커는 기본값으로 빠집니다.

옵션기본값켜면 달라지는 것
filltrue채움이 없어도 도형 모양은 범위에 포함
strokefalse선과 선 끝 모양까지 범위에 포함
markersfalse화살표 같은 마커가 그린 모양까지 포함
clippedfalseclip-path로 잘려 보이는 부분만 범위로 계산

선이 2픽셀 굵기라면 바깥으로 1만큼 나오므로, 제가 쓴 여유 2는 그 정도를 넉넉히 덮는 값입니다. 선이 굵은 그림이라면 여유를 더 키우거나 stroke 옵션을 켜서 재면 됩니다.

줄였는데도 위아래로 빈 칸이 남을 때

viewBox를 딱 맞췄는데 상자 안에서 그림이 가운데에 떠 있고 위아래나 양옆이 비어 보일 때가 있습니다. 이때는 preserveAspectRatio를 봅니다. MDN은 이 속성이 “viewBox와 비율이 다른 뷰포트에 어떻게 맞출지”를 정한다고 설명하며, 기본값은 xMidYMid(가로 세로 모두 가운데 정렬)입니다.

그림 비율이 상자 비율과 다르면 비율을 지킨 채 한쪽 방향이 남고, 남는 부분은 가운데로 몰립니다. 그림이 찌그러지지 않고 안 잘리게 맞추는 기본 동작이어서 대부분은 그대로 둡니다.

값동작언제 쓰나
xMidYMid meet(기본)비율 유지, 그림 전체가 들어오게 맞춤, 가운데 정렬대부분의 로고·아이콘
xMinYMin meet비율 유지, 왼쪽 위에 붙임본문 글 옆에 붙여 쓸 때
xMidYMid slice비율 유지, 상자를 꽉 채우고 넘치는 부분은 잘림배경처럼 꽉 채울 때
none비율을 무시하고 늘려서 맞춤(찌그러질 수 있음)일부러 늘려야 할 때만

MDN에는 viewBox가 없으면 preserveAspectRatio가 SVG 크기 조절에 아무 영향도 주지 않는다고 적혀 있습니다. 둘은 짝으로 움직입니다.

내 SVG 점검 순서

  1. 파일 맨 앞 <svg>에 viewBox가 있는지 봅니다. 없으면 크기 조절 자체가 다르게 동작합니다.
  2. 브라우저 개발자 도구 콘솔에서 getBBox()를 실행해 실제 그림 범위를 잽니다.
  3. viewBox의 폭·높이와 잰 폭·높이를 비교합니다. 잰 값이 viewBox의 70%도 안 되면 여백이 크게 낀 겁니다.
  4. 잰 값에 선 굵기만큼 여유를 더해 viewBox를 새로 씁니다.
  5. 상자 비율이 다르면 preserveAspectRatio 값을 확인합니다.
  6. 키워 놓은 화면에서 선이 잘리지 않는지 눈으로 마지막 확인을 합니다.

알아둘 것

잰 값은 변환이 적용되기 전 좌표입니다. MDN에 따르면 getBBox()는 요소나 부모에 걸린 transform(이동·회전·확대)을 반영하지 않습니다. 그림에 transform이 걸려 있다면 잰 숫자와 실제 화면 위치가 다를 수 있으니, 이동한 만큼 따로 더해 줘야 합니다.

한 번 잰 값은 안 바뀝니다. 같은 파일이면 항상 같으니 저장해 두고 다시 쓰면 됩니다. 저는 잰 값을 파일로 남겨 두고, 다음부터는 재지 않습니다.

원본을 고치지 않아도 됩니다. 파일을 읽어서 viewBox 부분만 바꿔 쓰면 되니, 받아 온 SVG를 그대로 두고도 처리할 수 있습니다.

참고로 이전 판의 글에는 “화면에 그려져 있어야만 잴 수 있다”고 적었는데, MDN은 아직 렌더링되지 않은 요소도 호출 시점의 값으로 돌려준다고 설명합니다. 이번에 바로잡았습니다.

왜 여백이 들어 있나

만든 쪽 입장에서는 이유가 있습니다. 여러 그림의 크기를 맞춰 두면 나란히 놓기 편합니다. 그림마다 상자가 제각각이면 정렬이 어렵습니다.

그러니 잘못 만든 게 아닙니다. 쓰는 쪽에서 목적에 맞게 조정하면 되는 부분입니다. 여러 그림을 같은 줄에 맞춰야 하는 목록형 화면이라면 여백을 그대로 두는 편이 낫고, 그림 하나를 크게 보여줘야 하는 대표 이미지라면 여백을 줄이는 편이 낫습니다.

자주 막히는 지점 세 가지

1. 숫자를 줄였더니 그림이 잘렸다. 시작 좌표(min-x, min-y)를 바꾸지 않고 폭과 높이만 줄이면 범위가 왼쪽 위 기준으로 줄어듭니다. 그림이 (78, 75)에서 시작하는데 시작을 0으로 두고 폭만 248로 줄이면 그림의 오른쪽이 상자 밖으로 나갑니다. 시작 좌표부터 잰 값에 맞추세요.

2. CSS로 크기를 줬는데도 작다. width와 height는 상자의 크기이고, 그림이 상자 안에서 얼마나 차지하느냐는 viewBox가 정합니다. 상자를 키워도 비율은 그대로이므로 여백도 같이 커집니다. 상자 크기와 viewBox는 따로 봐야 합니다.

3. 줄인 SVG를 여러 곳에 썼더니 어긋났다. 같은 줄에 나란히 놓는 용도의 SVG까지 모두 줄이면 위치가 제각각이 됩니다. 목록형에는 원래 여백을 두는 편이 안전하니, 대표 이미지처럼 하나만 크게 쓰는 파일만 줄이는 쪽을 권합니다.

명세는 viewBox를 어떻게 정의하나

MDN 말고 표준 문서인 W3C의 SVG 1.1 명세에서도 같은 내용을 볼 수 있습니다. 명세는 viewBox 값을 “min-x, min-y, width, height 네 개의 숫자 목록”으로 정의하고, 이 값이 해당 요소가 만드는 뷰포트의 경계에 대응된다고 설명합니다. 아래는 명세에 적힌 규칙을 독자용으로 풀어 쓴 표입니다.

규칙명세에 적힌 내용실제로 겪는 일
네 숫자min-x, min-y, width, height 순서의 목록숫자 순서를 바꾸면 엉뚱한 부분이 보입니다.
폭·높이가 음수오류로 분류됩니다.음수를 넣지 말고 시작 좌표를 바꿔서 위치를 맞춥니다.
폭·높이가 0요소 렌더링이 꺼집니다.그림이 아예 사라진 것처럼 보일 수 있습니다.
preserveAspectRatio같은 요소에 viewBox 값이 있을 때만 적용됩니다.viewBox가 없으면 정렬 옵션을 바꿔도 달라지지 않습니다.
기본 정렬xMidYMid, 그리고 meet(전체가 들어오게 맞춤)이 기본입니다.비율이 다른 상자에서는 남는 쪽이 가운데로 몰립니다.
slice상자를 꽉 채우고 넘치는 부분은 잘릴 수 있습니다.배경처럼 쓸 때만 고릅니다.
none정렬 값이 none이면 meet·slice 값은 무시됩니다.비율이 무시되어 그림이 늘어납니다.

표의 규칙을 정리하면 이렇습니다. 그림이 사라졌다면 폭과 높이가 0인지 먼저 보고, 정렬이 의도와 다르면 viewBox가 같은 요소에 있는지 확인하는 순서가 빠릅니다. 이 순서는 앞의 점검 순서와 겹치지만, 표준 문서의 문장에 근거를 둔 확인이라는 점이 다릅니다.

확인 자료

위 문서는 2026년 10월 기준 내용입니다. 배율 표의 숫자는 위 설명을 바탕으로 이 글에서 계산한 예입니다.