파일 업로드 폼을 만들었는데 $_FILES 배열이 텅 비어있다. 백엔드 코드는 멀쩡한데 왜 자꾸 파일이 안 넘어오는 걸까. 대부분의 개발자들은 이 상황에서 PHP 코드만 디버깅하다가 며칠을 낭비한다. 사실 $_FILES 배열이 비는 원인은 PHP 설정, HTML 폼 속성, 서버 리소스 등 여러 곳에 숨어있다. 이번에는 실무에서 직접 겪은 $_FILES 배열 비는 이유 5가지와 각각의 완벽한 해결책을 정리해서 소개하겠다.

 

1. HTML 폼에 enctype="multipart/form-data" 누락

가장 흔한 실수다. 파일 업로드를 받으려면 HTML 폼에 반드시 enctype 속성을 지정해야 한다. 이걸 빠뜨리면 아무리 PHP 코드가 완벽해도 $_FILES는 항상 빈 배열이다.

✗ 잘못된 코드

<form method="POST" action="upload.php">
  <input type="file" name="document">
  <button type="submit">업로드</button>
</form>

이 경우 $_FILES는 완전히 비어있다. 브라우저가 파일을 multipart 형식으로 인코딩하지 않기 때문이다.

✓ 올바른 코드

<form method="POST" action="upload.php" enctype="multipart/form-data">
  <input type="file" name="document">
  <button type="submit">업로드</button>
</form>

enctype="multipart/form-data"를 추가하면 브라우저가 자동으로 파일 데이터를 multipart 형식으로 인코딩해서 전송한다. 이제 $_FILES['document']에 파일 정보가 들어온다.

 

2. POST 최대 크기 제한 초과 (post_max_size)

PHP.ini의 post_max_size 설정보다 큰 파일을 업로드하면, $_FILES뿐 아니라 $_POST도 함께 비워진다. 이것도 $_FILES가 빈 것처럼 보이지만, 실은 요청 자체가 거부된 것이다.

현재 설정 확인

<?php
echo "post_max_size: " . ini_get('post_max_size');
echo "upload_max_filesize: " . ini_get('upload_max_filesize');
?>

일반적인 기본값은 post_max_size=8M, upload_max_filesize=2M이다.

✗ 잘못된 상황

<?php
// 10MB 파일 업로드 시도했는데 post_max_size가 8M로 설정됨
var_dump($_FILES); // array(0) { }
var_dump($_POST);  // array(0) { }
// 둘 다 비어있음
?>

✓ 올바른 해결법 (php.ini 수정)

post_max_size = 100M
upload_max_filesize = 50M
memory_limit = 256M

post_max_size는 upload_max_filesize보다 항상 커야 한다(multipart 헤더와 필드도 포함되므로). memory_limit도 충분히 높여야 대용량 파일 처리 중 메모리 오류가 안 난다.

 

3. 서버 디스크 용량 부족 또는 권한 오류

PHP 임시 업로드 디렉토리(/tmp 또는 upload_tmp_dir)의 디스크 용량이 부족하거나 쓰기 권한이 없으면, 파일이 이동되지 않는다. 이 경우 $_FILES는 에러 정보를 담고 있다.

✗ 배열은 있지만 에러 확인 안 함

<?php
if ($_FILES['document']['error'] !== UPLOAD_ERR_OK) {
  var_dump($_FILES['document']);
  // array(5) {
  //   ["name"] => string(10) "file.pdf"
  //   ["type"] => string(15) "application/pdf"
  //   ["tmp_name"] => string(0) ""
  //   ["error"] => int(6) // UPLOAD_ERR_NO_TMP_DIR
  //   ["size"] => int(0)
  // }
}
?>

error 코드 6은 임시 디렉토리가 없거나 접근 불가라는 뜻이다.

✓ 올바른 디렉토리 권한 설정

chmod 755 /var/www/uploads
chown www-data:www-data /var/www/uploads

또는 php.ini에서 업로드 임시 디렉토리 변경:

upload_tmp_dir = /var/www/uploads/tmp

 

4. 클라이언트 측 JavaScript에서 파일 입력값 미설정

JavaScript로 폼을 동적으로 처리할 때, input[type=file]에 파일을 선택하지 않고 제출하면 $_FILES는 빈 배열이다. 또는 JavaScript에서 form을 완전히 재구성할 때 file input을 누락하기도 한다.

✗ 잘못된 JavaScript

<form id="uploadForm" enctype="multipart/form-data">
  <input type="file" id="fileInput" name="document">
  <button type="button" id="submitBtn">업로드</button>
</form>

<script>
document.getElementById('submitBtn').addEventListener('click', function() {
  const formData = new FormData();
  formData.append('name', 'John'); // 파일 input을 append하지 않음
  fetch('upload.php', { method: 'POST', body: formData });
});
</script>

✓ 올바른 JavaScript

<script>
document.getElementById('submitBtn').addEventListener('click', function() {
  const fileInput = document.getElementById('fileInput');
  const formData = new FormData();
  
  // 파일이 선택되었는지 확인
  if (fileInput.files.length === 0) {
    alert('파일을 선택하세요');
    return;
  }
  
  formData.append('document', fileInput.files[0]);
  formData.append('name', 'John');
  
  fetch('upload.php', { method: 'POST', body: formData });
});
</script>

 

5. PHP 설정에서 file_uploads 비활성화

매우 드물지만, php.ini에서 file_uploads = Off로 설정되어 있으면 모든 파일 업로드가 거부된다. 공유 호스팅에서 보안상 이렇게 설정하는 경우가 가끔 있다.

확인 방법

<?php
echo ini_get('file_uploads'); // 0 = Off, 1 = On
?>

✓ 올바른 설정 (php.ini 또는 .htaccess)

file_uploads = On

만약 공유 호스팅에서 이 설정을 바꿀 수 없다면, 호스팅 업체에 요청해야 한다.

 

완벽한 파일 업로드 검증 함수

위의 모든 경우를 대비한 안전한 업로드 검증 함수를 만들어보자.

<?php
function validateFileUpload($fileInputName) {
  // 1. 파일이 업로드되었는지 확인
  if (!isset($_FILES[$fileInputName])) {
    return ['success' => false, 'error' => '파일 입력이 없습니다'];
  }
  
  $file = $_FILES[$fileInputName];
  
  // 2. 업로드 에러 코드 확인
  if ($file['error'] !== UPLOAD_ERR_OK) {
    $errorMessages = [
      UPLOAD_ERR_INI_SIZE => 'upload_max_filesize 초과',
      UPLOAD_ERR_FORM_SIZE => 'HTML MAX_FILE_SIZE 초과',
      UPLOAD_ERR_PARTIAL => '파일이 부분적으로만 업로드됨',
      UPLOAD_ERR_NO_FILE => '파일이 선택되지 않음',
      UPLOAD_ERR_NO_TMP_DIR => '임시 디렉토리 없음',
      UPLOAD_ERR_CANT_WRITE => '디스크 쓰기 실패',
      UPLOAD_ERR_EXTENSION => 'PHP 확장 프로그램이 업로드 중단'
    ];
    return ['success' => false, 'error' => $errorMessages[$file['error']] ?? '알 수 없는 에러'];
  }
  
  // 3. 임시 파일이 존재하는지 확인
  if (!is_uploaded_file($file['tmp_name'])) {
    return ['success' => false, 'error' => '업로드된 파일이 아닙니다'];
  }
  
  // 4. 파일 크기 확인
  $maxSize = 50 * 1024 * 1024; // 50MB
  if ($file['size'] > $maxSize) {
    return ['success' => false, 'error' => '파일이 너무 큽니다'];
  }
  
  // 5. 파일 확장자 확인 (MIME 타입도 검증)
  $allowedExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx'];
  $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
  if (!in_array($extension, $allowedExtensions)) {
    return ['success' => false, 'error' => '허용되지 않는 파일 형식입니다'];
  }
  
  return ['success' => true, 'file' => $file];
}

// 사용 예
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $result = validateFileUpload('document');
  if ($result['success']) {
    $file = $result['file'];
    $uploadDir = '/var/www/uploads/';
    $fileName = time() . '_' . preg_replace('/[^a-zA-Z0-9._-]/', '', $file['name']);
    
    if (move_uploaded_file($file['tmp_name'], $uploadDir . $fileName)) {
      echo json_encode(['success' => true, 'message' => '업로드 성공']);
    } else {
      echo json_encode(['success' => false, 'error' => '파일 이동 실패']);
    }
  } else {
    echo json_encode($result);
  }
}
?>

 

요약 및 체크리스트
원인 확인 방법 해결책
HTML enctype 누락 폼 태그 검사 enctype="multipart/form-data" 추가
post_max_size 초과 phpinfo() 확인 php.ini에서 post_max_size 증가
디렉토리 권한/용량 $_FILES['error'] 코드 확인 chmod 755, chown www-data 설정
클라이언트 파일 미선택 JavaScript 디버깅 fileInput.files.length 확인 후 FormData append
file_uploads 비활성화 ini_get('file_uploads') php.ini에서 file_uploads = On 설정

PHP 파일 업로드는 간단해 보이지만, 실제로는 클라이언트, 브라우저, PHP 설정, OS 권한 등 여러 계층이 관여한다. $_FILES 배열이 비는 문제는 대부분 이 중 한 가지 계층에서 발생한다. 위의 5가지 원인을 체계적으로 확인하고, 제공한 검증 함수를 사용하면 안전한 파일 업로드 시스템을 만들 수 있을 것이다. 특히 에러 코드를 무시하고 넘어가는 습관이 가장 위험하니, 항상 $_FILES['error']를 먼저 확인하는 것을 기억하자.