PHP로 프로젝트를 만들다 보면 거의 한 번은 마주치는 오류가 있습니다.
Warning: require_once(.../config/config.php): failed to open stream: No such file or directory
Fatal error: require_once(): Failed opening required '.../config/config.php'분명 파일은 있는데 "없다"고 나옵니다. 원인은 대부분 경로 계산 깊이 착오입니다. 이 글에서 원인과 안전한 해결 패턴을 정리합니다.
원인: dirname() 단계가 한 칸 어긋난다
PHP에서 현재 파일이 있는 폴더는 __DIR__, 그 부모 폴더는 dirname(__DIR__)로 거슬러 올라갑니다. 한 번 호출할 때마다 한 단계씩 위로 갑니다.
파일 위치: /var/www/blog/admin/posts/form.php
__DIR__ = /var/www/blog/admin/posts
dirname(__DIR__) = /var/www/blog/admin
dirname(dirname(__DIR__)) = /var/www/blog ← 프로젝트 루트
dirname(dirname(dirname(...)))= /var/www ← 한 칸 더 가버림(오류!)루트가 /var/www/blog인데 dirname을 한 번 더 부르면 /var/www가 되고, 거기에 /config/config.php를 붙이니 존재하지 않는 경로가 되는 것입니다.
해결: 진입 파일마다 깊이를 정확히 맞춘다
각 PHP 파일이 루트에서 몇 단계 아래에 있는지 세고, 그 수만큼만 dirname을 호출합니다.
| 파일 위치 | 루트까지 깊이 | ROOT_PATH 정의 |
|---|---|---|
/blog/index.php | 0단계 | __DIR__ |
/blog/admin/dashboard.php | 1단계 | dirname(__DIR__) |
/blog/admin/posts/form.php | 2단계 | dirname(dirname(__DIR__)) |
// /blog/admin/posts/form.php (루트에서 2단계 아래)
define('ROOT_PATH', dirname(dirname(__DIR__))); // = /blog
require_once ROOT_PATH . '/config/config.php';
require_once dirname(__DIR__) . '/_auth.php'; // = /blog/admin/_auth.php디버깅 팁: 일단 찍어본다
경로가 의심되면 추측하지 말고 실제 값을 출력해 보세요.
echo ROOT_PATH; // 기대한 루트가 맞는지
var_dump(file_exists(ROOT_PATH . '/config/config.php'));file_exists()가 false면 경로가 틀린 것이고, 출력된 ROOT_PATH가 한 칸 위/아래인지 바로 보입니다.
정리
- 오류 메시지에 찍힌 실제 경로를 먼저 본다 — 어디를 잘못 가리키는지 답이 거기 있다.
dirname호출 횟수 = 파일이 루트에서 떨어진 단계 수.- 진입 파일이 여러 깊이에 흩어져 있으면, 각 파일의 깊이를 개별로 맞춰야 한다. 한 패턴을 복붙하면 깊이가 다른 파일에서 깨진다.
같은 오류를 만나면 메시지의 경로부터 확인하세요. 90%는 거기서 끝납니다.