HTML 폼과 PHP
폼이란?
폼(Form)은 사용자로부터 데이터를 입력받아 서버로 전송하는 HTML 요소입니다.
로그인, 회원가입, 게시글 작성, 검색창 — 모두 폼으로 만들어집니다.
폼의 기본 구조는 이렇습니다.
<form action="처리할PHP파일.php" method="post">
<!-- 여기에 입력 요소들 -->
<input type="submit" value="전송">
</form>
action— 폼 데이터를 보낼 PHP 파일 경로method— 전송 방식.GET또는POST
GET vs POST
| GET | POST | |
|---|---|---|
| 데이터 위치 | URL에 노출 (?name=홍길동) | HTTP 본문에 숨겨짐 |
| 길이 제한 | 있음 (URL 길이 제한) | 사실상 없음 |
| 보안 | 낮음 (주소창에 보임) | 상대적으로 안전 |
| 용도 | 검색, 필터, 페이지 이동 | 로그인, 글쓰기, 파일 업로드 |
| 북마크 | 가능 | 불가능 |
GET은 "이 조건으로 데이터를 가져와줘" 할 때,
POST는 "이 데이터를 서버에 저장/처리해줘" 할 때 씁니다.
<?php
// GET 방식으로 넘어온 값: $_GET['변수명']
$keyword = $_GET['keyword'] ?? '';
// POST 방식으로 넘어온 값: $_POST['변수명']
$name = $_POST['name'] ?? '';
폼 요소들
text — 일반 텍스트 입력
<input type="text" name="username" size="20" maxlength="20" placeholder="이름을 입력하세요">
name— PHP에서$_POST['username']으로 받습니다maxlength— 입력 가능한 최대 글자 수placeholder— 입력 전 안내 문구value— 초기값 (수정 폼에서 기존 값을 채울 때 씁니다)
password — 비밀번호 입력
<input type="password" name="pass" maxlength="30">
입력한 내용이 *로 가려집니다. 그 외에는 text와 동일합니다.
email / number / tel — HTML5 타입
<input type="email" name="email" placeholder="example@email.com">
<input type="number" name="age" min="1" max="120">
<input type="tel" name="phone" placeholder="010-0000-0000">
브라우저가 형식을 기본 검증해줍니다. 모바일에서는 맞는 키보드가 자동으로 올라옵니다.
브라우저 검증만 믿으면 안 됩니다. PHP에서도 반드시 서버 쪽 검증을 해야 합니다.
클라이언트 쪽 검증은 언제든 우회할 수 있습니다.
checkbox — 체크박스
<input type="checkbox" name="agree" value="yes"> 약관에 동의합니다
<!-- 여러 개 선택 (배열로 받습니다) -->
<input type="checkbox" name="hobby[]" value="coding"> 코딩
<input type="checkbox" name="hobby[]" value="reading"> 독서
<input type="checkbox" name="hobby[]" value="gaming"> 게임
<?php
// 단일 체크박스
$agree = isset($_POST['agree']) ? 'yes' : 'no';
// 복수 체크박스 — 배열로 넘어옵니다
$hobbies = $_POST['hobby'] ?? [];
foreach ($hobbies as $hobby) {
echo htmlspecialchars($hobby, ENT_QUOTES, 'UTF-8') . "\n";
}
radio — 라디오 버튼
여러 개 중 하나만 선택할 때 씁니다. name이 같은 것들이 한 그룹이 됩니다.
<input type="radio" name="gender" value="m"> 남성
<input type="radio" name="gender" value="f"> 여성
<input type="radio" name="gender" value="n" checked> 선택 안 함
<?php
$gender = $_POST['gender'] ?? 'n';
select — 드롭다운 / 리스트박스
<!-- 드롭다운 (하나만 선택) -->
<select name="city">
<option value="">-- 지역 선택 --</option>
<option value="seoul">서울</option>
<option value="busan">부산</option>
<option value="daegu">대구</option>
</select>
<!-- 리스트박스 (여러 개 선택 가능) -->
<select name="skills[]" size="4" multiple>
<option value="php">PHP</option>
<option value="js">JavaScript</option>
<option value="css">CSS</option>
<option value="sql">SQL</option>
</select>
<?php
$city = $_POST['city'] ?? '';
$skills = $_POST['skills'] ?? []; // multiple이면 배열로 넘어옵니다
textarea — 긴 텍스트 입력
<textarea name="content" rows="10" cols="60" placeholder="내용을 입력하세요"></textarea>
rows— 세로 줄 수cols— 가로 글자 수<textarea>태그 사이에 내용을 넣으면 초기값이 됩니다
hidden — 숨겨진 필드
사용자에게 보이지 않지만 폼과 함께 전송되는 값입니다.
<input type="hidden" name="board_id" value="42">
수정/삭제 시 ID 값 전달, CSRF 토큰 전달 등에 씁니다.
파일 업로드
파일을 업로드할 때는 폼에 enctype="multipart/form-data"를 반드시 추가해야 합니다.
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="photo" accept="image/*">
<input type="submit" value="업로드">
</form>
<?php
declare(strict_types=1);
if ($_FILES['photo']['error'] !== UPLOAD_ERR_OK) {
exit('업로드 실패: ' . $_FILES['photo']['error']);
}
$file = $_FILES['photo'];
$maxSize = 5 * 1024 * 1024; // 5MB
// 파일 크기 확인
if ($file['size'] > $maxSize) {
exit('파일 크기는 5MB 이하여야 합니다.');
}
// 확장자 확인 (MIME 타입만 믿으면 안 됩니다 — 조작 가능)
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'], true)) {
exit('허용되지 않는 파일 형식입니다.');
}
// 파일명 고정 (원본 파일명 그대로 저장하면 보안 문제!)
$newName = uniqid('upload_', true) . '.' . $ext;
$dest = __DIR__ . '/uploads/' . $newName;
if (!move_uploaded_file($file['tmp_name'], $dest)) {
exit('파일 저장에 실패했습니다.');
}
echo '업로드 완료: ' . $newName;
원본 파일명을 그대로 저장하면 경로 탐색 공격(../../etc/passwd 같은 이름)에 취약합니다.
항상 uniqid()나 random_bytes()로 새 파일명을 만들어서 저장하세요.
폼 처리 패턴 — Post/Redirect/Get
폼을 처리한 뒤 바로 결과 HTML을 출력하면 새로 고침 시 같은 요청이 반복됩니다.
(게시글이 두 번 저장되는 문제)
이를 막기 위해 Post → 처리 → Redirect → Get 패턴을 씁니다.
<?php
declare(strict_types=1);
// 1. POST로 데이터 받기
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: write.php');
exit;
}
// 2. 처리 (DB 저장 등)
$name = trim($_POST['name'] ?? '');
$content = trim($_POST['content'] ?? '');
// ... DB에 저장 ...
// 3. 처리 완료 후 리다이렉트 (새로 고침해도 재전송 안 됨)
header('Location: list.php');
exit;
폼 처리에서 가장 중요한 것은 서버 쪽 입력값 검증입니다.
HTML 속성(required,maxlength등)은 브라우저에서만 동작하고, 악의적인 요청은 이를 그냥 우회합니다.
PHP 쪽에서 항상 길이, 형식, 필수 여부를 다시 확인하는 습관을 들이세요.