PHP 배열을 다루다 보면 갑자기 화면에 떠서 당황하게 되는 경고가 있다. 'Warning: Trying to access array offset on value of type null'이다. 코드는 잘 작동하는 것 같은데 경고만 나타나고, 실제로는 예상과 다른 결과가 나오거나 에러 로그가 가득 찬다. 대부분의 개발자는 이 경고를 무시하거나 error_reporting을 낮춰버리는 식으로 대충 넘어간다. 하지만 이것은 잠재된 버그의 신호다. 이번에는 이 경고가 정확히 뭔지, 왜 발생하는지, 어떻게 완벽하게 해결하는지 정리해서 소개하겠다.

 

1단계. null 타입 배열 접근이 뭔가

PHP 변수는 처음부터 어떤 타입일지 정해지지 않는다. 함수에서 데이터를 받을 때 배열을 기대했는데 null이 반환되거나, API 응답 파싱에 실패해서 null이 할당되는 경우가 자주 일어난다. 그 상태에서 배열처럼 대괄호로 접근하면 PHP는 경고를 내보낸다.

$data = null; // API 응답 실패
echo $data['key']; // Warning: Trying to access array offset on value of type null

이 경고는 PHP 7.4부터 명시적으로 구분되기 시작했다. 이전 버전에서는 'Trying to access offset on value of type'이라는 더 모호한 메시지였다. PHP 8.0 이후로는 더욱 엄격해져서 Strict mode에서는 이것이 에러(Fatal Error)가 되기도 한다.

 

2단계. 왜 이런 일이 일어날까

원인은 크게 5가지다.

원인 예시
함수 반환값 체크 누락 json_decode()가 null을 반환했는데 바로 접근
배열 키 존재 여부 미확인 $_GET['param']이 없을 때 바로 $data['items'][0]으로 접근
쿼리 결과 null 처리 누락 데이터베이스에서 가져온 행이 null일 때 필드 접근
타입 변환 실패 배열로 강제 형변환했지만 실제로는 null
조건부 반환에서 기본값 누락 특정 조건에만 배열을 반환하고 다른 경우 return;

 

3단계. 완벽한 해결 방법들

 

방법 1. isset() + null coalescing으로 체크하기

✗ 잘못된 코드. 경고 발생한다.

$response = json_decode($json, true);
echo $response['data']['name']; // null이면 경고

✓ 올바른 코드. isset()으로 먼저 체크한다.

$response = json_decode($json, true);
echo isset($response['data']['name']) ? $response['data']['name'] : 'Unknown';

또는 Null Coalescing Operator를 사용한다.

$response = json_decode($json, true);
echo $response['data']['name'] ?? 'Unknown';

다만 Null Coalescing은 정의되지 않은 키에는 경고를 내지 않지만, null 값에 바로 접근하면 여전히 경고가 난다. 예를 들어 $response가 null 상태라면 $response['data']는 경고를 낸다.

 

방법 2. 함수 반환 시 기본값 명시하기

✗ 잘못된 코드. null 반환의 가능성을 무시했다.

function getUser($id) {
    $user = fetchFromDB($id);
    if ($user) {
        return $user; // 데이터가 있으면
    }
    // 없으면 그냥 반환 (null이 반환됨)
}

$user = getUser(999);
echo $user['name']; // null이면 경고

✓ 올바른 코드. 빈 배열을 기본값으로 반환한다.

function getUser($id) {
    $user = fetchFromDB($id);
    if ($user) {
        return $user;
    }
    return []; // 빈 배열을 반환하거나
    // return null; // 명시적으로 타입 힌트 추가
}

$user = getUser(999);
echo $user['name'] ?? 'Not found'; // 안전함

더 좋은 방법은 타입 힌트를 추가하는 것이다.

function getUser($id): ?array {
    $user = fetchFromDB($id);
    return $user ?: null; // 명시적으로 ?array 타입 표시
}

$user = getUser(999);
if ($user) {
    echo $user['name'];
}

 

방법 3. is_array()로 타입 먼저 확인하기

✗ 잘못된 코드. 배열인지 확인하지 않았다.

$items = API::getData();
foreach ($items as $item) { // $items가 null이면 경고
    echo $item['id'];
}

✓ 올바른 코드. 먼저 배열 타입을 확인한다.

$items = API::getData();
if (is_array($items) && count($items) > 0) {
    foreach ($items as $item) {
        echo $item['id'];
    }
} else {
    echo 'No data';
}

더 간결하게는 이렇게 쓸 수 있다.

$items = API::getData() ?: [];
foreach ($items as $item) {
    echo $item['id'] ?? 'N/A';
}

 

방법 4. array_key_exists() + 필터링 함수 조합

중첩된 배열을 다룰 때 안전한 접근 함수를 만들어 사용하면 코드가 깔끔해진다.

function arrayGetNested($array, $path, $default = null) {
    $keys = explode('.', $path);
    $value = $array;
    
    foreach ($keys as $key) {
        if (!is_array($value) || !array_key_exists($key, $value)) {
            return $default;
        }
        $value = $value[$key];
    }
    
    return $value;
}

$data = json_decode($json, true);
echo arrayGetNested($data, 'response.user.profile.name', 'Unknown');

이 함수는 null이 나오는 지점에서 즉시 기본값을 반환하므로 경고가 절대 나지 않는다.

 

4단계. 흔한 실수와 해결책

 

실수 1. error_reporting을 낮춰서 경고 숨기기

✗ 절대 하지 말 것.

error_reporting(E_ERROR | E_PARSE); // 경고 무시
$data = null;
echo $data['key']; // 경고가 안 보이지만 여전히 문제

✓ 올바른 방법.

error_reporting(E_ALL); // 모든 경고 표시
$data = null;
echo $data['key'] ?? 'default'; // 경고 없이 안전함

 

실수 2. 빈 문자열 검사로 충분하다고 생각하기

✗ 잘못된 코드. null과 빈 문자열은 다르다.

if ($response != '') { // 빈 문자열만 체크
    echo $response['data']; // null이면 경고
}

✓ 올바른 코드. is_array() 또는 isset()으로 체크한다.

if (is_array($response)) {
    echo $response['data'] ?? 'N/A';
}

 

실수 3. Optional Chaining처럼 한 번에 여러 깊이 접근하기

✗ 잘못된 코드. 중간에 null이 있으면 경고.

echo $config['database']['host']['port']; // 어디서 null이 나올지 모름

✓ 올바른 코드. 단계별 체크하거나 위의 arrayGetNested() 같은 헬퍼 함수 사용.

$db = $config['database'] ?? null;
$host = $db['host'] ?? null;
$port = $host['port'] ?? 3306;
echo $port;

// 또는
echo arrayGetNested($config, 'database.host.port', 3306);

 

5단계. 정적 분석 도구로 미리 잡기

PHPStan이나 Psalm 같은 정적 분석 도구를 사용하면 실행하기 전에 이런 경고들을 미리 발견할 수 있다.

composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse src/

이 도구들은 null 가능성(nullability)을 자동으로 추적해서 경고를 낸다. IDE에서 실시간으로 피드백을 받을 수도 있다.

 

마무리

'Trying to access array offset on value of type null'은 단순한 경고가 아니라, 코드에 잠재된 버그를 알려주는 신호다. 작은 최적화와 방어적 코딩 습관이 모여서 유지보수하기 쉽고 안정적인 애플리케이션을 만든다는 점을 잊지 말자. 이 글에서 소개한 isset(), null coalescing, is_array() 같은 기본 도구들을 습관처럼 사용하고, 더 복잡한 중첩 구조는 arrayGetNested() 같은 헬퍼 함수를 제작해서 쓰면, 이 경고와 완전히 작별할 수 있을 것이다.