PHP에서 파일을 include하거나 설정 파일을 로드할 때, 경로를 잘못 지정해서 골치 아파본 경험이 있을까? 특히 다른 폴더 구조의 서버로 이사할 때 갑자기 파일을 못 찾는 에러가 터진다. 다만 대부분의 개발자들은 상대경로와 절대경로의 차이를 정확히 모른 채 인터넷에서 가져온 코드를 그냥 복사해 붙인다. 이번에는 __FILE__과 __DIR__ 상수가 정확히 뭔지, 어느 상황에서 써야 하는지, 그리고 경로 오류를 완벽하게 방지하는 방법을 정리해서 소개하겠다.
PHP에서 파일 경로는 크게 두 가지로 나뉜다. 상대경로(relative path)는 현재 실행 중인 파일 위치를 기준으로 상대적인 경로를 나타내고, 절대경로(absolute path)는 서버의 루트(/)부터 시작하는 고정된 경로다.
문제는 상대경로를 잘못 사용할 때 터진다. 웹 서버에서는 '현재 파일'이 어디냐에 따라 상대경로의 기준이 바뀌기 때문이다. include 'config.php'라고 쓰면, PHP 엔진이 현재 작업 디렉토리(current working directory)를 기준으로 찾는데, 이게 당신이 예상한 위치와 다를 수 있다.
__FILE__은 현재 PHP 파일의 절대 경로를 나타내는 매직 상수다. 심볼릭 링크를 따라가므로 실제 파일의 경로를 반환한다.
<?php
// /var/www/html/app/controllers/UserController.php 파일에서
echo __FILE__;
// 출력: /var/www/html/app/controllers/UserController.php
?>
__DIR__은 현재 파일이 있는 디렉토리의 절대 경로다. 내부적으로는 dirname(__FILE__)과 동일하다.
<?php
// /var/www/html/app/controllers/UserController.php 파일에서
echo __DIR__;
// 출력: /var/www/html/app/controllers
?>
아래와 같은 디렉토리 구조를 예로 들어보자:
/var/www/html/
├── index.php
├── config/
│ └── database.php
└── app/
└── controllers/
└── UserController.php
<?php
// /var/www/html/app/controllers/UserController.php
include 'config/database.php'; // ← 이게 문제!
// 현재 작업 디렉토리가 /var/www/html이면 되지만,
// 때에 따라 다른 디렉토리일 수 있어서 파일을 못 찾을 수 있다.
?>
<?php
// /var/www/html/app/controllers/UserController.php
include __DIR__ . '/../../config/database.php';
// 절대 경로로 변환되므로 항상 같은 파일을 찾는다.
// 더 나은 방법: dirname()으로 한 단계 위로
include dirname(__DIR__) . '/config/database.php';
// /var/www/html/app 에서 한 단계 위 = /var/www/html
// 최고: 경로 계산을 명확하게
define('PROJECT_ROOT', dirname(__DIR__, 2)); // 2단계 위로
include PROJECT_ROOT . '/config/database.php';
?>
실무에서는 프로젝트 루트를 한 번 정의해서 모든 include에서 사용하는 방식을 권장한다.
<?php
// /var/www/html/config/app.php (프로젝트 설정 파일)
define('PROJECT_ROOT', dirname(__DIR__)); // /var/www/html
define('CONFIG_PATH', PROJECT_ROOT . '/config');
define('APP_PATH', PROJECT_ROOT . '/app');
define('VIEWS_PATH', PROJECT_ROOT . '/views');
define('LOGS_PATH', PROJECT_ROOT . '/logs');
?>
<?php
// /var/www/html/app/controllers/UserController.php
require_once dirname(dirname(__DIR__)) . '/config/app.php';
// 또는 더 간단하게
require_once __DIR__ . '/../../config/app.php';
// 이제 설정 상수를 사용
include CONFIG_PATH . '/database.php';
include VIEWS_PATH . '/user/list.php';
?>
| 상황 | ✗ 잘못된 방법 | ✓ 올바른 방법 |
|---|---|---|
| 상대경로 사용 | include 'config.php'; | include __DIR__ . '/config.php'; |
| 부모 폴더 접근 | include '../config.php'; | include dirname(__DIR__) . '/config.php'; |
| 여러 단계 상위 | include '../../../config.php'; | include dirname(__DIR__, 3) . '/config.php'; |
| 다른 프로젝트 | $path = $_SERVER['DOCUMENT_ROOT']; | $path = dirname(__DIR__, 2); |
| 파일 존재 확인 | if (file_exists('config.php')) | if (file_exists(__DIR__ . '/config.php')) |
PHP 7.0부터 dirname()에 두 번째 파라미터를 넣어서 여러 단계를 한 번에 올라갈 수 있다.
<?php
// /var/www/html/app/modules/User/Model.php
echo dirname(__FILE__, 1); // /var/www/html/app/modules/User
echo dirname(__FILE__, 2); // /var/www/html/app/modules
echo dirname(__FILE__, 3); // /var/www/html/app
echo dirname(__FILE__, 4); // /var/www/html
?>
파일이 필수인가? require를 쓰자. 선택사항인가? include를 쓰자.
<?php
// 필수 설정 파일은 require 사용
require_once __DIR__ . '/config.php';
// 선택사항인 템플릿은 include 사용
if (file_exists(__DIR__ . '/templates/header.php')) {
include __DIR__ . '/templates/header.php';
}
?>
파일 경로 오류는 PHP 개발 초기에 가장 자주 마주치는 문제 중 하나다. 절대경로를 사용하고 프로젝트 루트를 한 번만 정의해두면 대부분의 경로 문제는 사라진다. 특히 팀 프로젝트나 서버 이전 시 상대경로의 불안정성 때문에 고생하는 일을 완벽히 피할 수 있다. 이 글의 __DIR__ 활용법과 dirname() 레벨 파라미터를 참고해 프로젝트 루트를 정의하고, 모든 include/require에 절대경로를 적용하면, 환경이 바뀌어도 안전한 코드를 유지할 수 있을 것이다.