PHP 개발하다 보면 갑자기 화면이 하얀색으로 변하면서 로그에 'Call to undefined function' 에러가 떨어진다. 함수를 분명히 정의했는데 왜 없다고 하는 걸까. 이건 단순한 오타가 아니라 함수의 스코프, 네임스페이스, 파일 로드 순서 같은 다양한 원인이 숨어있다. 대부분의 개발자는 에러 메시지만 보고 당황하지만, 원인을 정확히 파악하고 대처하는 방법이 있다. 이번엔 'Call to undefined function' 에러가 나는 모든 상황과 그 해결책을 실제 코드로 정리해서 설명하겠다.
이 에러는 PHP가 당신이 호출한 함수를 찾을 수 없다는 뜻이다. 크게 5가지 원인으로 나뉜다. 첫째, 함수가 정의된 파일을 include/require 하지 않았다. 둘째, 함수 이름을 잘못 입력했다(오타나 대소문자). 셋째, 함수가 조건부로 정의되어 있어서 실행되지 않았다. 넷째, 네임스페이스 때문에 함수를 찾지 못한다. 다섯째, 함수가 선언되기 전에 호출했다.
가장 흔한 경우다. 다른 파일에 정의된 함수를 사용하려면 반드시 그 파일을 include나 require로 로드해야 한다. 로드하지 않으면 PHP는 그 함수의 존재조차 모른다.
✗ 잘못된 코드
<?php
// index.php
$result = helper_function();
?>
// helper.php에 정의된 함수
<?php
function helper_function() {
return "Hello";
}
?>
// 에러 발생: Call to undefined function helper_function()
✓ 올바른 코드
<?php
// index.php
require_once 'helper.php';
$result = helper_function();
echo $result; // "Hello" 출력
?>
require_once를 쓰는 이유는 같은 파일을 여러 번 로드하는 것을 방지하고, 중복 정의 에러를 피하기 위함이다. include는 파일 로드 실패 시 경고만 표시하고 계속 실행되지만, require는 에러를 던지고 실행을 멈춘다. 보안상으로는 require가 낫다.
오타나 대소문자 불일치도 흔한 원인이다. PHP 함수 이름은 대소문자를 구분하지 않지만, 호출할 때와 정의할 때 정확히 같아야 한다.
✗ 잘못된 코드
<?php
function calculate_total() {
return 100;
}
$sum = calcuate_total(); // 오타: calculate의 'l'을 빠뜨림
echo $sum;
// 에러 발생: Call to undefined function calcuate_total()
?>
✓ 올바른 코드
<?php
function calculate_total() {
return 100;
}
$sum = calculate_total(); // 올바른 함수명
echo $sum; // 100 출력
?>
IDE의 자동완성을 쓰거나, PHP_CodeSniffer 같은 린터를 도입하면 이런 실수를 미리 잡을 수 있다.
함수를 if 문 안에 정의하면, 그 조건이 거짓일 때 함수가 존재하지 않는다. 함수 정의는 실행 흐름을 탄다.
✗ 잘못된 코드
<?php
if (false) {
function conditional_function() {
return "Never defined";
}
}
echo conditional_function(); // 에러 발생
?>
✓ 올바른 코드
<?php
// 방법 1. 함수는 최상단에 정의하고, 호출은 조건부로 한다
function conditional_function() {
return "Defined";
}
if (true) {
echo conditional_function(); // "Defined" 출력
}
// 방법 2. 함수가 존재하는지 먼저 확인한다
if (function_exists('some_function')) {
some_function();
} else {
echo "Function not found";
}
?>
function_exists() 함수로 런타임에 함수 존재 여부를 확인할 수 있다. 플러그인이나 훅 시스템처럼 함수 존재 여부가 불확실한 경우 유용하다.
네임스페이스를 쓸 때는 함수 호출도 네임스페이스를 고려해야 한다. 전역 네임스페이스의 함수를 호출하려면 백슬래시(\)를 앞에 붙여야 한다.
✗ 잘못된 코드
<?php
namespace MyApp;
function my_function() {
return "Inside MyApp namespace";
}
namespace AnotherApp;
echo my_function(); // 에러 발생
// PHP는 AnotherApp\my_function()을 찾으려 한다
?>
✓ 올바른 코드
<?php
namespace MyApp;
function my_function() {
return "Inside MyApp namespace";
}
namespace AnotherApp;
echo MyApp\my_function(); // "Inside MyApp namespace" 출력
// 또는 use 문으로 임포트
use MyApp\my_function;
echo my_function(); // 같은 결과
?>
Composer 오토로더를 쓰는 경우, 클래스처럼 함수도 use 문으로 임포트할 수 있다(PHP 5.6+). 이렇게 하면 호출할 때 네임스페이스를 생략할 수 있다.
드물지만, include 순서나 함수 정의 순서가 맞지 않으면 발생할 수 있다. 특히 여러 파일에서 함수를 호출할 때 로드 순서가 중요하다.
✗ 잘못된 코드
<?php
// bootstrap.php
require_once 'routes.php'; // routes.php에서 helper_function()을 호출
require_once 'helpers.php'; // helper_function이 여기서 정의됨
?>
✓ 올바른 코드
<?php
// bootstrap.php
require_once 'helpers.php'; // 먼저 helper_function을 정의
require_once 'routes.php'; // 그 다음 사용
?>
Composer를 쓰는 프로젝트라면 함수를 PSR-4 오토로더로 관리하는 게 좋다. 직접 require 하지 않아도 자동으로 파일을 로드해준다.
// composer.json
{
"require": {},
"autoload": {
"files": ["src/helpers.php"],
"psr-4": {"App\\": "src/"}
}
}
composer.json에 "files" 키 안에 함수 파일을 등록하면, composer 오토로더가 부트스트랩할 때 자동으로 로드한다. 클래스와 다르게 함수는 이런 식으로 관리하는 게 일반적이다.
// 프로젝트 진입점 (index.php나 app.php)
<?php
require_once 'vendor/autoload.php';
// 이제 어디서나 helper_function() 호출 가능
echo helper_function();
?>
'Call to undefined function' 에러가 나면 먼저 다음을 확인하자.
| 확인 항목 | 디버깅 방법 |
|---|---|
| 함수가 정의되었는가 | grep -r "function my_function" . 로 코드에서 함수 정의를 찾거나, IDE의 검색 기능 사용 |
| 파일을 로드했는가 | 해당 파일이 include/require 되었는지 확인. 에러 로그의 스택 트레이스 보기 |
| 함수 이름이 맞는가 | 정의된 함수명과 호출한 함수명을 대소문자까지 정확히 비교 |
| 네임스페이스는 맞는가 | 현재 파일의 namespace와 함수가 정의된 파일의 namespace 확인 |
| 조건부 정의는 아닌가 | 함수 정의 코드를 grep하고, if/switch 등 조건문 안에 있는지 확인 |
PHP 에러 로그를 항상 켜두는 것도 중요하다. php.ini에서 display_errors와 error_log를 설정하면, 에러 발생 시 정확한 파일명과 줄 번호를 볼 수 있다.
// php.ini
error_reporting = E_ALL
display_errors = On
error_log = /var/log/php-errors.log
PHPStan이나 Psalm 같은 정적 분석 도구를 쓰면, 코드를 실행하지 않고도 이런 에러를 미리 잡을 수 있다.
composer require --dev phpstan/phpstan
./vendor/bin/phpstan analyse src/
PHPStan은 함수 호출 전에 함수가 정의되어 있는지, 파라미터 타입이 맞는지 등을 점검한다. 다만 동적으로 정의되거나 외부 라이브러리에서 주입되는 함수는 감지하지 못할 수 있다.
| 실수 | 원인 | 해결책 |
|---|---|---|
| require 'file' 후에도 에러 발생 | 파일 경로가 잘못되었거나, 파일이 없음 | require_once로 변경하고, 경로를 절대 경로(__DIR__)로 지정 |
| 네임스페이스 내에서 함수 호출 실패 | 같은 네임스페이스에만 있는 줄 앎 | 전역 함수는 앞에 백슬래시(\) 붙이기 |
| WordPress/Laravel 플러그인에서 에러 | 함수가 조건부 로드되거나 훅에 등록됨 | function_exists() 또는 has_action() 등으로 먼저 확인 |
| Composer 오토로더 후에도 안 됨 | composer.json 수정 후 dump-autoload를 실행하지 않음 | composer dump-autoload 실행 |
'Call to undefined function' 에러는 단순한 오타가 아니라 파일 로드 순서, 네임스페이스, 함수 정의 위치 같은 여러 원인이 얽혀 있을 수 있다. 에러 로그에서 정확한 함수명과 파일명을 읽고, 위의 5가지 원인을 순서대로 확인하면 대부분 해결된다. PHPStan 같은 정적 분석 도구를 개발 단계에 도입하면, 프로덕션 서버에서 만나기 전에 미리 잡을 수 있다. 이 글의 디버깅 체크리스트를 북마크해두고, 다음에 이 에러가 나면 순서대로 확인해보자.