Windows 환경에서 PHP 에러가 분명히 나는데도 화면에는 흰 화면만 보이고, error_log에도 아무것도 안 남는 경우가 종종 있습니다. 이때는 “설정이 잘못됐다”는 감으로 접근하기보다, 재현 → 원인 좁히기 → 해결 순서로 체크하면 빠르게 정리됩니다.
아래 순서대로 하나씩 확인하면, 보통 10~20분 안에 어디서 에러가 사라지는지 잡힙니다.
먼저, 지금 문제가 “에러가 안 나는 것”이 아니라 “에러가 안 보이는 것”인지부터 분리합니다.
1) 재현부터: 30초짜리 최소 재현 스크립트 만들기
현재 프로젝트에서 바로 찾으려 하면 변수가 너무 많습니다. 문서 루트(또는 테스트 가능한 폴더)에 파일 하나로 의도적으로 에러를 발생시키고, 그 에러가 어디로 가는지 확인합니다.
- 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 아래 등)가 단골입니다.
- 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 설정 값을 기준으로 더 정확한 분기까지 같이 정리할 수 있어요.