PHP를 다루다 보면 갑자기 터지는 "Call to undefined function" 에러를 경험해봤을까? 대부분의 초급 개발자들은 이 에러가 나면 당황해서 함수명을 다시 확인하고, 오타가 없으면 인터넷에서 다른 사람의 코드를 그냥 복사해 붙인다. 하지만 정확한 원인을 모르면 같은 실수를 반복하게 된다. 이번에는 "Call to undefined function" 에러가 정확히 왜 발생하는지, 어떤 상황에서 나타나는지, 그리고 각 상황별로 어떻게 해결하는지 완벽하게 정리해서 소개하겠다.
이 에러는 PHP가 호출한 함수를 찾을 수 없을 때 발생한다. 단순해 보이지만 원인은 생각보다 다양하다. 함수를 정의하지 않았거나, 파일을 include/require 하지 않았거나, 네임스페이스 때문에 함수가 현재 스코프에서 보이지 않을 수도 있다.
가장 흔한 경우다. 다른 파일에 정의된 함수를 사용하는데 파일을 로드하지 않았다.
// ✗ 잘못된 코드 - functions.php를 로드하지 않음
<?php
echo get_user_name(1); // Call to undefined function get_user_name()
?>
// ✓ 올바른 코드 - 함수 파일을 먼저 include
<?php
require_once __DIR__ . '/functions.php';
echo get_user_name(1); // OK
?>
// functions.php 파일 내용
<?php
function get_user_name($id) {
return "User" . $id;
}
?>
함수를 if 블록 안에 정의했는데 그 조건이 false가 되었다면 함수는 메모리에 로드되지 않는다.
// ✗ 잘못된 코드 - 조건이 false면 함수가 정의되지 않음
<?php
if (false) {
function helper_function() {
return "test";
}
}
echo helper_function(); // Call to undefined function helper_function()
?>
// ✓ 올바른 코드 - 함수를 조건 밖에서 정의
<?php
function helper_function() {
return "test";
}
if (some_condition()) {
echo helper_function(); // OK
}
?>
함수명을 잘못 입력했거나 대소문자를 틀렸다. PHP 함수는 대소문자를 구분하지 않지만, 사용자 정의 함수는 구분하지 않는다... 아니다, PHP 함수명은 모두 대소문자 구분이 없다. 하지만 현대적 PHP 프로젝트에서는 네임스페이스 때문에 케이싱이 중요해질 수 있다.
// ✗ 잘못된 코드 - 함수명 오타
<?php
function my_function() {
return "test";
}
echo My_Function(); // OK (사용자 정의 함수는 대소문자 무시)
echo my_functoin(); // OK (내장 함수도 마찬가지)
// 하지만 클래스 메서드는 다름!
class MyClass {
public function myMethod() {
return "test";
}
}
$obj = new MyClass();
echo $obj->mymethod(); // 경고 또는 에러 가능
?>
// ✓ 올바른 코드 - 정확한 함수명과 일관된 케이싱
<?php
function my_function() {
return "test";
}
echo my_function(); // OK - 항상 정의한 대로 호출
class MyClass {
public function myMethod() {
return "test";
}
}
$obj = new MyClass();
echo $obj->myMethod(); // OK - 정확한 메서드명
?>
현대적인 PHP 프로젝트는 네임스페이스를 사용한다. 다른 네임스페이스에 정의된 함수를 호출할 때 전체 네임스페이스 경로를 명시하지 않으면 같은 네임스페이스 내에서만 찾는다.
// ✗ 잘못된 코드 - 네임스페이스 지정 누락
<?php
namespace AppUtils;
class Helper {
public function process() {
echo format_string("test"); // Call to undefined function
}
}
// 실제로 format_string()은 전역 네임스페이스에 정의됨
namespace {
function format_string($str) {
return strtoupper($str);
}
}
?>
// ✓ 올바른 코드 - 전역 네임스페이스 함수는 를 붙여서 호출
<?php
namespace AppUtils;
class Helper {
public function process() {
echo format_string("test"); // OK - 전역 함수임을 명시
}
}
namespace {
function format_string($str) {
return strtoupper($str);
}
}
?>
일부 함수는 특정 PHP 확장이 설치되어야만 사용할 수 있다. mysqli, PDO, GD, cURL 등이 대표적이다.
// ✗ 잘못된 코드 - 확장이 없는 환경에서 호출
<?php
echo curl_version(); // Call to undefined function curl_version()
// cURL 확장이 설치되지 않았거나 로드되지 않음
?>
// ✓ 올바른 코드 - 함수 존재 여부 확인 후 호출
<?php
if (function_exists('curl_init')) {
echo curl_version(); // OK
} else {
echo "cURL 확장이 설치되지 않았습니다";
}
// 또는 extension_loaded() 확인
if (extension_loaded('curl')) {
echo curl_version();
}
?>
<?php
$function_name = 'my_custom_function';
if (function_exists($function_name)) {
call_user_func($function_name); // 동적으로 함수 호출
} else {
echo "함수 '{$function_name}'이(가) 정의되지 않았습니다";
echo "정의된 함수 목록: ";
print_r(get_defined_functions()['user']);
}
?>
<?php
$functions = get_defined_functions();
echo "사용자 정의 함수: ";
print_r($functions['user']);
echo "내장 함수 개수: " . count($functions['internal']);
?>
// composer.json이 올바르게 설정되었는지 확인
{
"autoload": {
"files": [
"src/functions.php" // 전역 함수 파일
],
"psr-4": {
"App\": "src/" // 클래스 네임스페이스
}
}
}
// Composer 자동로더 재생성
// $ composer dump-autoload
<?php
require __DIR__ . '/vendor/autoload.php';
// 이제 functions.php의 모든 함수 사용 가능
echo my_helper_function(); // OK
?>
| 실수 | 원인 | 해결책 |
|---|---|---|
| 함수를 로드하기 전에 호출 | include/require 순서 문제 | 로드를 먼저 하고, 호출은 나중에 |
| 조건부 정의된 함수 | if 블록 안에 함수 정의 | 함수를 항상 정의하고, 호출 시점에 조건 적용 |
| 네임스페이스 경로 누락 | 다른 네임스페이스의 함수 사용 | use 문 또는 전체 경로() 명시 |
| 대문자/소문자 혼동 | 클래스 메서드명 케이싱 | 정의한 대로 정확하게 호출 |
| 확장 미설치 | 필요한 PHP 확장 없음 | function_exists() 또는 extension_loaded()로 확인 |
"Call to undefined function" 에러는 단순해 보이지만 실제로는 PHP의 파일 로드 구조, 네임스페이스, 그리고 확장 프로그램까지 여러 계층을 이해해야 해결할 수 있다. 초급 개발자들이 자주 놓치는 부분은 "파일을 include했으니 당연히 함수가 있을 것"이라는 가정인데, 실제로는 조건부 로드, 네임스페이스 충돌, 순환 참조 등으로 함수가 메모리에 로드되지 않을 수 있다. 이 글에서 소개한 function_exists()와 get_defined_functions()를 습관적으로 사용하면, 프로덕션 환경에서 이런 에러를 사전에 방지할 수 있을 것이다. 특히 Composer를 사용하는 모던 PHP 프로젝트라면, autoload 설정이 올바른지부터 확인하는 것이 문제 해결의 가장 빠른 방법이다.