백엔드 API나 DB 연동 과정에서 전달받은 중첩 다차원 배열에서 특정 깊이의 키 값만 모아 추출할 때, 매번 foreach와 isset()을 지저분하게 중첩 사용해본 경험이 있을 것이다. 다만 PHP 기본 제공 함수만으로는 깊은 계층의 데이터(예: 'users.*.profile.name')나 객체가 혼합된 복잡한 구조를 깔끔하게 뽑아내기 어렵다는 문제가 있다. 이번에는 다차원 배열을 안전하게 탐색하고 원하는 키 값만 가공해내는 array_pluck() 서포트 함수가 정확히 뭔지, 왜 필요한지, 그리고 실무에서 바로 쓸 수 있게 구현하는 방법까지 완벽하게 정리해서 소개하겠다.
PHP에는 배열의 특정 컬럼 값을 추출하는 array_column()이라는 훌륭한 기본 함수가 있다. 1차원 또는 단순 2차원 배열에서는 array_column($list, 'name') 형태로 깔끔하게 동작하지만, 배열의 깊이가 3단계 이상으로 깊어지거나 중간에 객체(Object)가 섞인 순간 array_column()은 제 기능을 하지 못한다.
실무에서 자주 다루는 데이터 형태와 array_column()의 한계를 표로 정리하면 다음과 같다.
| 구분 | array_column() 사용 | array_pluck() 서포트 함수 사용 |
|---|---|---|
| 2차원 연관 배열 | 지원함 | 지원함 |
| 3차원 이상 중첩 배열 | 지원 불가 (1단계 깊이만 추출 가능) | 점 표기법('user.profile.name')으로 자유롭게 추출 |
| 배열 내부 객체 혼합 구조 | PHP 7.0 이상 제한적 지원 | 객체와 배열 속성 모두 자동 판별 지원 |
| 와일드카드(*) 계층 탐색 | 지원 불가 | 목록 내 전체 아이템의 하위 속성 추출 지원 |
다만 대부분의 개발자들은 이러한 한계에 부딪혔을 때 매번 중첩 반복문을 작성하거나 array_map() 내부에서 일일이 null 체크를 수행한다. 이 방식은 코드 가독성을 극도로 떨어뜨리고, 예상치 못한 타입 에러를 유발하는 주범이 된다.
우리가 만들 array_pluck() 서포트 함수는 크게 두 가지 핵심 동작으로 구성된다. 첫째, 탐색 대상이 전체 목록인 경우 각 요소에 접근하는 와일드카드 처리 기능이다. 둘째, 'a.b.c' 형태의 점 표기법 주소를 분할하여 배열이나 객체의 깊은 내부까지 에러 없이 탐색하는 도우미 기능이다.
배열이나 객체에서 점 문자열 경로를 파싱하여 값을 가져오는 유틸리티 함수를 먼저 정의해야 한다. 이 함수는 지정한 경로에 값이 존재하지 않더라도 Notice나 Warning 에러를 내지 않고 안전하게 기본값(null)을 반환해야 한다.
다음은 API 응답으로 받은 복잡한 유저 데이터 목록에서 각 유저의 프로필 사진 URL만 뽑아내야 하는 상황의 실무 코드 비교다.
PHP 기본 함수나 중첩 반복문으로 구현하면 코드가 길어지고 중간 키가 빠져있을 때 에러가 발생한다.
<?php
$users = [
['id' => 1, 'profile' => ['avatar' => 'https://example.com/a.jpg']],
['id' => 2, 'profile' => null], // profile이 null인 경우
['id' => 3], // profile 키 자체가 없는 경우
];
$avatars = [];
foreach ($users as $user) {
// 매번 isset이나 empty로 안전성 검사를 해야 함
if (isset($user['profile']) && is_array($user['profile']) && isset($user['profile']['avatar'])) {
$avatars[] = $user['profile']['avatar'];
} else {
$avatars[] = null;
}
}
// array_column을 사용해도 깊은 계층은 가져오지 못함
$failed = array_column($users, 'profile.avatar');
var_dump($failed); // 빈 배열 반환
이 방식은 키의 깊이가 깊어질수록 if 조건문이 기하급수적으로 복잡해지며, array_column()은 하위 키를 인식하지 못해 비어있는 배열만 반환하는 문제가 생긴다.
재귀적 탐색과 점 표기법을 지원하는 array_pluck() 서포트 함수를 만들어 활용해보자.
<?php
/**
* 다차원 배열 또는 객체 목록에서 특정 점 표기법 경로의 값들을 추출합니다.
*
* @param array|object $target 탐색 대상 배열 또는 객체
* @param string|array $key 추출할 점 표기법 키 (예: 'profile.avatar')
* @param mixed $default 값이 없을 때 기본값
* @return array
*/
function array_pluck($target, $key, $default = null) {
$results = [];
if (!is_array($target) && !is_object($target)) {
return $results;
}
$keys = is_array($key) ? $key : explode('.', $key);
foreach ($target as $item) {
$value = $item;
$hasKey = true;
foreach ($keys as $segment) {
if ($segment === '*') {
continue;
}
if (is_array($value) && array_key_exists($segment, $value)) {
$value = $value[$segment];
} elseif (is_object($value) && isset($value->{$segment})) {
$value = $value->{$segment};
} else {
$hasKey = false;
break;
}
}
$results[] = $hasKey ? $value : $default;
}
return $results;
}
// 실무 데이터 적용
$users = [
['id' => 1, 'profile' => ['avatar' => 'https://example.com/a.jpg']],
['id' => 2, 'profile' => null],
['id' => 3, 'profile' => ['avatar' => 'https://example.com/c.jpg']],
];
// 단 한 줄로 안전하게 추출
$avatars = array_pluck($users, 'profile.avatar', 'https://example.com/default.jpg');
print_r($avatars);
Array
(
[0] => https://example.com/a.jpg
[1] => https://example.com/default.jpg
[2] => https://example.com/c.jpg
)
코드 단 몇 줄만으로 중첩된 배열 구조 안에서도 안전하게 데이터를 추출해냈고, 값이 없는 인덱스에는 지정한 기본값(default.jpg)이 정확히 들어간 것을 확인할 수 있다.
서포트 함수를 사용할 때 개발자들이 흔히 범하는 실수는 객체 속성의 접근 제어자(public/private)와 null 값의 존재 유무 처리다.
✗ 잘못된 예: array_key_exists() 없이 단순히 Null Coalescing(??) 연산자만 믿고 객체의 private 속성에 접근하는 경우다. Reflection 없이 직접 접근하려 하면 Fatal Error가 발생하거나 속성을 가져오지 못한다.
✓ 올바른 예: 공개된 public 속성 또는 연관 배열 구조인지 엄격히 검증하고, 배열에 특정 키의 값으로 null이 직접 할당되어 있는 경우(['avatar' => null])와 키 자체가 존재하지 않는 경우를 명확히 구분하여 처리해야 한다.
array_pluck() 서포트 함수는 복잡한 다차원 응답 데이터를 다루는 백엔드 개발자에게 필수적인 유틸리티다. 지저분한 foreach 중첩과 수많은 isset() 검사를 제거해 주며, 코드의 가독성과 유지보수성을 극적으로 향상시킨다.
가벼운 서포트 함수 하나를 프로젝트의 공통 헬퍼 파일에 등록해두는 작은 습관이 모여서, 향후 서비스 스케일이 커졌을 때 데이터 가공 코드 전반의 안정성을 다지는 큰 효과를 만든다. 이 글의 3단계 코드를 작성 중인 프로젝트에 바로 복사해 등록하고 실무 데이터 가공 작업에 직접 적용해보자. 반복문 지옥에서 벗어나 한층 깔끔해진 백엔드 코드를 얻을 수 있을 것이다.