Windows 환경에서 PHP 에러가 분명히 나는데도 화면에는 흰 화면만 보이고, error_log에도 아무것도 안 남는 경우가 종종 있습니다. 이때는 “설정이 잘못됐다”는 감으로 접근하기보다, 재현 → 원인 좁히기 → 해결 순서로 체크하면 빠르게 정리됩니다.

Lighthouse guiding PHP errors into a log flow

아래 순서대로 하나씩 확인하면, 보통 10~20분 안에 어디서 에러가 사라지는지 잡힙니다.

먼저, 지금 문제가 “에러가 안 나는 것”이 아니라 “에러가 안 보이는 것”인지부터 분리합니다.

1) 재현부터: 30초짜리 최소 재현 스크립트 만들기

현재 프로젝트에서 바로 찾으려 하면 변수가 너무 많습니다. 문서 루트(또는 테스트 가능한 폴더)에 파일 하나로 의도적으로 에러를 발생시키고, 그 에러가 어디로 가는지 확인합니다.

Diagram of PHP error types flowing to log or screen

  • parse error (문법 오류): try-catch로 못 잡습니다. 로그/표시 설정이 핵심입니다.
  • runtime error/exception: try-catch로 잡히는지 확인합니다.
  • fatal error: shutdown handler로만 잡히는 종류가 있습니다.

테스트 예시(개념): 문법 오류 1개, throw Exception 1개, 존재하지 않는 함수 호출 1개를 각각 따로 만들어 어떤 유형이 “사라지는지”를 봅니다. 이 결과가 이후 분기점이 됩니다.

재현이 되면, 이제 “PHP가 어떤 모드로 실행 중인지”를 먼저 고정합니다. (CLI인지, 웹 서버 모듈/CGI/FastCGI인지에 따라 로그 위치와 동작이 달라질 수 있어요.)

2) 실행 환경 고정: CLI vs Web(SAPI) 설정이 다른지 확인

Windows에서는 특히 CLI의 php.ini웹 서버가 사용하는 php.ini가 달라서, CLI에서는 로그가 잘 남는데 웹에서는 안 남는 일이 흔합니다.

  • CLI에서 php --ini로 로드되는 ini 경로 확인
  • 웹에서는 phpinfo()로 Loaded Configuration File, Scan this dir for additional .ini files 확인
  • IIS(FastCGI)라면 사이트/앱풀별로 다른 ini를 가리키는지 확인

CLI와 웹의 ini 경로가 다르면, 지금 겪는 문제는 “설정이 적용되지 않았다”일 가능성이 큽니다. 이후 단계는 웹 SAPI 기준으로 진행하세요.

다음은 가장 핵심인 “로그가 실제로 어디로 쓰이는지, 쓸 수 있는지”를 확인합니다.

3) 로그가 안 남는 1순위: error_log 경로/권한/상대경로 문제

Windows에서 error_log 설정이 애매하면, PHP가 로그 파일을 열지 못하고 조용히 넘어가는 경우가 있습니다. 특히 상대 경로존재하지 않는 폴더, 권한이 없는 위치(Program Files 아래 등)가 단골입니다.

Windows log folder and file path routing diagram

  • error_log는 절대 경로로 두는 게 안전합니다. (예: D:\logs\php\error.log)
  • 로그 폴더가 실제로 존재하는지, 서비스 계정이 쓰기 가능한지 확인
  • IIS라면 앱풀 계정(예: ApplicationPoolIdentity)이 해당 폴더에 쓰기 권한이 있는지 확인
  • Apache/Nginx(Windows)라면 해당 프로세스 실행 계정 기준으로 쓰기 권한 확인

: “파일은 있는데 내용이 안 쌓임”이면, 경로는 맞지만 에러 레벨/표시 설정이 다른 경우가 많습니다. “파일 자체가 생성이 안 됨”이면, 경로/권한/폴더 존재 여부를 먼저 의심하세요.

로그 경로가 정리되면, 그 다음은 “에러를 기록하도록 PHP가 설정되어 있는지”를 단계적으로 확인합니다.

4) php.ini 필수 조합: display_errors, log_errors, error_reporting

개발/테스트에서 화면 출력이 필요할 때도 있고, 운영에서는 화면에 노출하면 안 될 때도 있습니다. 하지만 어떤 경우든 로그는 남아야 문제 해결이 됩니다.

  • log_errors=On: 로그 기록 켜기
  • error_log=...: 로그 파일 경로 고정
  • error_reporting=E_ALL: 필요한 에러 레벨 활성화(문제 재현 시에는 보통 E_ALL 권장)
  • display_errors: 개발/테스트는 On, 운영은 Off 권장

Windows에서 흔한 패턴은, display_errors는 꺼져 있고(log는 켜져 있어야 하는데) log_errors도 꺼져 있어서 결과적으로 어디에도 안 보이는 상태입니다. 재현 단계에서는 잠깐이라도 로그를 확실히 남기는 조합으로 바꾼 뒤, 원인 해결 후 운영 정책에 맞게 되돌리는 식이 안전합니다.

이제 에러가 “어디론가” 남기기 시작했다면, 다음은 코드 레벨에서 잡아야 할 에러/예외를 분리합니다.

5) try-catch로 안 잡히는 종류 정리 + shutdown handler 점검

“catch에 안 들어와요”는 버그가 아니라 에러 타입 특성인 경우가 많습니다.

  • Parse error(문법 오류): 실행 전에 실패하므로 try-catch로 처리 불가
  • Fatal error(치명적 오류): 일부는 catch로 못 잡고 스크립트가 종료됩니다
  • Warning/Notice: 예외가 아니라 메시지일 뿐이라 catch로 잡히지 않습니다(설정/핸들러로 처리)

운영에서 최소한의 방어를 하려면, 다음을 함께 고려합니다.

  • set_error_handler로 Warning/Notice를 로깅 정책에 맞게 처리
  • register_shutdown_function + error_get_last()로 종료 직전 fatal 계열을 로깅(가능한 범위 내)
  • 프레임워크를 쓴다면 자체 핸들러(예: Laravel/ Symfony)의 설정이 PHP 기본 설정을 덮는지 확인

중요한 포인트는 “모든 것을 예외로 만들겠다”가 아니라, 우리 서비스에서 놓치면 안 되는 신호를 안정적으로 남기자입니다.

마지막으로, Windows에서 특히 자주 보는 “원인은 따로 있는데 증상이 에러 누락처럼 보이는” 케이스를 정리합니다.

6) 안전한 체크리스트 + 흔한 실수 목록(Windows 단골)

  • 체크리스트
    • 웹 SAPI가 사용하는 php.ini 경로를 phpinfo()로 확인했다
    • log_errors=On, error_log 절대 경로, error_reporting=E_ALL로 재현 테스트를 했다
    • 로그 폴더가 존재하고, 서비스 계정이 쓰기 권한을 가진다
    • 테스트 파일로 parse/runtime/fatal을 각각 재현해 “사라지는 타입”을 구분했다
    • 프레임워크/부트스트랩 코드가 ini_set으로 설정을 덮지 않는지 확인했다
  • 흔한 실수
    • error_log를 상대 경로로 두고, 현재 작업 디렉터리가 바뀌어 로그가 다른 곳에 생김
    • Program Files 아래에 로그를 만들려다 권한 문제로 실패
    • CLI에서는 정상인데 웹에서는 다른 php.ini를 써서 설정이 적용되지 않음
    • display_errors=Off인데 log_errors도 Off라서 “아무것도 안 보이는” 상태
    • try-catch로 parse/fatal까지 잡으려다 시간만 소비

여기까지 했는데도 로그가 비어 있다면, 다음 후보는 웹 서버 쪽 로그(예: IIS Failed Request Tracing, Apache error.log, Nginx error.log) 또는 PHP 자체 크래시/프로세스 종료(이벤트 뷰어)입니다. 다만 대부분은 위 1~4단계에서 해결됩니다.

마무리

Windows에서 PHP 에러가 “안 보이는” 문제는 대체로 재현 스크립트 + 웹 SAPI ini 확인 + error_log 경로/권한에서 갈립니다. 감으로 설정을 바꾸기보다, 에러 타입을 분리하면서 한 단계씩 좁혀가면 금방 결론이 납니다.

필요하면 현재 사용 중인 웹 서버(IIS/Apache/Nginx)와 phpinfo()의 Loaded Configuration File, error_log 설정 값을 기준으로 더 정확한 분기까지 같이 정리할 수 있어요.