현록

HTML disabled와 readonly 차이

HTML 폼에서 값을 수정하지 못하게 만들 때 disabledreadonly를 자주 사용한다.
두 속성 모두 사용자 입력을 막지만 브라우저가 해당 control을 다루는 방식은 다르다.

disabled는 지금 사용할 수 없는 control을 폼 동작에서 제외한다.
readonly는 값을 읽고 제출할 수는 있지만 사용자가 수정하지 못하게 한다.
서버 전송 여부와 control 종류를 먼저 확인해야 예상하지 못한 폼 동작을 피할 수 있다.

핵심 차이

기준disabledreadonly
사용자의 값 변경불가능불가능
키보드 포커스받지 않음받을 수 있음
값 선택과 복사일반적인 상호작용에서 제외가능
폼 제출 데이터포함되지 않음포함됨
constraint validation참여하지 않음참여하지 않음
주요 적용 대상button, fieldset, input, select, textarea일부 inputtextarea
사용자의 값 변경
disabled
불가능
readonly
불가능
키보드 포커스
disabled
받지 않음
readonly
받을 수 있음
값 선택과 복사
disabled
일반적인 상호작용에서 제외
readonly
가능
폼 제출 데이터
disabled
포함되지 않음
readonly
포함됨
constraint validation
disabled
참여하지 않음
readonly
참여하지 않음
주요 적용 대상
disabled
button, fieldset, input, select, textarea
readonly
일부 inputtextarea

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']]

readonlyaccountId는 entry list에 들어간다.
disabledpromotionCodenamevalue가 있어도 entry list에서 제외된다.

두 상태 모두 사용자가 값을 바꿀 수 없으므로 constraint validation에 참여하지 않는다.
requiredpattern을 함께 적어도 이 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 값 역시 검증해야 한다.

disabledbutton, 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 속성과 동적 변경

disabledreadonly는 Boolean 속성이다.
HTML Boolean 속성은 값의 문자열이 아니라 속성의 존재 여부로 상태를 결정한다.

<!-- 두 input 모두 disabled 상태다. -->
<input name="first" disabled>
<input name="second" disabled="false">

disabled="false"라고 적어도 속성이 존재하므로 disabled 상태다.
상태를 해제하려면 속성을 제거하거나 DOM property인 input.disabledfalse를 대입해야 한다.
HTML 속성은 소문자인 readonly이고 DOM property는 대문자 O가 들어간 readOnly라는 점도 구분한다.

상태 표현과 접근성

네이티브 disabledreadonly를 사용하면 브라우저와 보조 기술에 control 상태가 전달된다.
색상이나 pointer-events: none만으로 비슷한 모양을 만드는 것은 같은 의미와 키보드 동작을 제공하지 않는다.

상태 차이를 색상 하나에만 의존하지 말고 주변 설명이나 명확한 텍스트로도 전달한다.
disabled control은 포커스를 받지 않으므로 비활성화 이유를 해당 control의 hover나 focus 안내에만 넣으면 키보드 사용자가 확인하기 어렵다.

aria-disabled="true"는 비활성 상태를 보조 기술에 알리지만 네이티브 동작을 자동으로 막지 않는다.
이를 사용한다면 클릭과 키보드 동작을 코드로 막고 상태 스타일도 함께 구현해야 한다.
네이티브 form control에서는 먼저 기본 disabledreadonly가 요구사항에 맞는지 확인한다.

각 control의 이름은 상태와 별개로 필요하다.
label 연결 방법은 HTML label과 input 연결 방법에서 이어서 볼 수 있다.

서버 검증과 선택 기준

disabledreadonly는 브라우저 UI의 동작을 정하는 속성이지 보안 경계가 아니다.
사용자는 개발자 도구로 속성을 제거하거나 다른 값으로 요청을 직접 만들 수 있다.

상품 가격, 권한, 계정 ID처럼 서버가 신뢰해야 하는 값은 요청에 표시된 상태와 관계없이 서버에서 다시 확인한다.
disabled라서 값이 오지 않을 것이라는 가정도 안전하지 않다.
readonly라서 값이 바뀌지 않을 것이라는 가정도 안전하지 않다.

현재 선택할 수 없는 옵션이나 실행할 수 없는 버튼이면 disabled를 사용한다.
해당 값이 폼 제출 데이터에서도 빠져야 하는지 함께 확인한다.

값을 보여주고 복사와 포커스를 허용하면서 제출 데이터에도 포함해야 하는 텍스트 계열 control이면 readonly를 사용한다.
사용하는 input 타입에 readonly가 실제로 적용되는지도 확인한다.

단순히 수정할 필요가 없는 정보를 보여주는 화면이라면 form control을 쓰지 않고 일반 텍스트로 표시하는 편이 더 자연스러울 수 있다.
폼 모양을 유지해야 한다는 이유만으로 모든 값을 input에 넣을 필요는 없다.

참고 자료

관련 포스트
HTTP Cache-Control no-cache와 no-store 차이 thumbnail
HTTP Cache-Control no-cache와 no-store 차이
HTTP Cache-Control의 no-cache와 no-store 차이를 저장 여부, 재검증, ETag와 304 응답, private와 max-age 조합 기준으로 정리합니다.
Git reset과 revert 차이 thumbnail
Git reset과 revert 차이
Git reset과 revert의 차이를 커밋 이력, staging area와 working tree, 공유 브랜치의 안전한 되돌리기 기준으로 정리합니다.
Git fetch와 pull 차이 thumbnail
Git fetch와 pull 차이
Git fetch와 pull이 원격 변경을 가져오고 현재 브랜치에 통합하는 방식을 정리합니다. remote-tracking branch, upstream, fast-forward, merge와 rebase 선택 기준을 함께 봅니다.
HTTP Content-Type과 Accept 차이 thumbnail
HTTP Content-Type과 Accept 차이
HTTP Content-Type과 Accept 헤더의 방향, JSON 요청과 응답 예제, 콘텐츠 협상과 406·415 상태 코드의 차이를 정리합니다.
HTTP PUT과 PATCH 차이 thumbnail
HTTP PUT과 PATCH 차이
HTTP PUT과 PATCH의 차이를 리소스 교체와 변경 명령, 멱등성, JSON Patch와 JSON Merge Patch, ETag를 이용한 동시 수정 방지 기준으로 정리합니다.
HTML label과 input 연결 방법 thumbnail
HTML label과 input 연결 방법
HTML label과 input을 for와 id로 연결하는 방법, 암시적 연결, 폼 그룹과 보조 설명, 접근성 오류 패턴을 정리합니다.
HTML section과 article 차이 thumbnail
HTML section과 article 차이
HTML section과 article의 의미, 독립성 판단 기준, 중첩 구조, 제목과 접근성을 고려한 선택 방법을 정리합니다.
HTML div와 span 차이 thumbnail
HTML div와 span 차이
HTML div와 span의 차이를 기본 display, 담을 수 있는 내용, 시맨틱 태그, CSS와 JavaScript에서 그룹화하는 기준으로 정리합니다.
HTTP 상태 코드 정리 thumbnail
HTTP 상태 코드 정리
HTTP 상태 코드의 2xx, 3xx, 4xx, 5xx 의미와 200, 201, 204, 301, 304, 400, 401, 403, 404, 500, 502, 503 차이를 정리합니다.
HTML id와 class 차이 thumbnail
HTML id와 class 차이
HTML id와 class의 차이를 문서 내 유일성, 여러 값 사용, CSS 선택자, JavaScript 탐색, fragment 링크와 이름 작성 기준으로 정리합니다.
HTML button과 a 태그 차이 thumbnail
HTML button과 a 태그 차이
HTML button과 a 태그의 차이를 이동과 동작의 의미, href, type 속성, 폼 제출, 키보드 접근성, 잘못된 사용 패턴으로 정리합니다.
HTTP GET과 POST 차이 thumbnail
HTTP GET과 POST 차이
HTTP GET과 POST의 차이를 초보자 기준으로 정리합니다. 조회와 변경, query string과 request body, 캐시, safe, idempotent, form과 fetch 사용 기준을 함께 봅니다.
npm outdated와 npm update 차이 thumbnail
npm outdated와 npm update 차이
npm outdated와 npm update의 차이를 초보자 기준으로 정리합니다. Current, Wanted, Latest의 의미와 semver 범위 안에서 업데이트되는 방식, package-lock.json 변화까지 함께 봅니다.
npm install -g와 npx 차이 thumbnail
npm install -g와 npx 차이
npm install -g와 npx의 차이를 초보자 기준으로 정리합니다. 전역 설치, 로컬 설치, 일회성 실행, 프로젝트 scripts에 넣는 기준까지 함께 봅니다.
package.json에서 ^와 ~ 차이 thumbnail
package.json에서 ^와 ~ 차이
package.json 의존성 버전 앞에 붙는 ^와 ~의 차이를 초보자 기준으로 정리합니다. semantic versioning, 버전 범위, 0.x 예외, package-lock.json과의 관계까지 함께 봅니다.
Git merge와 rebase 차이 thumbnail
Git merge와 rebase 차이
Git merge와 rebase가 커밋 그래프를 어떻게 바꾸는지 정리합니다. fast-forward, merge commit, rebase의 커밋 ID 변경, 충돌 처리, 공유 브랜치에서의 안전한 사용 기준까지 살펴봅니다.
npx와 npm exec 차이 thumbnail
npx와 npm exec 차이
npx와 npm exec가 어떤 명령어인지 초보자 기준으로 정리합니다. 로컬 패키지 실행, 원격 패키지 임시 실행, --package 옵션, -- 인자 전달 차이까지 함께 봅니다.
npm scripts와 npm run 정리 thumbnail
npm scripts와 npm run 정리
npm scripts란 무엇인지, package.json scripts와 npm run의 관계를 초보자 기준으로 정리합니다. npm run dev, npm start, npm test, node_modules/.bin, -- 인자 전달 방식까지 함께 봅니다.
dependencies와 devDependencies 차이 thumbnail
dependencies와 devDependencies 차이
package.json의 dependencies와 devDependencies 차이를 초보자 기준으로 정리합니다. npm install과 npm install -D, 배포 환경 설치, package-lock.json과의 관계까지 함께 봅니다.
npm ci와 npm install 차이 thumbnail
npm ci와 npm install 차이
npm ci와 npm install의 차이를 초보자 기준으로 정리합니다. clean install의 의미, package-lock.json 조건, CI에서 npm ci를 쓰는 이유, .npmrc 플래그 주의점까지 함께 봅니다.
.npmrc 파일이란? thumbnail
.npmrc 파일이란?
storybook을 사용해보려고 하다 마주한 이슈의 해결법을 알아보다가 등장한 .npmrc라는 파일에 대해 공부해보았다. .npmrc 파일이란? .npmrc 파일은 npm에 대한 config 파일이다. (npm에 대한 rc 파일) 프로젝트별 registry, install 옵션, 인증 토큰처럼 npm CLI가 읽는 설정을 관리할 때 사용한다.
스토리북이란? thumbnail
스토리북이란?
Storybook은 UI 컴포넌트를 독립적으로 개발하고, 문서화하고, 테스트하기 위한 프론트엔드 워크샵입니다. Storybook 10.4 기준 설치 흐름과 stories, 문서화, 테스트 활용 방식을 정리합니다.
TTV와 TTI 차이 thumbnail
TTV와 TTI 차이
TTV와 TTI의 차이를 초보자 기준으로 정리합니다. 사용자가 화면을 보는 시점, 상호작용 가능한 시점, FCP, LCP, INP, Core Web Vitals와의 관계까지 함께 봅니다.
Maria DB 외부 접속 설정하기 thumbnail
Maria DB 외부 접속 설정하기
안녕하세요. 오늘은 Maria DB 초기 세팅 시, 외부에서 접속이 안될 때 매뉴얼을 작성해보겠습니다. dotenv 패키지를 통해서 환경변수로 관리한다면, 별도의 추가 작업을 할 일이 없으실 겁니다.
package-lock.json 파일의 역할 thumbnail
package-lock.json 파일의 역할
안녕하세요. 오늘은 node 환경의 개발자라면 한번쯤 궁금했을만한 package-lock.json의 역할에 대해 알아보겠습니다. 우리는 node 환경에서 개발을 할 때 다양한 패키지들을 설치하여 활용하곤 합니다. 우리가 설치하는 패키지 또한 다른 npm 패키지를 활용하여 만든 패키지들이고 이들 또한 설치를 하게 됩니다. 이렇게 직간접적으로 설치된 패키지들은 대부분 호환성을 "^1.1.5"와 같이 표현하여, 범위로 지정해두고 있습니다.
Linux 환경 배포 자동화 체험해보기 thumbnail
Linux 환경 배포 자동화 체험해보기
안녕하세요. 요즘 포트폴리오를 만들면서 서버 상에 자주 반영할 일이 생겼는데, 매번 명령어들을 타이핑하는 것이 비효율적이라 생각이 들어 배포 자동화를 생각해보게 되었습니다. 현재 레벨에서는 단순히 명령어들만 단축시켜도 효율적이라 생각이 들어 간단한 쉘 스크립트만 작성하였습니다. 정말 간단하니 여러분도 도전해보시길 바랍니다.
협업 필수품. Prettier thumbnail
협업 필수품. Prettier
안녕하세요. 오늘은 Prettier이라는 도구에 대해 알려드리고자 합니다. 개발자는 각자의 코딩스타일이 존재합니다. 그러다보니 같은 프로젝트에서도 작성하는 소스마다 스타일이 제각기 다르기 일쑤입니다. 그럴 때 도입하면 좋은 것이 Prettier입니다. 프로젝트 root 폴더에 .prettierrc 라는 파일을 생성한 뒤, 위 예시와 같이 원하는 옵션을 JSON 형식으로 작성해주면 됩니다.