HTML disabled와 readonly 차이
HTML 폼에서 값을 수정하지 못하게 만들 때 disabled와 readonly를 자주 사용한다.
두 속성 모두 사용자 입력을 막지만 브라우저가 해당 control을 다루는 방식은 다르다.
disabled는 지금 사용할 수 없는 control을 폼 동작에서 제외한다.readonly는 값을 읽고 제출할 수는 있지만 사용자가 수정하지 못하게 한다.
서버 전송 여부와 control 종류를 먼저 확인해야 예상하지 못한 폼 동작을 피할 수 있다.
핵심 차이
| 기준 | disabled | readonly |
|---|---|---|
| 사용자의 값 변경 | 불가능 | 불가능 |
| 키보드 포커스 | 받지 않음 | 받을 수 있음 |
| 값 선택과 복사 | 일반적인 상호작용에서 제외 | 가능 |
| 폼 제출 데이터 | 포함되지 않음 | 포함됨 |
| constraint validation | 참여하지 않음 | 참여하지 않음 |
| 주요 적용 대상 | button, fieldset, input, select, textarea 등 | 일부 input과 textarea |
disabled- 불가능
readonly- 불가능
disabled- 받지 않음
readonly- 받을 수 있음
disabled- 일반적인 상호작용에서 제외
readonly- 가능
disabled- 포함되지 않음
readonly- 포함됨
disabled- 참여하지 않음
readonly- 참여하지 않음
disabledbutton,fieldset,input,select,textarea등readonly- 일부
input과textarea
disabled control은 일반적인 포커스와 클릭 대상에서 빠진다.
readonly control은 포커스를 받고 표시된 값을 선택하거나 복사할 수 있다.
두 속성은 사용자 조작을 제한하지만 JavaScript가 값을 바꾸는 것까지 막지는 않는다.
값을 보내야 하지만 사용자가 수정하면 안 된다면 readonly가 가깝다.
현재 사용할 수 없고 제출 대상에서도 빼야 한다면 disabled가 가깝다.
제출 데이터와 유효성 검사
두 속성의 차이는 FormData로 확인할 수 있다.
<form id="account-form">
<label for="account-id">계정 ID</label>
<input id="account-id" name="accountId" value="user-42" readonly>
<label for="promotion-code">프로모션 코드</label>
<input id="promotion-code" name="promotionCode" value="WELCOME" disabled>
</form>const form = document.querySelector('#account-form')
const entries = [...new FormData(form).entries()]
console.log(entries)
// [['accountId', 'user-42']]readonly인 accountId는 entry list에 들어간다.disabled인 promotionCode는 name과 value가 있어도 entry list에서 제외된다.
두 상태 모두 사용자가 값을 바꿀 수 없으므로 constraint validation에 참여하지 않는다.required나 pattern을 함께 적어도 이 control의 제출을 막는 검증으로 기대하면 안 된다.
서버가 반드시 받아야 하는 값을 disabled input에만 넣으면 요청에서 값이 사라진다.
반대로 readonly 값은 전송되지만 서버가 신뢰해도 되는 값이 되는 것은 아니다.
readonly의 적용 범위
readonly는 모든 form control에 적용되지 않는다.text, search, tel, url, email, password, date, month, week, time, datetime-local, number 타입의 input과 textarea가 여기에 해당한다.
select, button, checkbox, radio, file, range, color 같은 control에는 readonly가 적용되지 않는다.
예를 들어 checkbox에 readonly를 적어도 사용자가 체크 상태를 바꾸는 동작은 막히지 않는다.
select의 현재 값을 보여주면서 제출해야 한다면 무작정 readonly를 붙이지 말고 요구사항에 맞는 구조를 따로 설계해야 한다.
읽기 전용 텍스트로 값을 표시하고 필요한 데이터를 hidden input에 넣는 방식도 있지만 서버에서는 그 hidden 값 역시 검증해야 한다.
disabled는 button, fieldset, input, optgroup, option, select, textarea에 사용할 수 있다.
텍스트 입력뿐 아니라 선택 상자와 제출 버튼을 일시적으로 사용할 수 없게 만들 때도 쓸 수 있다.
fieldset 단위 비활성화
여러 control을 한꺼번에 사용할 수 없게 만들 때는 각 요소에 disabled를 반복하기보다 fieldset을 사용할 수 있다.
<fieldset disabled>
<legend>배송지 입력</legend>
<label for="address">주소</label>
<input id="address" name="address">
<label for="delivery-note">배송 요청 사항</label>
<textarea id="delivery-note" name="deliveryNote"></textarea>
</fieldset>disabled fieldset의 자손 form control은 함께 비활성화되고 제출 데이터에서도 제외된다.
다만 fieldset의 첫 번째 legend 자식 안에 있는 control은 이 비활성화 규칙의 예외다.
전체 영역을 잠갔다고 가정하기 전에 폼 구조를 확인해야 한다.
Boolean 속성과 동적 변경
disabled와 readonly는 Boolean 속성이다.
HTML Boolean 속성은 값의 문자열이 아니라 속성의 존재 여부로 상태를 결정한다.
<!-- 두 input 모두 disabled 상태다. -->
<input name="first" disabled>
<input name="second" disabled="false">disabled="false"라고 적어도 속성이 존재하므로 disabled 상태다.
상태를 해제하려면 속성을 제거하거나 DOM property인 input.disabled에 false를 대입해야 한다.
HTML 속성은 소문자인 readonly이고 DOM property는 대문자 O가 들어간 readOnly라는 점도 구분한다.
상태 표현과 접근성
네이티브 disabled와 readonly를 사용하면 브라우저와 보조 기술에 control 상태가 전달된다.
색상이나 pointer-events: none만으로 비슷한 모양을 만드는 것은 같은 의미와 키보드 동작을 제공하지 않는다.
상태 차이를 색상 하나에만 의존하지 말고 주변 설명이나 명확한 텍스트로도 전달한다.
disabled control은 포커스를 받지 않으므로 비활성화 이유를 해당 control의 hover나 focus 안내에만 넣으면 키보드 사용자가 확인하기 어렵다.
aria-disabled="true"는 비활성 상태를 보조 기술에 알리지만 네이티브 동작을 자동으로 막지 않는다.
이를 사용한다면 클릭과 키보드 동작을 코드로 막고 상태 스타일도 함께 구현해야 한다.
네이티브 form control에서는 먼저 기본 disabled와 readonly가 요구사항에 맞는지 확인한다.
각 control의 이름은 상태와 별개로 필요하다.
label 연결 방법은 HTML label과 input 연결 방법에서 이어서 볼 수 있다.
서버 검증과 선택 기준
disabled와 readonly는 브라우저 UI의 동작을 정하는 속성이지 보안 경계가 아니다.
사용자는 개발자 도구로 속성을 제거하거나 다른 값으로 요청을 직접 만들 수 있다.
상품 가격, 권한, 계정 ID처럼 서버가 신뢰해야 하는 값은 요청에 표시된 상태와 관계없이 서버에서 다시 확인한다.
disabled라서 값이 오지 않을 것이라는 가정도 안전하지 않다.
readonly라서 값이 바뀌지 않을 것이라는 가정도 안전하지 않다.
현재 선택할 수 없는 옵션이나 실행할 수 없는 버튼이면 disabled를 사용한다.
해당 값이 폼 제출 데이터에서도 빠져야 하는지 함께 확인한다.
값을 보여주고 복사와 포커스를 허용하면서 제출 데이터에도 포함해야 하는 텍스트 계열 control이면 readonly를 사용한다.
사용하는 input 타입에 readonly가 실제로 적용되는지도 확인한다.
단순히 수정할 필요가 없는 정보를 보여주는 화면이라면 form control을 쓰지 않고 일반 텍스트로 표시하는 편이 더 자연스러울 수 있다.
폼 모양을 유지해야 한다는 이유만으로 모든 값을 input에 넣을 필요는 없다.
참고 자료


























