← 전체 글로 돌아가기

웹 개발

페이지 로딩 속도가 갑자기 느려졌을 때 시작하는 방법

운영 중 페이지가 버벅거리기 시작했을 때, 어디서부터 디버깅을 시작할지 결정하는 체크리스트.

어제는 빨랐던 페이지가 오늘은 느려졌다. 무언가 바뀌었는데 뭔지 모른다. 사용자 불평이 늘어난다.

이런 경우 고칠 게 정말 많아 보인다. 하지만 순서대로 확인하면, 대부분 몇 가지 원인 중 하나다.

1단계: 로컬에서 재현 가능한가

# 같은 URL을 여러 번 방문
curl -w "Total: %{time_total}s\n" https://yoursite.com/page

# 또는 DevTools에서
# 네트워크 연결 속도 제한 (Throttling)
# F12 → Network → Slow 3G 시뮬레이션

로컬이 빠르면 운영 서버의 네트워크나 리소스 문제다. 로컬도 느리면 코드나 DB 쿼리 문제다.

2단계: 페이지의 어느 부분이 느린가

// DevTools Performance에서 기록
// F12 → Performance → 기록 시작 → 페이지 새로고침 → 기록 중단

// 또는 Core Web Vitals 측정
// LCP (Largest Contentful Paint): 가장 큰 요소가 보일 때까지
// FID (First Input Delay): 첫 입력 지연
// CLS (Cumulative Layout Shift): 레이아웃 변동

Performance 플레임 차트에서:

  • Network: 리소스 다운로드 (이미지, JS, CSS)
  • Scripting: JavaScript 실행
  • Rendering: 레이아웃 계산, 페인트

어느 구간이 긴가 본다.

3단계: Network 탭에서 느린 리소스 확인

DevTools Network 탭
→ 이미지, JS, CSS 파일 확인
→ 크기가 크거나 로드 시간이 긴 파일 찾기

특정 파일이 갑자기 커졌거나, 새로운 파일이 추가됐다면 그게 범인일 수 있다.

# 배포된 파일 크기 확인
ls -lh dist/
ls -lh .next/static/

빌드 결과물이 예상보다 크다면 bundler 설정을 봐야 한다.

4단계: 서버 응답 시간 확인

# Time to First Byte (TTFB)
curl -w "TTFB: %{time_starttransfer}s\n" https://yoursite.com/page

TTFB가 1초 이상이면 서버가 느리다는 뜻이다.

서버 로그를 본다:

# 평균 요청 처리 시간
docker logs app | grep "ms" | tail -100

# 또는 application 성능 모니터링
# APM 도구 (DataDog, New Relic) 확인

특정 엔드포인트만 느린가, 아니면 전체가 느린가?

5단계: 데이터베이스 쿼리 확인

서버가 느려진 주요 원인은 DB 쿼리다:

// 쿼리 실행 시간 로깅
const start = Date.now()
const result = await db.query('SELECT * FROM posts')
const elapsed = Date.now() - start
console.log(`Query took ${elapsed}ms`)

만약 쿼리가 10초 이상 걸린다면:

  • 인덱스가 없는가
  • 데이터가 최근에 매우 늘어났는가
  • DB 연결이 느린가
-- PostgreSQL
EXPLAIN ANALYZE SELECT * FROM posts WHERE user_id = 123;

-- 인덱스 생성
CREATE INDEX idx_posts_user_id ON posts(user_id);

6단계: 리소스 사용률 확인

# CPU
top -o %CPU

# 메모리
free -h

# 디스크
df -h

# 네트워크
iftop  # 또는 nethogs

어느 리소스가 꽉 찼는가?

CPU 100%: 앱이 무거운 계산을 하거나, 무한 루프에 빠짐 메모리 80% 이상: 메모리 누수 또는 캐시가 커짐 디스크 90% 이상: 로그가 꽉 찼거나 DB가 커짐

7단계: 최근 변경사항 확인

# 어제와 오늘의 배포 내역
git log --oneline -20

# 또는 배포 플랫폼 히스토리
# Dokploy, Vercel 등에서 최근 배포 확인

패턴:

  • 새 버전 배포 후 느려짐: 코드 변경
  • 배포 안 했는데 느려짐: DB 용량 증가, 트래픽 증가, 외부 API 지연

8단계: 캐시 확인

# Redis 캐시 크기
redis-cli INFO stats

# 캐시 히트율
redis-cli INFO stats | grep hit

# 또는 캐시 플러시 후 재로드
redis-cli FLUSHALL

캐시가 커서 메모리 사용량이 늘어났을 수도 있다. 또는 캐시 히트율이 낮아서 DB 쿼리가 많아졌을 수도.

9단계: 트래픽 증가 확인

# 웹 서버 로그에서 요청 수
grep $(date +%d/%b/%Y) /var/log/nginx/access.log | wc -l

# 또는 모니터링 대시보드
# CloudFlare Analytics, AWS CloudWatch 확인

갑자기 사용자가 50배 늘어났다면, 당연히 느려진다. 이 경우 스케일링이 필요하다:

# Docker Swarm
docker service scale app=3

# Kubernetes
kubectl scale deployment app --replicas=3

10단계: 응급 조치

원인을 찾는 데 시간이 걸리면, 빠른 개선부터 한다:

# 1. 캐시 재시작
redis-cli FLUSHALL
redis-cli SHUTDOWN
# Redis 재시작

# 2. 오래된 로그 정리
find /var/log -type f -mtime +30 -delete

# 3. DB 최적화
VACUUM;  # PostgreSQL
OPTIMIZE TABLE;  # MySQL

# 4. CDN 캐시 초기화 (Cloudflare 등)
# 대시보드에서 캐시 삭제

# 5. 마지막 수단: 인스턴스 재시작
docker-compose restart

이 중 하나라도 하면 대부분 일시적으로 빨라진다. 그 사이 원인을 파악한다.

성능 저하 원인 순서 (통계)

  1. DB 쿼리 느림 (40%): 인덱스 없음, 데이터 폭증
  2. 메모리 부족 (30%): 캐시 커짐, 메모리 누수
  3. 트래픽 증가 (15%): 스케일링 필요
  4. 큰 리소스 (10%): 이미지, JS 번들 증가
  5. 외부 API 지연 (5%): 의존하는 서비스 느림

체크리스트 (5분)

  1. curl -w "TTFB: %{time_starttransfer}s" - 서버 응답
  2. top -o %CPU - CPU/메모리
  3. DevTools Performance - 병목 지점
  4. git log -1 - 최근 배포
  5. DB 쿼리 로그 - 느린 쿼리

이 5가지만 확인하면, 대부분의 경우 원인을 찾을 수 있다.