HTML hidden 입력: 숨은 값 전송과 보안상 주의점
input type="hidden"은 입력창을 화면에 표시하지 않으면서 폼에 이름과 값을 포함하는 HTML 요소입니다. name은 전송할 항목 이름, value는 값입니다. 화면에서 보이지 않을 뿐 사용자가 확인하거나 변경할 수 있으므로 비밀번호·권한·가격 등을 보호하는 수단으로 사용하면 안 됩니다.
hidden은 언제 사용하나요?
게시물 수정 폼에서 대상 게시물 ID를 함께 보내거나, 현재 검색 조건을 다음 요청에 이어 보내는 용도로 사용합니다. 사용자가 직접 입력할 필요 없는 값을 전달하는 도구이며, 그 값이 올바른지 판단하는 책임은 서버에 있습니다.
예를 들어 게시물 ID를 받았다고 그 글의 수정 권한이 생기지는 않습니다. 서버는 로그인 세션으로 사용자를 확인하고 해당 게시물에 대한 권한을 검사해야 합니다. 계정 정보나 접속 IP도 클라이언트가 보낸 hidden 값만으로 신뢰해서는 안 됩니다.
| 구분 | 화면에서의 의미 | 폼 데이터 |
|---|---|---|
type="hidden" |
입력 UI가 없고 포커스를 받지 않습니다. | 이름이 있고 비활성화되지 않았다면 포함됩니다. |
disabled |
해당 컨트롤을 비활성화합니다. | 제외됩니다. |
display: none으로 숨긴 일반 입력 |
CSS로 화면 배치에서 숨깁니다. | CSS로 숨겼다는 이유만으로 전송에서 제외되지는 않습니다. |
id는 스크립트에서 요소를 찾는 데 사용할 수 있지만 전송 이름을 대신하지 못합니다. name이 빠진 hidden 요소는 일반적인 폼 제출에 이름·값 항목으로 포함되지 않습니다. required를 붙여도 hidden 요소는 브라우저의 제약 조건 검증에 참여하지 않습니다.
보이지 않는 값이 함께 전송되는지 확인하기
아래 전체 코드를 hidden.html로 저장해 브라우저에서 여세요. 별도 서버는 필요하지 않습니다. 제출 시 실제 네트워크 요청은 보내지 않고 FormData로 만들어진 이름·값 목록을 표시합니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hidden 값 확인</title>
<style>
body { margin: 24px; font-family: sans-serif; }
label { display: block; margin: 12px 0; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
</style>
</head>
<body>
<h1>색상 선택</h1>
<form id="demo">
<label><input type="checkbox" name="color" value="blue" checked> 파랑</label>
<label><input type="checkbox" name="color" value="red"> 빨강</label>
<input type="hidden" id="size" name="size" value="big">
<input type="hidden" name="excluded" value="not-sent" disabled>
<button type="submit">전송 값 확인</button>
<button type="button" id="change">숨은 값을 small로 변경</button>
<button type="button" id="restore">숨은 값을 big으로 복원</button>
</form>
<p id="status" aria-live="polite">초기 숨은 값은 big입니다.</p>
<pre id="result" aria-live="polite"></pre>
<script>
const form = document.querySelector('#demo');
const size = document.querySelector('#size');
const result = document.querySelector('#result');
const status = document.querySelector('#status');
form.addEventListener('submit', (event) => {
event.preventDefault();
result.textContent = JSON.stringify([...new FormData(form).entries()], null, 2);
});
function setSize(value) {
size.value = value;
status.textContent = `현재 숨은 값: ${value}. 전송 값 확인을 다시 누르세요.`;
}
document.querySelector('#change').addEventListener('click', () => setSize('small'));
document.querySelector('#restore').addEventListener('click', () => setSize('big'));
</script>
</body>
</html>
- 처음에 전송 값 확인을 누르면
color=blue와size=big이 나옵니다. size 입력창은 화면에 없지만 값은 포함됩니다.excluded는disabled때문에 빠집니다. - 빨강도 체크하고 다시 확인하면
color이름으로 blue와 red 두 항목이 생깁니다. 체크를 모두 해제하면 color 항목은 없고 size만 남습니다. - 숨은 값을 small로 변경을 누르고 다시 제출하면
size=small이 나옵니다. hidden 값도 JavaScript 등으로 바꿀 수 있다는 뜻입니다. - 복원 버튼으로 big으로 돌린 뒤 다시 제출해 확인합니다. 변경 버튼은 동작을 관찰하기 위한 실습용이며 실제 서비스에 필요한 기능은 아닙니다.
실제 서버에 제출할 때
실제 서비스에서는 폼의 action에 존재하는 처리 경로를 지정하고 method="post" 등 서버가 지원하는 메서드를 선택합니다. 위 예제의 submit 이벤트 처리기는 요청을 막으므로, 실제 전송으로 바꾸려면 해당 처리기를 제거하거나 서버 요청 코드로 교체해야 합니다. hidden 항목도 일반 텍스트 항목과 마찬가지로 서버의 폼 데이터 처리 기능으로 받습니다.
이름이 같은 체크박스를 여러 개 받을 때는 서버 프레임워크의 배열·복수 값 처리 규칙도 확인해야 합니다. 브라우저가 보낸 반복 항목을 서버가 마지막 값 하나로 읽는 경우가 있기 때문입니다. 예제의 JavaScript 결과는 반복 이름을 보존하여 이 차이를 관찰하도록 합니다.
숨김과 보안을 혼동하지 않기
hidden 값은 HTML 소스·개발자 도구·네트워크 요청에서 확인할 수 있습니다. HTTPS로 전송해도 값을 보낸 사용자에게서 숨겨지는 것은 아닙니다. 제품 ID를 받으면 서버에서 제품과 가격을 다시 조회하고, 허용된 크기 값인지 검사하는 등 서버의 신뢰할 수 있는 정보와 대조하세요.
hidden 필드로 CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조) 토큰을 전달하는 구현도 있습니다. 보호 효과는 서버가 토큰을 적절히 발급하고 검증하는 데서 생깁니다. 임의의 고정 문자열을 hidden에 넣는 것만으로 요청 위조가 방지되지는 않습니다.
요소의 동작과 검증 제외 규칙은 MDN hidden 입력 문서에서 확인할 수 있습니다.




