프로덕션의 HTTP 500은 증상이지 원인 진단이 아닙니다. CTO나 창업자에게 첫 결정은 서비스를 안정화할지, 최근 배포를 롤백할지, 장애를 더 조사할지입니다. 무작정 메모리와 타임아웃을 늘리기 전에 증거를 보존하세요. 이 글은 PHP와 Node.js의 차이, 오류별 진단 순서, 안전한 복구와 재발 방지를 정리합니다.
첫 대응: 영향 범위 확인, 증거 보존, 담당자 지정
- 영향 확인: 어느 경로, 고객, 지역, 업무 흐름에서 실패하는지 파악합니다. 오류율, 지연 시간, 상태 확인을 평소 수치와 비교하고, 애플리케이션 500과 프록시 502/504를 구별합니다.
- 기록: 첫 실패 시각, 마지막 정상 배포, 배포 ID, 설정 변경, 요청 ID, 민감정보를 제거한 오류 스택, 프로세스 재시작, 메모리와 외부 의존 서비스 상태를 모읍니다. 로그 접근을 제한합니다.
- 안전한 완화: 배포 직후 시작됐다면 검증된 롤백이나 기능 플래그 비활성화를 고려합니다. 이전 코드와 현재 DB 스키마가 호환되는지 먼저 확인합니다. 로그 삭제, 무분별한 권한 변경, 반복적인 무작정 재시작은 피합니다.
- 공유: 영향, 현재 완화 조치, 담당자, 다음 업데이트 시간을 알립니다. 원인이 확인되기 전 단정하지 않습니다.
진단 목록: 오류별 첫 확인 지점
- HTTP 500: 애플리케이션 예외, PHP 치명적 오류, 의존 서비스 실패 등이 가능합니다. 요청 ID와 시각으로 프록시·앱·DB 로그를 연결하고 어느 계층이 상태 코드를 냈는지 확인합니다.
- 메모리 부족: PHP의 “Allowed memory size exhausted” 오류라면 해당 웹 실행 환경의 실제 memory_limit을 확인합니다. Node.js에서는 V8 힙 사용량, 프로세스 RSS, 컨테이너 제한과 OOM 종료를 구분합니다.
- 타임아웃: 브라우저, 로드밸런서, 웹 서버, PHP 실행, Node 클라이언트, DB, 외부 API 중 어디의 제한인지 찾습니다. 느린 쿼리, 대기 중인 I/O, 큐 포화를 먼저 조사합니다.
- Node EADDRINUSE: 지정한 주소나 포트를 이미 다른 프로세스가 사용 중입니다. 배포 설정, 중복 리스너, 겹친 배포 프로세스를 확인합니다. 모르는 프로세스를 포트 확보를 위해 바로 종료하지 마세요.
- 처리되지 않은 Promise 거부: 실패한 비동기 작업과 호출 맥락을 찾고 await 경계에서 명시적으로 처리합니다. 런타임의 미처리 거부 동작을 확인하고, 전역 이벤트 리스너만으로 원래 실패를 덮지 않습니다.
- 로그가 없음: 올바른 워커, 컨테이너, 시간대를 조회했는지, 로그 전달·보존·시계 차이를 확인합니다. 앱까지 요청이 도달하지 않았거나 기록 전에 프로세스가 종료됐을 수도 있습니다.
PHP: 오류 보고와 요청별 실행 환경
PHP는 웹 서버와 PHP-FPM 같은 프로세스 관리자 뒤에서 동작할 수 있습니다. 화면의 500만 보지 말고 웹 서버 오류 로그와 해당 PHP 워커·풀 로그를 확인하세요. error_reporting은 보고할 오류 수준을, log_errors와 error_log는 로깅을 제어합니다. 프로덕션에서 display_errors로 내부 경로나 스택을 사용자에게 노출하지 마세요. CLI와 웹 SAPI의 설정 파일이 다를 수 있으므로 실제 실패한 실행 환경에서 유효 설정을 확인해야 합니다.
핵심 설정인 memory_limit과 max_execution_time은 오류 해석에 중요하지만, 수치만 높이면 무한 반복, 과도한 쿼리, 대용량 처리 문제를 숨길 수 있습니다. 웹 서버, PHP-FPM, DB, 역방향 프록시의 제한 시간도 각각 다를 수 있습니다. 스택과 함께 최근 패키지 변경, 파일 권한, 자동 로딩, 연결 고갈을 살펴보세요.
Node.js: 장기 실행 프로세스와 비동기 오류
Node.js 서비스는 요청마다 끝나는 PHP 코드와 달리 장기 실행 프로세스입니다. 처리되지 않은 예외, Promise 거부, 포트 바인딩 실패, 힙 고갈로 프로세스가 종료되면 재시작 전까지 여러 요청에 영향이 갈 수 있습니다. EADDRINUSE 같은 시스템 오류는 바뀔 수 있는 문구보다 error.code를 기준으로 식별합니다. Promise API의 거부와 콜백 API의 오류 인자를 각각 처리해야 합니다.
메모리 진단은 process.memoryUsage()의 heapUsed, rss 추이와 컨테이너 메모리 한도, 재시작 기록을 함께 봅니다. V8 old-space 한도만 올리는 --max-old-space-size 조치는 호스트나 컨테이너 메모리가 부족하면 장애 지점을 옮길 뿐입니다. 오래 유지되는 객체, 무제한 캐시, 큰 페이로드와 동시성을 먼저 분석하세요.
로그와 롤백: 되돌릴 수 있는 조치를 선택
사용 중인 로깅 도구에서 서비스·배포 버전·요청 ID·장애 시간대로 좁힌 읽기 전용 조회를 하세요. 첫 실패 요청과 마지막 정상 요청을 비교하고 모든 조치 시각을 타임라인에 남깁니다. 티켓이나 AI 도구에 운영 비밀번호, 토큰, 고객 원본 데이터를 붙여 넣지 마세요. 로그가 부족하면 재배포 전에 기존 컨테이너와 플랫폼 이벤트를 보존합니다.
장애 시작 시점이 배포와 일치하고 이전 버전이 정상임이 확인되면 롤백이 합리적일 수 있습니다. 단, 현재 데이터 형식과 외부 계약에 이전 코드가 호환되는지 확인하세요. 호환되지 않으면 기능 플래그나 특정 경로 차단 등 제한된 완화책이 더 안전할 수 있습니다. 복구 검증은 헬스체크 한 개가 아니라 핵심 사용자 흐름, 오류율, 지연 시간으로 합니다.
예방: 한 번의 장애를 운영 개선으로 연결
- 오류율, p95 지연, 재시작·OOM 종료, 메모리 추이, 큐 길이, 외부 서비스 실패에 담당자와 에스컬레이션 경로가 있는 알림을 둡니다.
- 요청 ID와 배포 ID를 포함한 구조화 로그를 사용하고 개인정보를 최소화합니다. 프록시에서 앱·DB까지 요청을 추적할 수 있는지 점검합니다.
- 외부 호출에 명시적 타임아웃, 역압력, 상한 있는 재시도와 지터를 적용해 재시도 폭주를 막습니다.
- 비프로덕션에서 롤백·백업 복구를 연습하고 스키마 호환성과 기능 플래그 절차를 문서화합니다.
- 원인에 대한 회귀 테스트를 추가하고 현실적인 부하에서 리소스 한도를 검토합니다. 비난보다 학습에 초점을 맞춘 장애 보고서에 담당자와 기한을 남깁니다.
외부 개발 파트너가 필요한 시점
현재 버전을 재현할 수 없거나, 계정 소유권·로그 접근·안전한 복구 절차가 불명확하면 지원을 요청할 때입니다. 필요한 결과물은 근거 있는 진단, 위험도별 개선 계획, 반복 가능한 배포·롤백 절차입니다. Nextchain의 웹 애플리케이션 개발 및 지속 개발 소개에는 기존 애플리케이션 검토와 개선이 포함됩니다. 운영 지식이 기존 개발사에 묶여 있다면 개발사 교체 인수인계 체크리스트로 코드·계정·복구 절차를 확인하세요. 증상과 영향, 제약 조건을 정리해 프로젝트 진단을 요청할 수 있습니다. 비밀번호나 민감한 운영 데이터는 보내지 마세요.
자주 묻는 질문
프로덕션에서 PHP display_errors를 켜야 하나요?
아니요. 상세 진단은 접근이 제한된 로그에 남기고 사용자에게 내부 경로, 스택, 비밀정보를 노출하지 마세요. 실패한 워커의 실제 설정을 확인합니다.
HTTP 500이면 PHP나 Node.js 자체의 문제인가요?
아닙니다. 서버가 요청을 처리하지 못했다는 결과일 뿐입니다. 프록시, 애플리케이션, 외부 의존 서비스 중 어느 계층에서 시작됐는지 확인해야 합니다.
메모리나 타임아웃을 늘리면 해결되나요?
용량과 안전성을 검토한 뒤 임시 완화책이 될 수는 있습니다. 먼저 정상적인 부하 증가인지, 누수·느린 쿼리·외부 서비스 장애인지 구분하고 변경 효과와 롤백 경로를 확인하세요.
롤백이 위험한 경우는 언제인가요?
새 데이터베이스 스키마, 데이터 형식, 외부 연동이 이전 코드와 호환되지 않을 때입니다. 검증된 호환성 계획이나 제한된 완화 조치를 사용하세요.


