워드프레스가 특정 시간대에만 느려지는 이유와 PHP Worker·CPU·메모리 구분법
워드프레스 사이트가 하루 종일 느린 것이 아니라 매일 비슷한 시간에만 지연된다면, 단순한 서버 사양 부족으로 보기 어렵습니다. 특정 시점에 방문자 요청이 몰렸거나 예약 작업이 실행됐을 수 있고, PHP Worker가 외부 API나 데이터베이스 응답을 기다리느라 묶였을 수도 있습니다. 먼저 문제가 발생한 정확한 시간과 함께 CPU, 메모리, PHP 처리량, 예약 작업 로그를 비교해야 불필요한 업그레이드를 피할 수 있습니다.
종일 느린 사이트와 특정 시간에 느린 사이트는 다르게 봐야 한다
하루 종일 페이지가 느리다면 이미지 용량, 플러그인, 테마, 데이터베이스, 캐시 설정, 서버 성능을 전반적으로 점검해야 합니다. 반면 오후 3시 5분부터 10분간, 또는 매일 새벽 2시 전후처럼 증상이 반복된다면 그 시간에 실행되는 작업을 먼저 의심하는 편이 효율적입니다.
| 증상 | 먼저 볼 곳 |
|---|---|
| 하루 종일 느리다 | 이미지 용량, 플러그인, 테마, 캐시 설정, 서버 사양 |
| 매일 같은 시간에만 느리다 | 그 시간에 실행되는 예약 작업, 백업, 보안 스캔 |
| 관리자 화면만 느리다 | 캐시를 타지 않는 동적 요청, 특정 플러그인의 외부 API 호출 |
| CPU는 낮은데 느리다 | 느린 데이터베이스 쿼리, 외부 API 대기로 묶인 PHP Worker |
가능한 원인은 방문자 증가만이 아닙니다. 자동 백업, 보안 스캔, 데이터베이스 정리, 이미지 변환, 대량 이메일 발송, 외부 서비스 동기화, 워드프레스 예약 작업, 공유 호스팅의 자원 제한도 영향을 줄 수 있습니다.
특히 관리자 화면만 느리고 일반 방문자용 페이지는 빠르다면 캐시 차이를 봐야 합니다. 공개 페이지는 페이지 캐시에서 바로 전달되지만 /wp-admin/은 대부분 PHP와 데이터베이스를 거쳐 동적으로 처리됩니다. 그래서 서버나 데이터베이스에 문제가 생기면 관리자 화면에서 먼저 체감되는 경우가 많습니다.
CPU, 메모리, PHP Worker는 서로 다른 지표다
세 지표는 자주 뭉뚱그려 이야기되지만 가리키는 대상이 다릅니다. 어느 쪽이 문제인지에 따라 손대야 할 곳도 달라집니다.
| 지표 | 무엇을 보여주나 | 자주 하는 오해 |
|---|---|---|
| CPU | 연산이 필요한 작업의 양 | 상승이 원인인지 결과인지 구분하지 않는 것 |
| 메모리 | memory_limit(스크립트 한도)과 서버 RAM(전체 공유분) |
PHP 오류가 곧 RAM 증설 필요라고 보는 것 |
| PHP Worker | 동시에 처리 중인 동적 요청 수 | CPU 여유가 있으면 안 밀린다고 보는 것 |
CPU 사용량은 PHP 코드 실행, 데이터 처리, 이미지 압축처럼 연산이 필요한 작업이 얼마나 많이 진행되는지를 보여줍니다. 사이트가 느려진 시점에 CPU 그래프가 급격히 상승했다면 관련성을 의심할 수 있지만, CPU 상승이 원인인지 무거운 작업의 결과인지는 추가 확인이 필요합니다.
메모리는 실행 중인 프로그램이 데이터를 보관하는 공간입니다. 여기서 PHP memory_limit과 서버 전체 RAM을 구분해야 합니다. memory_limit은 개별 PHP 프로세스나 스크립트가 사용할 수 있는 한도이고, 서버 RAM은 운영체제, 웹 서버, 데이터베이스, 여러 PHP 프로세스가 함께 나눠 씁니다. PHP 메모리 오류가 발생했다고 항상 RAM 증설이 필요한 것은 아니며, 반대로 PHP 제한값만 높인다고 서버 전체 메모리 부족이 해결되는 것도 아닙니다.
워드프레스의 WP_MEMORY_LIMIT도 서버의 물리 메모리를 직접 늘리는 기능은 아닙니다. 호스팅과 PHP 환경이 허용하는 범위 안에서 워드프레스가 요청하는 메모리 설정에 가깝습니다. 따라서 wp-config.php에 값을 추가하기 전에 실제 PHP 설정과 호스팅의 계정별 메모리 제한을 확인해야 합니다.
PHP Worker는 동적인 PHP 요청을 처리하는 작업자입니다. 캐시되지 않은 페이지, 관리자 화면, WooCommerce 장바구니와 주문 처리처럼 PHP 실행이 필요한 요청이 동시에 들어오면 Worker가 사용됩니다. 모든 Worker가 점유된 상태에서는 CPU에 여유가 있어도 새 요청이 대기하면서 사이트가 느려질 수 있습니다.
다만 Worker 부족과 CPU 부족은 같은 문제가 아닙니다. 외부 API 응답이나 느린 데이터베이스 쿼리를 기다리는 동안에도 Worker는 계속 점유됩니다. 반대로 CPU가 이미 포화된 상황에서 Worker 수만 늘리면 동시에 실행되는 작업이 증가해 CPU와 메모리 경쟁이 더 심해질 수 있습니다.
지표를 실제로 재는 방법
앞에서 CPU와 메모리, PHP Worker가 서로 다른 지표라고 했습니다. 문제는 이 셋을 어떻게 구분해서 보느냐입니다. 호스팅 관리 화면의 그래프만 보면 “무언가 높다”까지는 알아도 무엇이 병목인지는 알기 어렵습니다. 서버에 접속할 수 있다면 명령 몇 개로 훨씬 분명해집니다.
부하 평균은 코어 수와 함께 읽는다
가장 많이 오해받는 지표가 부하 평균입니다. uptime을 실행하면 끝에 숫자 세 개가 나옵니다.
$ uptime
17:53:58 up 152 days, 7:14, 2 users, load average: 0.06, 0.03, 0.00
순서대로 1분, 5분, 15분 평균입니다. 그런데 이 숫자는 절대값이 아니라 코어 수에 대한 비율로 읽어야 합니다. 코어 수는 다음으로 확인합니다.
$ nproc
2
코어가 2개인 서버에서 부하 평균 2.0이면 정확히 꽉 찬 상태이고, 0.06이면 3% 정도만 쓰고 있다는 뜻입니다. 코어가 8개인 서버라면 같은 2.0이 25%에 불과합니다. 코어 수를 모르고 숫자만 비교하면 아무 의미가 없습니다. “부하가 1.5인데 괜찮은가요”라는 질문에 답이 없는 이유가 이것입니다.
세 숫자의 관계도 정보입니다. 1분 값만 높고 15분 값이 낮으면 방금 시작된 일시적 부하이고, 세 값이 비슷하게 높으면 지속적인 포화입니다. 특정 시간대에만 느려지는 문제를 쫓을 때는 그 시간에 실제로 접속해서 이 값을 봐야 판단이 섭니다.
PHP Worker는 개수와 메모리를 함께 본다
Worker 고갈은 CPU와 무관하게 일어납니다. CPU가 놀고 있어도 Worker가 전부 대기 중이면 새 요청은 줄을 섭니다. 현재 떠 있는 개수는 이렇게 셉니다.
$ ps -C php-fpm --no-headers | wc -l
6
그리고 설정된 상한을 확인합니다. 설정 파일 위치는 환경마다 다르지만 대개 /etc/php-fpm.d/ 아래입니다.
pm = dynamic
pm.max_children = 20
pm.start_servers = 3
pm.max_requests = 500
pm.max_children이 동시에 뜰 수 있는 Worker의 최대 개수입니다. 이 값에 계속 붙어 있다면 Worker 고갈입니다. 반대로 상한에 한참 못 미치는데 느리다면 원인은 다른 곳에 있습니다.
Worker를 늘리기 전에 곱셈을 해 본다
여기서 가장 흔한 실수가 나옵니다. 느리니까 pm.max_children을 올리는 것입니다. 하지만 Worker는 하나가 곧 메모리 한 덩어리라서, 메모리가 감당하지 못하는 값을 넣으면 상황이 더 나빠집니다. 실제로 재 보면 이렇습니다.
$ ps -C php-fpm -o rss= --no-headers | awk '{s+=$1; n++} END {print n, s/1024"MB", s/n/1024"MB"}'
6 601MB 100MB
Worker 하나가 평균 90~100MB를 쓰고 있습니다. 그러면 pm.max_children = 20은 최대 2GB 가까이를 요구한다는 뜻입니다. 전체 메모리를 보면 다음과 같았습니다.
$ free -m
total used free shared buff/cache available
Mem: 1649 869 154 139 948 780
여기서 available 값을 그대로 예산으로 삼으면 안 됩니다. 이 780MB는 이미 떠 있는 Worker 6개를 쓰고 난 나머지이기 때문입니다. Worker가 쓸 수 있는 전체 예산은 이렇게 잡아야 합니다.
| 항목 | 값 | 설명 |
|---|---|---|
| 전체 메모리 | 1,649MB | 출발점 |
| Worker 외 상주 | 약 294MB | 데이터베이스, 웹서버, 운영체제 |
| 여유분 | 약 400MB | 순간 증가와 파일 캐시를 위한 몫 |
| Worker 예산 | 약 955MB | 1,649에서 위 둘을 뺀 값 |

Worker 하나가 평균 93MB이므로 안전한 상한은 10개 안팎입니다. 설정된 20의 절반입니다. 평소에는 6개만 떠 있어서 문제가 드러나지 않다가, 트래픽이 몰려 Worker가 늘어나는 순간 메모리가 바닥나고 스왑이 시작됩니다. 그때부터는 CPU도 부하 평균도 이상하지 않은데 사이트만 극단적으로 느려집니다.
이 서버에서는 그 흔적이 이미 남아 있었습니다. free -m의 마지막 줄을 보면 스왑이 2,047MB 중 456MB나 쓰인 상태였습니다.
Swap: 2047 456 1591
스왑이 쓰였다는 것은 과거 어느 시점에 물리 메모리가 부족했다는 뜻입니다. 지금 지표가 멀쩡해도 이 값이 0이 아니면 이미 한 번은 겪은 것입니다. 특정 시간대 지연을 쫓을 때 반드시 함께 봐야 할 값입니다.
특정 시간대에만 느려지는 사이트에서 이 조합은 유력한 후보입니다. 평상시 지표가 멀쩡하다는 점이 오히려 특징입니다.
| 확인할 것 | 명령 | 판단 기준 |
|---|---|---|
| 코어 수 | nproc |
부하 평균을 읽는 기준값 |
| 부하 평균 | uptime |
코어 수에 가까우면 CPU 포화 |
| Worker 개수 | ps -C php-fpm | wc -l |
상한에 붙어 있으면 고갈 |
| Worker 메모리 | ps -C php-fpm -o rss= |
상한 × 평균이 가용 메모리를 넘는지 |
| 메모리 여유 | free -m |
available 값이 실제 여유분 |
정리하면 계산은 이렇습니다.
상한 = (전체 메모리 - Worker 외 상주 사용량 - 여유분) / Worker 평균 사용량
여기서 자주 하는 실수가 free -m의 available 값을 그대로 넣는 것입니다. 그 값은 지금 떠 있는 Worker를 이미 제외한 나머지라서, 그대로 나누면 현재 돌아가는 개수보다 작은 상한이 나옵니다. 계산이 현실과 어긋나면 대개 이 지점입니다.
이 계산을 해 보지 않고 상한만 올리면 Worker 고갈이 메모리 부족으로 바뀔 뿐입니다. 반대로 상한이 메모리보다 크게 잡혀 있는 상태를 그대로 두는 것도 위험합니다. 평소에는 아무 문제가 없다가 트래픽이 몰리는 순간에만 무너지는데, 하필 그때가 방문자가 가장 많은 시간입니다.
캐시가 있어도 관리자와 동적 기능은 느릴 수 있다
페이지 캐시가 적용된 일반 콘텐츠는 대체로 다음과 같은 PHP 처리 과정을 건너뛸 수 있습니다.
방문자 → 웹 서버 → PHP → 데이터베이스 → HTML 생성
캐시가 있다면 저장된 HTML을 바로 전달하므로 PHP와 데이터베이스 부하가 줄어듭니다. 방문자에게 거의 동일한 콘텐츠를 보여주는 블로그나 콘텐츠 사이트에서 페이지 캐시의 효과가 큰 이유입니다.
하지만 로그인 사용자, 장바구니, 결제, 개인화된 회원 페이지는 사용자마다 결과가 달라 동일한 방식으로 캐시하기 어렵습니다. WooCommerce나 회원제 사이트는 방문자 수가 많지 않아도 동적 요청이 많아 PHP Worker와 데이터베이스에 더 큰 부담을 줄 수 있습니다.
관리자 화면도 일반적으로 캐시 대상이 아닙니다. 게시물 목록, 플러그인 관리, 주문 목록, 미디어 라이브러리, 대시보드 위젯을 열 때만 느리다면 프런트엔드 속도 측정 결과만으로 서버가 정상이라고 판단해서는 안 됩니다. 특정 관리자 메뉴에서만 외부 API를 호출하거나 대량의 데이터를 조회하는 플러그인이 있을 수도 있습니다.
정해진 시간에 실행되는 작업부터 대조한다
매일 비슷한 시간에 지연이 반복되면 아래 세 가지를 우선 확인할 만합니다.
WP-Cron 예약 작업
WP-Cron은 예약 게시, 업데이트 확인, 플러그인 작업 등을 처리하는 워드프레스의 예약 시스템입니다. 기본적으로 서버 시스템 Cron처럼 정해진 순간에 독립 실행되는 방식이 아니라 페이지 요청 등을 계기로 실행 시점을 확인합니다. 따라서 방문자가 적은 사이트에서는 작업이 늦게 실행될 수 있고, 여러 예약 작업이 한 시점에 겹칠 수도 있습니다.
자동 백업
백업 플러그인은 파일과 데이터베이스를 읽고, 백업 파일을 압축하고, 저장 위치로 전송합니다. 파일 수가 많거나 데이터베이스가 큰 사이트라면 디스크 입출력과 CPU를 동시에 사용할 수 있습니다.
보안 스캔
보안 플러그인의 전체 파일 검사 역시 파일 수에 따라 실행 시간이 길어지고 서버 자원을 점유할 수 있습니다.
이때 백업이나 보안 기능을 바로 끄는 것은 적절한 해결책이 아닙니다. 먼저 속도 저하 시간과 작업 실행 시간이 일치하는지 확인한 뒤, 이용자가 적은 시간으로 일정을 옮기거나 호스팅 업체의 서버 수준 백업 기능을 검토하는 순서가 안전합니다. 기본 WP-Cron 호출을 조정하고 시스템 스케줄러로 wp-cron.php를 실행하는 방법도 있지만, 호스팅 환경마다 지원 방식이 다르므로 공식 설정을 확인해야 합니다.
데이터베이스와 외부 API가 Worker를 붙잡을 수 있다
CPU와 메모리가 특별히 높지 않은데 동적 페이지가 느리다면 데이터베이스를 살펴봐야 합니다. 워드프레스는 게시물, 설정, 사용자, 주문 정보를 데이터베이스에서 읽습니다. 특정 플러그인이 비효율적인 쿼리를 반복하거나 데이터가 지나치게 늘어나면 쿼리 응답 시간이 길어집니다.
PHP는 데이터베이스 결과를 기다리는 동안 작업을 끝내지 못합니다. 따라서 겉으로는 PHP Worker가 부족해 보이지만 실제 시작점은 느린 쿼리일 수 있습니다. 게시물 목록이나 WooCommerce 주문 화면처럼 많은 데이터를 한 번에 조회하는 메뉴에서만 문제가 재현된다면 이 가능성이 더 커집니다.
외부 API도 비슷합니다. 결제 서비스, 라이선스 확인, 외부 데이터 동기화 기능이 느려지면 워드프레스가 응답을 기다리면서 PHP 요청 시간이 길어질 수 있습니다. 이 경우 서버 CPU는 높지 않은데 특정 플러그인 화면만 느려지는 양상이 나타날 수 있습니다. 플러그인별 HTTP 요청 로그와 타임아웃 설정을 확인하면 범위를 좁히는 데 도움이 됩니다.
실제 점검 순서
문제가 발생하면 먼저 “밤에 느리다”가 아니라 날짜, 시작 시각, 종료 시각, 느린 화면을 기록합니다. 예를 들어 “8월 20일 오후 9시 12분부터 약 15분간 관리자 페이지 지연”처럼 남겨야 서버 로그와 대조할 수 있습니다.
그 시간대에 다음 자료를 함께 비교합니다.
- 호스팅 패널의 CPU, 메모리, 프로세스 또는 PHP 사용량
- 실제 방문자 수와 요청량 변화
- WP-Cron 예약 작업, 백업, 보안 스캔 실행 기록
- PHP와 웹 서버 오류 로그
- 데이터베이스의 느린 쿼리 또는 응답 시간
- 특정 플러그인의 외부 API 호출과 관리자 메뉴별 재현 여부
호스팅에 문의할 때와 업그레이드를 결정할 때
공유 호스팅에서는 사이트 방문자가 늘지 않아도 다른 계정과 자원을 공유하거나 계정별 CPU·메모리·프로세스 제한에 도달할 수 있습니다. 관리 화면에서 Resource Usage, CPU Usage, Memory Usage 같은 항목을 제공하는지 확인하고, 반복적으로 제한에 걸린다면 상품 구조가 현재 사이트의 작업량과 맞는지 호스팅 업체에 문의해야 합니다.
문의 내용은 이런 형태가 좋습니다.
8월 20일 오후 9시 10분부터 9시 25분까지 관리자 페이지 응답이 지연됐습니다. 해당 시간의 CPU·메모리·PHP 프로세스 제한과 서버 오류 기록을 확인해 주세요.
“사이트가 느립니다”라고만 쓰면 확인 범위가 넓어져 답변이 늦어집니다.
서버를 업그레이드해야 하는 경우도 있습니다. 다만 비효율적인 플러그인, 겹치는 예약 작업, 느린 쿼리가 원인이라면 사양을 높여도 시간이 지나 다시 문제가 생길 수 있습니다. 정확한 시간대의 기록에서 CPU 포화, 실제 메모리 부족, PHP Worker 대기, 계정별 자원 제한이 반복적으로 확인될 때 업그레이드를 검토하는 편이 합리적입니다. 서버의 코어 수나 RAM 용량 하나만으로는 워드프레스 성능을 판단하기 어렵습니다.