S
STONI
PHP
Apache
Performance
OPcache
PHP-FPM
Cache
Monitoring

PHP & Apache 성능 튜닝 완전 가이드

PHP & Apache 성능 튜닝 완전 가이드

이 문서는 트래픽 증가에 대비해 PHP와 Apache 스택을 체계적으로 최적화하는 방법을 제공합니다. 빠른 승수효과(저비용 고효율)부터 인프라 확장까지, 실무 기준의 우선순위로 정리했습니다.

1) 빠른 체감 성능 개선 (Low-hanging fruits)

  • gzip/br 압축 활성화 (정적/동적)
  • HTTP/2, HTTP/3(QUIC) 활성화
  • 이미지 최적화(WebP/AVIF) 및 Lazy-loading
  • 정적 파일 캐시 헤더(Cache-Control, ETag) 강화
  • CDN 도입으로 Edge 캐시 활용
# Apache도 유사 정책 적용 (mod_headers)
add_header Cache-Control "public, max-age=31536000, immutable";

2) PHP 레이어 최적화

2.1 OPcache 활성화

PHP 코드를 바이트코드로 캐싱해 CPU 사용량을 크게 줄입니다.

; php.ini 또는 opcache.ini
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=50000
opcache.validate_timestamps=0   ; 배포 시 재시작 또는 preloading 사용
opcache.preload=/var/www/app/preload.php
  • 배포 파이프라인에서 opcache:reset 훅 또는 PHP-FPM 재시작 포함
  • 프레임워크(Route/Config) 프리로드로 콜드스타트 최소화

2.2 PHP-FPM 풀 튜닝

; www.conf
pm=dynamic                  ; 트래픽 예측 가능하면 static, 아니면 dynamic
pm.max_children=64          ; 서버 메모리/응답시간 기준 산정
pm.start_servers=8
pm.min_spare_servers=8
pm.max_spare_servers=16
pm.max_requests=1000        ; 메모리 누수 방지
request_terminate_timeout=60s
  • 계산법(대략): (총 RAM - OS/DB/캐시)/프로세스별 피크RSS ≈ max_children
  • slowlog 활성화로 느린 스크립트 추적
slowlog=/var/log/php-fpm/slow.log
request_slowlog_timeout=2s

2.3 Composer Autoload 최적화

composer dump-autoload --optimize --classmap-authoritative --no-dev
  • prod 빌드 이미지/아티팩트 단계에서 수행

3) Apache 레이어 최적화

3.1 MPM 선택

  • event MPM 권장(Keep-Alive 처리에 유리)
# /etc/apache2/mods-available/mpm_event.conf
<IfModule mpm_event_module>
  StartServers             2
  MinSpareThreads         25
  MaxSpareThreads         75
  ThreadsPerChild         25
  MaxRequestWorkers      400
  MaxConnectionsPerChild 5000
</IfModule>
  • PHP 처리 자체는 php-fpm으로 분리하고 Apache는 리버스 프록시+정적 서빙 역할에 집중

3.2 HTTP/2, 압축, 캐시 헤더

# http2
Protocols h2 http/1.1

# 압축(brotli>gzip)
LoadModule brotli_module modules/mod_brotli.so
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css application/javascript application/json image/svg+xml

# 캐시 헤더
<FilesMatch "\.(js|css|png|jpg|jpeg|gif|svg|webp|avif|woff2?)$">
  Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>

4) 애플리케이션 레이어

4.1 데이터 캐시 전략

  • PSR-16/PSR-6 캐시 추상화 사용 (Redis 권장)
  • 핫패스(메뉴, 설정, 권한, 카운트) 캐시
  • 쓰기경쟁은 분산락(Redis SET NX PX)으로 보호
$cacheKey = 'article:'.$id;
$data = $cache->get($cacheKey);
if ($data === null) {
  $data = $repo->fetch($id);
  $cache->set($cacheKey, $data, 300);
}

4.2 세션/큐 외부화

  • 세션: 파일 → Redis/Memcached
  • 큐: sync → Redis/RabbitMQ (이메일, 썸네일, 웹훅 비동기)

4.3 DB 최적화

  • N+1 제거(ORM eager loading)
  • 적절한 인덱스와 커버링 인덱스
  • 슬로우쿼리 로그 분석 + 쿼리캐시 금지(MySQL8 미제공)

5) 아키텍처 다이어그램

아래 이미지는 본 문서의 권장 아키텍처를 시각화한 것입니다. (확대 가능한 원본은 저장소의 /snapshot/ 폴더를 참고하세요.)

기본 아키텍처 (CDN→Apache(event)→PHP-FPM→Redis/DB)

확장 아키텍처 (수평 확장)

확장 아키텍처 (LB→Apache×N→PHP-FPM×N→Redis Cluster/DB Replication)


6) 모니터링 & 관측성

  • 시스템: htop, iostat, vmstat, sar
  • 웹: Apache status (mod_status), ab, wrk, k6
  • PHP: prometheus_client, blackfire, xdebug profiler(스테이징)
  • APM: OpenTelemetry → Tempo/Jaeger, Metrics → Prometheus + Grafana
# mod_status
<Location "/server-status">
  SetHandler server-status
  Require local
</Location>

7) 배포 파이프라인 체크리스트

  • [ ] 빌드 시 composer install --no-dev --classmap-authoritative
  • [ ] 에셋 빌드/압축, 파일 지문(fingerprint)
  • [ ] OPcache 프리로드/리셋
  • [ ] DB 마이그레이션 트랜잭션
  • [ ] 헬스체크 + 카나리 배포

8) 성능 벤치마크 예시(k6)

import http from "k6/http";
import { sleep } from "k6";

export const options = {
  vus: 50,
  duration: "1m",
  thresholds: {
    http_req_duration: ["p(95)<300"],
  },
};

export default function () {
  http.get("https://stoni.space/");
  sleep(1);
}

결론

  1. 프론트(압축/캐시/HTTP2) → 2) PHP(OPcache/FPM) → 3) Apache(MPM/헤더) → 4) 데이터/캐시/큐 → 5) 모니터링/확장 순으로 진행하면 위험을 줄이면서 성능을 크게 끌어올릴 수 있습니다.
Clickable cat