PHP에서 파일을 include하거나 설정 파일을 로드할 때, 경로를 잘못 지정해서 골치 아파본 경험이 있을까? 특히 다른 폴더 구조의 서버로 이사할 때 갑자기 파일을 못 찾는 에러가 터진다. 다만 대부분의 개발자들은 상대경로와 절대경로의 차이를 정확히 모른 채 인터넷에서 가져온 코드를 그냥 복사해 붙인다. 이번에는 __FILE__과 __DIR__ 상수가 정확히 뭔지, 어느 상황에서 써야 하는지, 그리고 경로 오류를 완벽하게 방지하는 방법을 정리해서 소개하겠다.

 

1단계: 파일 경로의 기본 개념 이해하기

PHP에서 파일 경로는 크게 두 가지로 나뉜다. 상대경로(relative path)는 현재 실행 중인 파일 위치를 기준으로 상대적인 경로를 나타내고, 절대경로(absolute path)는 서버의 루트(/)부터 시작하는 고정된 경로다.

문제는 상대경로를 잘못 사용할 때 터진다. 웹 서버에서는 '현재 파일'이 어디냐에 따라 상대경로의 기준이 바뀌기 때문이다. include 'config.php'라고 쓰면, PHP 엔진이 현재 작업 디렉토리(current working directory)를 기준으로 찾는데, 이게 당신이 예상한 위치와 다를 수 있다.

 

__FILE__ 상수: 현재 파일의 절대 경로

__FILE__은 현재 PHP 파일의 절대 경로를 나타내는 매직 상수다. 심볼릭 링크를 따라가므로 실제 파일의 경로를 반환한다.

<?php
// /var/www/html/app/controllers/UserController.php 파일에서
echo __FILE__;
// 출력: /var/www/html/app/controllers/UserController.php
?>

 

__DIR__ 상수: 현재 파일의 디렉토리

__DIR__은 현재 파일이 있는 디렉토리의 절대 경로다. 내부적으로는 dirname(__FILE__)과 동일하다.

<?php
// /var/www/html/app/controllers/UserController.php 파일에서
echo __DIR__;
// 출력: /var/www/html/app/controllers
?>

 

2단계: 상대경로 vs 절대경로 실전 비교

아래와 같은 디렉토리 구조를 예로 들어보자:

/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';
?>

 

3단계: 실전 예제 - 안전한 프로젝트 구조

실무에서는 프로젝트 루트를 한 번 정의해서 모든 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';
?>

 

4단계: 주의사항과 흔한 실수
상황 ✗ 잘못된 방법 ✓ 올바른 방법
상대경로 사용 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'))

 

dirname() 함수의 레벨 파라미터

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 vs include의 선택

파일이 필수인가? require를 쓰자. 선택사항인가? include를 쓰자.

<?php
// 필수 설정 파일은 require 사용
require_once __DIR__ . '/config.php';

// 선택사항인 템플릿은 include 사용
if (file_exists(__DIR__ . '/templates/header.php')) {
    include __DIR__ . '/templates/header.php';
}
?>

 

5단계: 마무리 - 경로 문제는 설계로 해결

파일 경로 오류는 PHP 개발 초기에 가장 자주 마주치는 문제 중 하나다. 절대경로를 사용하고 프로젝트 루트를 한 번만 정의해두면 대부분의 경로 문제는 사라진다. 특히 팀 프로젝트나 서버 이전 시 상대경로의 불안정성 때문에 고생하는 일을 완벽히 피할 수 있다. 이 글의 __DIR__ 활용법과 dirname() 레벨 파라미터를 참고해 프로젝트 루트를 정의하고, 모든 include/require에 절대경로를 적용하면, 환경이 바뀌어도 안전한 코드를 유지할 수 있을 것이다.