field-sizing: content — textarea 자동 높이 스크립트를 CSS 한 줄로 걷어내기

  • 4th September 2026
  • 7 min read

게시판 글쓰기 화면의 <textarea>는 늘 두 줄 높이로 시작한다. 세 줄째부터 스크롤바가 생기고, 사용자는 자기가 쓴 글의 윗부분을 못 보면서 계속 쓴다. 그래서 어느 프로젝트에나 이런 코드가 하나씩 있다.

textarea.addEventListener('input', function () {
  this.style.height = 'auto';
  this.style.height = this.scrollHeight + 'px';
});

키를 누를 때마다 높이를 0으로 눌렀다가 scrollHeight만큼 다시 편다. 동작은 하지만 이 코드는 브라우저가 원래 해줘야 할 일을 스크립트가 흉내 내는 것이다. 다른 요소는 전부 내용만큼 커지는데, 폼 컨트롤만 그러지 않아서 생긴 우회다.

2026년 6월부터 이 우회를 CSS 한 줄로 걷어낼 수 있게 됐다. field-sizing: content다.

폼 컨트롤은 왜 내용을 무시하는가

<div>에 글을 넣으면 글만큼 커진다. <span>도, <td>도 그렇다. 그런데 <input><textarea>는 내용과 상관없이 미리 정해진 크기로 그려진다. 입력란은 size 속성(기본 20자), 텍스트 영역은 rows·cols(기본 2줄·20자)가 그 크기를 정한다.

이건 버그가 아니라 설계다. 폼 컨트롤은 비어 있는 상태로 사용자에게 보여지는 요소라, 내용 기준으로 크기를 잡으면 처음에는 크기가 0이 된다. 그래서 브라우저는 "내용은 무시하고 정해진 폭으로 그린다"를 기본값으로 삼아왔다.

field-sizing은 이 기본값을 속성으로 끌어낸 것이다. 값은 둘뿐이다.

  • fixed — 지금까지의 동작. 기본값이다.
  • content — 다른 요소처럼 내용만큼 커진다.

즉 새 기능을 켜는 속성이라기보다, 폼 컨트롤만 예외로 두던 규칙을 해제하는 스위치다.

content로 바꾸면 무엇이 달라지나

textarea, input, select {
  field-sizing: content;
}

요소별로 달라지는 것이 다르다.

<textarea> — 줄 수만큼 높아진다. 네 줄을 쓰면 네 줄 높이가 되고, 지우면 줄어든다. 위에 있던 scrollHeight 스크립트가 하던 일 그대로다.

<input> — 글자만큼 넓어진다. text·email·number·password·search·tel·url처럼 직접 입력하는 타입에 적용된다. 파일 입력도 선택된 파일명 길이에 맞춰 폭이 바뀐다.

<select> — 드롭다운은 지금 선택된 항목의 폭이 된다. 기본값에서는 가장 긴 항목에 맞춰 폭이 정해지는데, 그러면 짧은 항목을 골랐을 때 오른쪽이 텅 빈다. multiple이나 size가 붙은 리스트박스는 항목을 전부 보여줄 만큼 높아진다.

field-sizing: fixed (기본값) field-sizing: content 회의록입니다. 오늘 논의한 내용을 정리하면 다음과 같습니다. 첫째, rows=2 에서 멈추고 스크롤. 셋째 줄부터 안 보인다. 회의록입니다. 오늘 논의한 내용을 정리하면 다음과 같습니다. 첫째, 일정은 다음 주로 미룹니다. 둘째, 담당은 A가 맡습니다. 네 줄이면 네 줄 높이. 지우면 다시 줄어든다. A업체 size=20 기준. 세 글자를 넣어도 스무 글자 폭. A업체 글자만큼만. 더 치면 따라 넓어진다. content 에서는 rows · cols · size 속성이 무시된다. 한도는 min-height · max-height · min-width · max-width 로 건다.
같은 마크업, 속성 한 줄 차이.

한 가지 더. 플레이스홀더도 내용으로 친다. 값이 비어 있으면 플레이스홀더 글자가 다 보일 만큼의 크기로 그려진다. 값도 플레이스홀더도 없으면 min-width까지 줄어든다 — 이 지점은 뒤에서 다시 다룬다.

지금까지 스크립트로 하던 것들

이 속성이 대체하는 코드가 어떤 것들인지 짚어두면, 어디에 넣을지가 보인다.

scrollHeight로 높이 맞추기

글 첫머리의 그 코드다. 문제가 세 가지 있다.

  • 스크립트가 돌기 전에는 두 줄이다. 서버가 값을 채워서 내려준 수정 화면은 페이지가 뜬 직후 잠깐 두 줄로 보였다가 스크립트가 돌면서 늘어난다. 화면이 한 번 덜컥인다.
  • value를 코드로 바꾸면 input 이벤트가 안 난다. 임시저장 불러오기, 템플릿 삽입, 폼 초기화 뒤에는 높이를 손으로 다시 맞춰야 한다. 빠뜨리면 내용은 열 줄인데 상자는 두 줄이다.
  • box-sizing과 어긋난다. scrollHeight는 패딩을 포함하고 테두리는 뺀 값이라, border-box인지 아닌지에 따라 매번 몇 픽셀씩 남거나 모자란다. 그래서 대개 + 2 같은 보정 상수가 붙어 있다.

거울 요소로 폭 재기

입력란 폭을 글자에 맞추는 것은 더 번거롭다. <input>scrollWidth가 믿을 만하지 않아서, 같은 글꼴을 준 숨은 <span>에 값을 복사해 넣고 그 offsetWidth를 읽는다. 아니면 캔버스의 measureText로 재기도 한다. 어느 쪽이든 글꼴·크기·자간을 입력란과 똑같이 맞춰야 하고, 웹폰트가 늦게 로드되면 잰 값이 틀린다.

field-sizing: content는 이 둘을 전부 레이아웃 엔진 안으로 옮긴다. 이벤트도, 측정도, 보정 상수도 없다. 값이 어떤 경로로 바뀌든 크기는 따라온다.

한도를 거는 법

내용만큼 커진다는 것은 곧 끝없이 커진다는 뜻이다. 게시글 본문을 200줄 쓰면 텍스트 영역이 200줄 높이가 되고, 페이지 전체가 그만큼 길어진다. 한도가 필요하다.

한도는 평범한 CSS로 건다.

textarea {
  field-sizing: content;
  min-height: 3lh;    /* 최소 세 줄 */
  max-height: 20lh;   /* 스무 줄까지 늘고, 그 뒤는 스크롤 */
}

input[type="text"] {
  field-sizing: content;
  min-width: 8ch;
  max-width: 100%;
}

세 가지를 알아두면 된다.

rows·cols·size 속성은 무시된다. 최소값으로도 쓰이지 않는다. rows="5"를 뒀다고 다섯 줄에서 시작하지 않는다. 최소 높이를 원하면 min-height로 옮겨야 한다. 위 예시의 lh 단위는 줄 높이 하나라, rows를 옮기기에 딱 맞는다.

width·height를 고정값으로 주면 도로 고정된다. height: 80px이 있으면 field-sizing이 할 일이 없다. 한도는 반드시 min-·max-로 건다.

maxlength에 닿으면 성장이 멈춘다. 글자 수 제한이 있는 입력란은 그 길이에서 폭이 고정되므로, 별도의 max-width가 없어도 된다.

브라우저 지원

2026년 6월 16일에 Baseline Newly available이 됐다. 주요 브라우저 전부가 지원하기 시작한 날이다.

브라우저 버전 출시일
Chrome · Chrome Android1232024-03-19
Edge1232024-03-22
Safari · iOS Safari26.22025-12-12
Firefox · Firefox Android1522026-06-16

출처: webstatus.dev, 2026-09-04 조회.

크롬은 2년 넘게 됐고, 사파리가 작년 말, 파이어폭스가 올여름에 합류했다. 그래서 "이제 쓸 수 있다"가 된 것은 최근이지만, 실제 방문자 브라우저 기준으로는 대부분이 이미 지원하는 상태다.

폴백은 따로 필요 없다

이 속성의 좋은 점은 미지원 브라우저에서 아무것도 망가지지 않는다는 것이다. 모르는 속성은 무시되고, 기본값 fixed가 남는다. 즉 지금 모양 그대로다. @supports로 감쌀 이유가 없다.

기존 스크립트를 당장 지우기 어렵다면, 반대로 지원되는 브라우저에서 스크립트를 끄는 쪽이 맞다.

if (!CSS.supports('field-sizing', 'content')) {
  // 미지원 브라우저에서만 scrollHeight 리사이저를 붙인다
  attachAutoResize(textarea);
}

둘 다 켜두면 스크립트가 height를 고정값으로 박아버려서 field-sizing이 무력화된다. 앞 절의 "고정값을 주면 도로 고정된다"가 바로 이 경우다.

부트스트랩과 부딪히는 곳

부트스트랩의 .form-control은 이렇게 돼 있다.

.form-control {
  display: block;
  width: 100%;
  ...
}

<textarea>는 이대로 괜찮다. 폭이 100%로 고정돼도 field-sizing이 건드리는 것은 높이라, 서로 방해하지 않는다. 클래스는 그대로 두고 field-sizing: contentmax-height만 얹으면 된다.

<input>은 안 된다. 글자만큼 넓어지려면 폭이 자유로워야 하는데 width: 100%가 못 박고 있다. 폭 자동을 원하는 입력란에만 덮어쓴다.

.form-control.is-fit {
  display: inline-block;
  width: auto;
  min-width: 8ch;
  max-width: 100%;
  field-sizing: content;
}

실제로는 입력란 폭을 글자에 맞추고 싶은 자리가 생각보다 적다. 폼의 입력란은 세로로 정렬돼 있고 왼쪽 끝을 맞추는 것이 중요해서, 폭이 제각각이면 오히려 지저분하다. 폭 자동이 어울리는 곳은 문장 사이에 끼는 입력란, 표 안의 짧은 값, 태그 입력처럼 인라인으로 흐르는 자리다. 텍스트 영역의 높이 자동은 거의 어디서나 이득이지만, 입력란의 폭 자동은 골라서 써야 한다.

주의할 것

빈 입력란은 사라진다. 값도 플레이스홀더도 없으면 min-width까지 줄어드는데, min-width를 안 줬으면 거의 0이다. 폭 자동을 켤 때는 min-width가 사실상 필수다. 플레이스홀더를 넣는 것도 방법인데, 그러면 플레이스홀더 길이가 초기 폭이 된다.

타이핑마다 레이아웃이 바뀐다. 폭이 자라면 옆에 있던 것이 밀린다. 표 안의 입력란이면 열 폭이 흔들리고, 플렉스 줄 안이면 줄바꿈 위치가 바뀐다. 텍스트 영역의 높이 변화는 아래로만 밀어서 대개 괜찮지만, 입력란의 폭 변화는 주변을 살펴야 한다.

rows를 믿고 있던 코드를 찾아야 한다. 속성이 무시되므로, 마크업에 rows="10"이 있고 그걸로 "이 영역은 넉넉하게" 를 표현하던 곳은 min-height로 옮기지 않으면 두 줄짜리가 아니라 한 줄짜리가 된다. 기본 최소가 없어지기 때문이다.

정리

field-sizing: content는 새 기능이라기보다 폼 컨트롤만 예외로 두던 규칙을 푸는 스위치다. 텍스트 영역이 줄 수만큼, 입력란이 글자만큼, 셀렉트가 선택된 항목만큼 커진다.

  • 대체하는 것: scrollHeight 리사이저, 거울 span·measureText 폭 측정
  • 같이 쓸 것: min-height·max-height·min-width·max-width. rows·cols·size는 무시된다
  • 지원: Chrome 123 · Edge 123 · Safari 26.2 · Firefox 152. Baseline 2026-06-16
  • 폴백: 필요 없음. 미지원이면 지금 모양. 기존 스크립트는 CSS.supports로 미지원 브라우저에서만 켠다
  • 부트스트랩: 텍스트 영역은 그대로, 입력란은 width: auto를 덮어써야 한다

가장 먼저 넣을 곳은 게시판·댓글·메모의 <textarea>다. 리사이저 스크립트를 지우고 두 줄을 넣는 것으로 끝난다.