SOFTMOA TECHNOLOGY

HTML disabled 입력값이 전송되지 않을 때 readonly와 FormData 확인 방법

소프트모아가 정리한 기술 기록입니다.

Contents
작성일 2026. 11. 06.

HTML 입력창에 주문번호가 보이는데 서버에는 값이 오지 않는다면 disabled부터 확인하세요. 브라우저는 disabled 상태인 입력을 FormData에 넣지 않습니다. 사용자가 수정하지 못하게 하면서 값을 전송하려는 텍스트 입력이라면 readonly가 맞습니다. 다만 부모 fieldset이 disabled이면 readonly를 붙여도 빠집니다. 서버 코드를 바꾸기 전에 브라우저가 만든 항목부터 비교하면 누락 지점을 좁힐 수 있습니다.

빈 값과 아예 없는 항목을 구분하기

주문번호 O-17을 비활성 입력에 표시하고 고객 코드 C-04는 읽기 전용으로 표시한 화면을 가정하겠습니다. 메모는 아직 쓰지 않았습니다. 세 칸 모두 화면에는 존재하지만 전송할 항목은 같지 않습니다. 아래 예제는 새 폼을 메모리에 만들 뿐 현재 페이지의 폼을 고치거나 요청을 보내지 않습니다. 개발자 도구 Console에서 실행하세요. 여기서는 Chromium 156.0.0.0에서 확인했습니다.

(() => {
  const form = document.createElement('form');
  form.innerHTML = `
    <input name="orderId" value="O-17" disabled>
    <input name="customer" value="C-04" readonly>
    <input name="memo" value="">
    <fieldset disabled>
      <input name="zone" value="A" readonly>
    </fieldset>`;
  const order = form.elements.namedItem('orderId');
  const zone = form.elements.namedItem('zone');
  const before = new FormData(form);
  console.log('before', JSON.stringify([...before]));
  console.log('missing', before.has('orderId'), before.get('orderId'));
  console.log('empty', before.has('memo'), before.get('memo'));

  order.disabled = false;
  order.readOnly = true;
  zone.disabled = false;
  console.log('zone', zone.disabled, zone.matches(':disabled'));
  console.log('after', JSON.stringify([...new FormData(form)]));
  console.log('old', JSON.stringify([...before]));
})();

첫 출력 before에는 customer=C-04와 memo의 빈 문자열만 들어갑니다. missing은 false, null이고 empty는 true, 빈 문자열입니다. 주문번호는 빈 값으로 전송되는 것이 아니라 항목 자체가 없습니다. 서버에서 둘을 모두 빈 문자열로 바꾸면 이 차이를 놓칩니다. FormData 표준의 has와 get 규칙으로 존재 여부와 값을 따로 확인하세요.

속성을 바꾼 뒤에도 남는 누락

예제의 뒷부분은 orderId의 disabled를 해제하고 readOnly를 켭니다. 새로 만든 after에는 orderId=O-17, customer=C-04, memo의 빈 문자열이 들어갑니다. 반면 old에는 여전히 처음의 두 항목만 남습니다. 이미 만든 FormData가 입력창 변경을 자동으로 따라가지는 않습니다. 속성을 고친 뒤 새 데이터를 만들었는지도 확인해야 합니다.

zone은 여전히 없습니다. zone.disabled를 false로 바꿔도 출력은 false, true입니다. 앞의 값은 입력 자체의 disabled 속성 상태이고 뒤의 값은 :disabled에 해당하는 실제 비활성 상태입니다. 부모 fieldset의 disabled가 남아 있기 때문입니다. 이 예제에는 legend가 없지만, 표준에는 disabled fieldset의 첫 번째 legend 자손에 대한 예외가 있습니다. 이 조건은 HTML의 비활성화 규칙에서 확인했습니다.

실제 화면에서는 해당 구역을 왜 잠갔는지 먼저 판단하세요. 사용할 수 없는 구역이라면 값이 빠지는 것이 의도한 결과일 수 있습니다. 전송이 필요한 구역인데 부모까지 잠갔다면 활성 범위를 다시 설계합니다. 이 예제의 수정은 주문번호만 복원하며 구역 A를 복원하거나 이전 요청의 누락을 고치지는 않습니다.

readonly로 바꿔도 해결되지 않는 경우

  • readonly는 모든 입력 요소의 대체 잠금 기능이 아닙니다. select와 체크박스에는 같은 방식으로 적용되지 않습니다.
  • disabled="false"도 속성이 존재하므로 비활성입니다. 속성을 제거하거나 JavaScript의 disabled 프로퍼티를 false로 바꿉니다.
  • 일반 텍스트 입력에 name이 없거나 비어 있으면 전송 항목에서 빠집니다. label 연결에 쓰는 id만으로는 부족합니다.

readonly 정의와 폼 항목 구성 규칙을 기준으로 구분한 조건입니다. readonly 입력은 브라우저의 제약 검증에서도 제외됩니다. 읽기 전용으로 잠갔다고 required 검사가 유지된다고 가정하지 마세요.

실제 요청에서는 어디까지 확인할까

운영 화면에서는 개발자 도구 Network의 요청 본문에 주문번호가 있는지 확인합니다. 위 실험은 FormData 생성만 검사했습니다. JavaScript가 별도로 JSON을 만들거나 항목을 추가·삭제한다면 실제 요청은 실험과 다를 수 있습니다. 요청에 값이 있는데 저장 결과에서만 빠진다면 서버의 바인딩과 저장 로직을 확인할 차례입니다.

전송 항목이 복구돼도 신뢰성 검사는 남습니다. readonly나 hidden 입력은 요청 위조를 막지 못하므로 주문번호가 로그인 사용자의 주문인지 서버에서 다시 확인하도록 설계하세요. 클라이언트에 표시한 금액을 그대로 승인 금액으로 쓰지 않는 것도 같은 이유입니다. 이번 점검은 값의 누락 원인을 찾는 절차이며 권한 검증이나 저장 성공을 보장하지 않습니다.

소프트모아의 웹 개발 안내는 softmoa.com에서 확인하세요. 폼 수정 범위는 화면의 편집 제한, 요청 항목, 서버 검증을 나눠 정하면 분명해집니다.

소프트모아는 해당 시스템을 구축합니다. 문의하기

Share this

Html and CSS

Have a questions?

견적 및 기술문의

mobile : 010-7931-4813

Contact Form