대용량 데이터를 동기식으로 처리하거나 외부 API를 대량 호출할 때 화면에 PHP Fatal error: Maximum execution time of 30 seconds exceeded 에러가 발생하며 작업이 멈춘 경험이 있을 것이다.
다만 단순 타임아웃 증가로 피할 수 있는지 아니면 코드의 구조적 문제인지 정확한 원인이나 해결책을 모르는 경우가 많다.
이번에는 이 에러가 정확히 뭔지, 왜 필요한지, 그리고 서버 설정과 코드 최적화를 통해 어떻게 해결하는지 완벽하게 정리해서 소개하겠다.

 

1. Maximum execution time exceeded 에러의 원인

PHP는 기본적으로 개별 웹 요청이 서버 리소스를 독점하는 것을 막기 위해 스크립트 최대 실행 시간을 제한한다. 이 제한을 담당하는 설정값이 바로 php.ini의 max_execution_time이다.
기본값은 30초로 설정되어 있으며, 스크립트가 30초를 초과하여 실행되면 PHP 엔진이 강제로 프로세스를 중단시키며 런타임 에러를 출력한다.

원인 분류발생 상황해결 방향
서버 설정 제한기본 설정값(30초)보다 오래 걸리는 긴 배치 작업php.ini 또는 set_time_limit()을 통한 타임아웃 연장
외부 통신 병목API 응답 지연 또는 무한 대기(cURL 타임아웃 미설정)cURL 타임아웃 옵션 추가 및 소켓 타임아웃 제한
비효율적인 루프대량의 데이터베이스 조회 및 내부 중첩 반복문청크(Chunk) 단위 데이터 처리 및 메모리/시간 분할

 

2. 타임아웃 설정을 변경하는 3가지 방법

이 문제를 해결하는 가장 직관적인 방법은 PHP 실행 제한 시간을 늘려주는 것이다. 작업 환경과 권한에 따라 세 가지 방법 중 선택하여 적용할 수 있다.

방법 1: php.ini 파일 수정 (전체 서버 적용)

서버 관리자 권한이 있다면 php.ini 파일에서 직접 해당 값을 변경할 수 있다. 수정 후에는 반드시 PHP-FPM이나 Apache 웹서버를 재시작해야 적용된다.

; php.ini 수정
max_execution_time = 300 ; 30초에서 300초(5분)로 변경
방법 2: PHP 코드 내에서 dynamici하게 설정 (set_time_limit)

특정 대용량 작업이 실행되는 파일 상단에만 시간을 연장하고 싶다면 set_time_limit() 함수를 사용한다. 0을 입력하면 실행 시간 제한이 무제한으로 변경된다.

<?php
// 스크립트 최상단에 추가
set_time_limit(300); // 실행 시간을 300초로 설정
?>
방법 3: .htaccess 또는 .user.ini 설정 (가상 호스팅 환경)

php.ini를 수정할 수 없는 공유 호스팅 환경에서는 .htaccess 파일에 php_value를 추가하거나 .user.ini를 작성하여 적용할 수 있다.

# .htaccess 파일에 추가하는 경우
php_value max_execution_time 300

 

3. 실전 예제: 배치 처리 최적화 코드

무작정 execution time을 늘리는 것은 서버 전체의 안정성을 저해할 수 있다. 코드 내부에서 루프가 돌 때마다 시간을 리셋하거나 데이터를 분할 처리하는 방법이 훨씬 안전하다.

✗ 잘못된 방식: 모든 데이터를 한 번에 가져와 오래 걸리는 동기 작업 처리

<?php
// ✗ 모든 회원에게 메일을 발송하는 루프 (30초 초과 시 에러 발생)
$users = $db->query("SELECT * FROM users")->fetchAll();

foreach ($users as $user) {
    // 외부 SMTP 통신으로 30초를 쉽게 넘김
    send_email($user['email']);
}
?>

✓ 올바른 방식: 루프 내 execution time 리셋 및 청크 단위 데이터 처리

<?php
// ✓ 반복할 때마다 타이머를 리셋하고 메모리를 효율적으로 관리
$page = 0;
$limit = 100;

do {
    // 반복문 실행 시작 시마다 30초의 추가 시간을 부여
    set_time_limit(30);

    $offset = $page * $limit;
    $users = $db->query("SELECT * FROM users LIMIT {$limit} OFFSET {$offset}")->fetchAll();

    foreach ($users as $user) {
        send_email($user['email']);
    }

    $page++;
} while (count($users) === $limit);
?>

출력/결과: 30초 타임아웃 중단 없이 만 명 이상의 사용자 데이터도 안전하게 순차 처리 완료.

 

4. 주의사항 및 흔한 실수

✗ 잘못된 접근: set_time_limit(0)을 설정해 두고 모든 문제를 해결했다고 방치하는 경우.
무제한 실행은 스크립트가 무한 루프에 빠졌을 때 PHP 프로세스가 서버 CPU와 메모리를 점유한 채 종료되지 않는 치명적인 결과를 초래한다.

✓ 올바른 접근: 웹 요청(HTTP) 처리 시에는 최대 30~60초 이내로 유지하고, 수분 이상 소요되는 대용량 데이터 작업은 CLI(Command Line Interface) 환경의 크론잡(Cron)이나 비동기 큐 시스템으로 분리해야 한다.

또한 Nginx와 FastCGI(PHP-FPM)를 사용할 경우, PHP 타임아웃을 300초로 늘려도 Nginx의 fastcgi_read_timeout 설정이 60초라면 Nginx에서 504 Gateway Timeout 에러가 먼저 발생한다는 점을 명심해야 한다.

 

5. 정리 및 결론

PHP Maximum execution time exceeded 에러는 서버 리소스를 보호하기 위한 안전장치가 작동했음을 의미한다. 무조건 타임아웃 시간만 늘리는 대증요법보다는 작업의 성격에 맞춰 데이터 청크 분할 및 runtime 시간 리셋을 적용하는 것이 바람직하다. 이 글의 3단계 청크 처리 예제 코드를 참고해 스크립트를 개선하면 타임아웃 에러 없이 안정적으로 대용량 작업을 완료할 수 있을 것이다.