티스토리 뷰
MySQL server has gone away (2006) 해결 방법|CodeIgniter 3 엑셀 다운로드 오류까지 정리
예술하는 개발자 최씨 2026. 10. 13. 16:58
PHP 관리자 페이지에서 엑셀을 다운로드하거나, 오래 걸리는 배치 작업을 실행할 때 갑자기 다음과 같은 오류가 나타나는 경우가 있습니다.
Error Number: 2006 — MySQL server has gone away
개발 환경에서는 멀쩡했는데 운영 서버에서만 발생하거나, 평소에는 정상이다가 데이터가 많아질 때만 나타나기도 합니다. 이때 wait_timeout을 무작정 늘리는 방법부터 시도하는 경우가 많지만, 원인은 연결 유휴 시간, 큰 패킷, 서버 재시작, 네트워크 문제 등 여러 가지일 수 있습니다.
이번 글에서는 MySQL 오류 2006의 원인을 구분하는 방법부터 CodeIgniter 3에서 연결을 재확인하는 방법, 대용량 엑셀 다운로드를 나누어 처리하는 PHP 7.2 호환 예제까지 정리합니다.
1. MySQL server has gone away는 어떤 오류일까?
MySQL server has gone away는 애플리케이션이 사용하던 MySQL 연결을 더 이상 정상적으로 사용할 수 없을 때 발생하는 오류입니다.
비슷한 메시지로 Lost connection to MySQL server during query도 있습니다. 일반적으로 오류 2006은 이미 끊어진 연결에 요청을 보내는 상황에서, 오류 2013은 쿼리 처리 또는 응답을 받는 도중 연결이 끊기는 상황에서 나타날 수 있습니다.
다만 오류 번호만으로 정확한 원인을 단정할 수는 없습니다. 오류 발생 시점과 서버 로그를 함께 확인해야 합니다.
2. 대표적인 원인 5가지
원인 ① 오래 사용하지 않은 연결이 종료된 경우
MySQL은 일정 시간 동안 활동이 없는 연결을 종료할 수 있습니다. 이때 관련된 설정이 wait_timeout입니다.
예를 들어 PHP에서 파일을 생성하거나 외부 API를 호출하는 데 오랜 시간이 걸렸고, 그동안 DB에 아무 요청도 보내지 않았다면 다음 쿼리에서 끊어진 연결을 재사용하려 할 수 있습니다.
중요한 점은 wait_timeout이 쿼리 실행시간 제한이 아니라 연결의 유휴시간 제한이라는 것입니다.
원인 ② 너무 큰 SQL 또는 데이터 패킷
한 번에 대량의 데이터를 INSERT하거나 매우 큰 BLOB 값을 전송할 때 max_allowed_packet 제한을 넘으면 연결이 종료될 수 있습니다.
여기서 자주 오해하는 부분이 있습니다. max_allowed_packet은 조회 결과 전체가 몇만 행이라는 이유만으로 반드시 초과하는 값이 아닙니다. SQL 문장, 개별 행 등 실제 통신 패킷의 크기와 관련된 제한입니다.
원인 ③ MySQL 서버 재시작 또는 비정상 종료
DB 서버가 재시작되거나 메모리 부족 등으로 종료되면 기존 연결은 무효가 됩니다. 이 경우 PHP 코드보다 MySQL 오류 로그와 서버 상태를 먼저 살펴봐야 합니다.
원인 ④ 네트워크 연결이 끊긴 경우
DB와 웹 서버가 서로 다른 장비에 있다면 방화벽, 네트워크 장비, 연결 제한시간 때문에 통신이 끊길 수 있습니다.
원인 ⑤ 대용량 작업 중 다른 문제가 겹친 경우
엑셀 파일 생성처럼 시간이 오래 걸리는 작업에서는 PHP 메모리 부족, 느린 SQL, DB 연결 유휴시간 초과가 함께 나타날 수 있습니다.
따라서 엑셀 다운로드에서 오류가 났다는 이유만으로 MySQL 타임아웃이 원인이라고 단정하면 안 됩니다.
3. 먼저 확인할 MySQL 설정과 상태
원인을 파악하려면 DB에서 다음 SQL을 실행해보세요.
SELECT VERSION();
SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';
SHOW VARIABLES LIKE 'max_allowed_packet';
SHOW VARIABLES LIKE 'net_read_timeout';
SHOW VARIABLES LIKE 'net_write_timeout';
SHOW GLOBAL STATUS LIKE 'Uptime';
SHOW GLOBAL STATUS LIKE 'Aborted_clients';
SHOW GLOBAL STATUS LIKE 'Aborted_connects';
각 항목의 의미는 다음과 같습니다.
| wait_timeout | 비대화형 연결의 유휴 종료시간 |
| interactive_timeout | 대화형 연결의 유휴 종료시간 |
| max_allowed_packet | 허용하는 최대 통신 패킷 크기 |
| net_read_timeout | 서버가 네트워크에서 데이터를 읽을 때의 제한시간 |
| net_write_timeout | 서버가 데이터를 보낼 때의 제한시간 |
| Uptime | MySQL 서버가 실행된 시간 |
| Aborted_clients | 비정상적으로 종료된 클라이언트 연결 누적 수 |
| Aborted_connects | 실패한 연결 시도 누적 수 |
Aborted_clients 값이 높다고 무조건 문제가 있다는 뜻은 아닙니다. 오류가 발생하는 시간대에 수치가 급격하게 증가하는지를 확인하는 것이 더 중요합니다.
실제 DB 설정값은 MySQL 버전과 운영 환경에 따라 다를 수 있으므로 인터넷에서 본 기본값을 그대로 가정하지 않는 것이 좋습니다.
4. CodeIgniter 3에서는 reconnect()를 언제 사용할까?

CodeIgniter 3에는 DB 연결을 다시 확인하는 reconnect() 메서드가 있습니다.
// 오래 걸리는 외부 작업 또는 파일 처리
$this->makeReportFile();
// 다음 DB 쿼리를 실행하기 전에 연결 상태 확인
$this->db->reconnect();
$query = $this->db->query(
'SELECT id, name FROM members WHERE id = ?',
array($memberId)
);
이 코드는 긴 작업 때문에 DB 연결이 유휴 상태로 오래 유지된 후, 다음 쿼리를 실행하기 직전에 사용하는 예제입니다.
하지만 reconnect()가 모든 오류 2006을 해결해주지는 않습니다.
이미 실행 중인 쿼리가 실패한 경우, MySQL 서버가 계속 재시작되는 경우, 패킷 제한을 초과한 경우에는 원인을 별도로 해결해야 합니다.
특히 트랜잭션 도중 연결이 끊어졌다면 이전 트랜잭션 상태가 유지된다고 가정하면 안 됩니다. 결제·정산·포인트 지급 같은 작업은 무조건 재시도하지 말고 중복 처리 방지 로직을 먼저 마련해야 합니다.
5. CodeIgniter 3의 DB 설정도 점검하기
CodeIgniter 3의 일반적인 DB 설정 파일은 다음 경로에 있습니다.
application/config/database.php
예를 들어 다음 설정을 확인할 수 있습니다.
$db['default'] = array(
'dsn' => '',
'hostname' => '127.0.0.1',
'username' => 'DB_USER',
'password' => 'DB_PASSWORD',
'database' => 'DB_NAME',
'dbdriver' => 'mysqli',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'save_queries' => FALSE
);
위 설정은 설명용으로 필요한 항목만 발췌한 예제입니다. 실제 프로젝트의 나머지 설정을 지우고 통째로 교체해서는 안 됩니다.
pconnect는 영구 연결 사용 여부입니다. 연결 재사용 때문에 문제가 의심되는 환경이라면 FALSE를 검토할 수 있지만, 이 값만 바꾼다고 모든 연결 끊김이 해결되지는 않습니다.
save_queries를 FALSE로 설정하면 CodeIgniter가 실행한 SQL 문자열과 실행시간 기록을 계속 보관하지 않으므로, 쿼리가 많은 요청의 메모리 사용을 줄이는 데 도움이 될 수 있습니다. 대신 해당 요청에서 SQL 프로파일링에 필요한 정보도 줄어듭니다.
운영 DB 계정과 비밀번호는 공개 저장소에 올리지 않도록 주의하세요.
6. 엑셀 다운로드에서 오류가 난다면 LIMIT보다 구조를 먼저 확인
관리자에서 전체 회원 목록이나 정산 내역을 엑셀로 내려받는 경우를 생각해보겠습니다.
다음 코드는 구현이 간단하지만 데이터가 많아질수록 부담이 커질 수 있습니다.
$query = $this->db->get('orders');
$rows = $query->result_array();
// 모든 데이터를 메모리에 올린 다음 엑셀 생성
문제는 조회 결과를 한꺼번에 배열로 만든 뒤 다시 엑셀 객체에 적재하면 PHP 메모리 사용량이 커질 수 있다는 점입니다.
이때 DB 오류가 함께 발생할 수는 있지만, PHP 메모리 부족과 MySQL 연결 끊김은 구분해서 진단해야 합니다.
가능하다면 필요한 컬럼만 조회하고, 한 번에 가져오는 행 수를 제한하고, 다운로드 파일을 조금씩 기록하는 방식으로 변경하는 것이 좋습니다.
7. PHP 7.2 + CodeIgniter 3에서 대용량 CSV 내려받기
엑셀에서 열 수 있는 CSV 파일이면 충분한 관리자 기능이라면 복잡한 XLSX 라이브러리 없이도 처리할 수 있습니다.
다음은 기본키 id가 있는 orders 테이블을 1,000건씩 조회해 CSV로 출력하는 예제입니다.
public function downloadOrdersCsv()
{
// 실제 서비스에서는 이 위치에서 관리자 권한을 먼저 검사해야 합니다.
$this->load->database();
$fileName = 'orders_' . date('Ymd_His') . '.csv';
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Cache-Control: no-store, no-cache, must-revalidate');
$fp = fopen('php://output', 'w');
// Excel에서 UTF-8 한글이 깨지는 문제를 줄이기 위한 BOM
fwrite($fp, "\xEF\xBB\xBF");
fputcsv($fp, array('주문번호', '주문자', '금액', '주문일시'));
$lastId = 0;
$limit = 1000;
while (true) {
$query = $this->db
->select('id, customer_name, total_price, created_at')
->from('orders')
->where('id >', $lastId)
->order_by('id', 'ASC')
->limit($limit)
->get();
$rows = $query->result_array();
$query->free_result();
if (empty($rows)) {
break;
}
foreach ($rows as $row) {
fputcsv($fp, array(
$row['id'],
$row['customer_name'],
$row['total_price'],
$row['created_at']
));
$lastId = (int) $row['id'];
}
if (count($rows) < $limit) {
break;
}
}
fclose($fp);
exit;
}
이 예제는 PHP 7.2에서 사용할 수 있는 문법으로 작성했습니다. 다만 실제 테이블명과 컬럼명은 프로젝트에 맞게 변경해야 합니다.
WHERE id > 마지막으로 처리한 ID 방식은 흔히 키셋 페이지네이션이라고 부릅니다. OFFSET을 계속 늘리는 방식보다 대량 데이터에서 효율적일 수 있습니다. id에는 인덱스가 있어야 합니다.
주의할 점도 있습니다. 다운로드 도중 데이터가 추가되거나 수정되면 조회 결과가 달라질 수 있으므로, 정산처럼 특정 시점의 일관된 결과가 필요한 경우에는 조회 기준 시점이나 스냅샷 전략을 별도로 마련해야 합니다.
또 CSV는 XLSX 파일이 아닙니다. 수식, 셀 서식, 여러 시트가 필요한 기능이라면 XLSX 라이브러리에서 청크 처리와 스트리밍 방식을 검토해야 합니다.
보안 주의: 사용자 입력값이 =, +, -, @ 등으로 시작하면 스프레드시트 프로그램에서 수식으로 해석될 수 있습니다. 외부 입력이 포함되는 CSV에는 수식 주입 방어도 적용해야 합니다.
8. 타임아웃 값을 늘리기 전에 꼭 확인할 것

인터넷에서는 다음처럼 설정값을 크게 늘리라는 해결책을 자주 볼 수 있습니다.
[mysqld]
wait_timeout=28800
max_allowed_packet=128M
하지만 이것은 모든 서버에 권장되는 공통 설정이 아닙니다.
예를 들어 wait_timeout이 이미 충분히 큰데도 특정 엑셀 다운로드에서만 오류가 발생한다면, 유휴 연결 종료보다는 SQL 처리량, 개별 패킷 크기, 서버 자원, 네트워크 등을 살펴봐야 합니다.
max_allowed_packet도 실제로 큰 패킷이 문제일 때 조정해야 합니다. 무조건 큰 값으로 변경하기보다 현재 값과 실제 데이터 크기를 비교하는 편이 좋습니다.
설정 변경은 MySQL 버전과 운영 방식에 따라 적용 범위가 다를 수 있습니다. 특히 전역 설정 변경은 이미 열린 세션에 바로 반영되지 않을 수 있으므로 테스트 환경에서 확인한 후 운영에 적용해야 합니다.
9. SQL이 느린 경우에는 EXPLAIN부터 확인
다운로드가 느리다면 DB 연결 설정을 바꾸기 전에 실행계획을 확인해보세요.
EXPLAIN
SELECT id, customer_name, total_price, created_at
FROM orders
WHERE id > 100000
ORDER BY id ASC
LIMIT 1000;
확인할 부분은 인덱스 사용 여부, 예상 조회 행 수, 불필요한 정렬 여부입니다.
특히 조회 조건에 맞는 인덱스가 없다면 한 번에 1,000건씩 나누어 가져오더라도 매번 많은 데이터를 탐색할 수 있습니다.
또한 JOIN이나 서브쿼리를 사용하는 경우에는 실제 실행계획과 데이터 분포에 따라 성능이 달라집니다. JOIN이 언제나 서브쿼리보다 빠르다고 단정하기보다는 실행계획으로 비교하는 것이 정확합니다.
10. 오류 상황별 해결 방법 정리
| 한동안 아무 작업이 없다가 첫 쿼리에서 오류 | wait_timeout, 연결 재사용 |
| 큰 INSERT 또는 BLOB 전송에서만 오류 | max_allowed_packet |
| 서버 전체에서 갑자기 오류 발생 | MySQL 재시작·오류 로그·자원 사용량 |
| 원격 DB에서 간헐적으로 오류 | 네트워크·방화벽·연결 제한 |
| 관리자 대용량 다운로드에서 오류 | SQL 실행계획·PHP 메모리·파일 생성 시간 |
| 트랜잭션 중 연결 끊김 | 트랜잭션 상태 확인·중복 처리 방지 |
자주 묻는 질문
Q. wait_timeout을 늘리면 무조건 해결되나요?
아닙니다. 오래 유휴 상태였던 연결이 종료되는 문제에는 도움이 될 수 있지만, 큰 패킷이나 DB 서버 재시작, 네트워크 장애 등에는 다른 조치가 필요합니다.
Q. max_execution_time을 늘리면 해결되나요?
PHP 스크립트 실행시간 제한에 걸리는 경우에는 관련이 있지만, MySQL 오류 2006의 직접적인 해결책은 아닙니다. PHP 제한시간과 DB 연결 문제를 구분해야 합니다.
Q. $this->db->reconnect()를 모든 쿼리 앞에 넣어도 되나요?
권장하지 않습니다. 연결이 오래 유휴 상태였을 가능성이 있는 시점에 사용하는 것이 목적에 맞습니다. 불필요한 재연결 확인은 비용이 들고 근본 원인을 가릴 수 있습니다.
Q. 엑셀 다운로드를 CSV로 바꾸면 반드시 해결되나요?
그렇지 않습니다. CSV 스트리밍은 PHP 메모리 사용을 줄이는 데 도움이 될 수 있지만, 실제 원인이 MySQL 서버 장애나 네트워크 문제라면 별도로 해결해야 합니다.
마무리
MySQL server has gone away 오류는 메시지는 하나지만 원인은 다양합니다.
가장 중요한 것은 오류가 언제 발생하는지 먼저 구분하는 것입니다.
오랫동안 유휴 상태였던 연결이 문제라면 재연결 전략을 검토하고, 큰 데이터 전송이 문제라면 패킷 크기를 확인해야 합니다. 관리자 엑셀 다운로드처럼 오래 걸리는 기능이라면 조회 쿼리와 파일 생성 방식을 함께 개선하는 것이 좋습니다.
무조건 타임아웃 값을 크게 늘리기보다 MySQL 설정, 서버 로그, SQL 실행계획, PHP 메모리 사용량을 순서대로 확인해보세요. 문제의 원인을 찾는 데 훨씬 도움이 됩니다.
'IT 한스푼' 카테고리의 다른 글
| 윈도우11 26H2 업데이트 안 뜰 때|24H2 지원 종료 후 25H2 설치·버전 확인 (0) | 2026.10.22 |
|---|---|
| 윈도우10 보안 업데이트 2027년까지 연장! ESU 무료 신청 방법과 등록 오류 해결법 (2026 최신) (0) | 2026.10.14 |
| VS Code Intelephense 빨간줄 해결|CodeIgniter 3 P1014·P1132·P1010 오류와 PHP 7.2 설정 (0) | 2026.10.12 |
| 실시간 웹 통신의 혁명: SSE(Server-Sent Events) 완벽 가이드 (3) | 2024.09.11 |
| Spring boot 설치법 시작하기 (0) | 2023.09.19 |
- Total
- Today
- Yesterday
- 단백질
- 웹 개발
- 예방
- express.js
- vue.js
- 다이어트
- 관리
- 서버
- pm2
- FCM
- 운동 효과
- 가을여행
- 단풍여행
- 건강한 식습관
- 관절염
- 체중 감량
- 운동
- 혈당 관리
- Firebase Cloud Messaging
- node.js
- 라이프스타일
- 고지혈증
- 딥링크
- mysql
- 건강
- socket.io
- 고혈압
- codeigniter3
- 크로스핏
- 당뇨
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 |
