배열에서 특정 조건을 만족하는 요소만 남기고 싶을 때 반복문으로 일일이 처리한 경험이 있을까. 다만 대부분의 개발자들은 array_filter()의 정확한 동작 방식과 콜백 함수의 세 번째 인자가 뭔지 모른 채 단순 반복문만 사용하곤 한다. 이번에는 array_filter()의 원리부터 시작해서 실전 필터링 패턴까지 완벽하게 정리해서 소개하겠다.
array_filter()는 배열의 각 요소를 주어진 콜백 함수로 평가해서 true를 반환하는 요소만 남기는 함수다. 단순히 null이나 false를 제거하는 게 아니라, 사용자 정의 조건으로 필터링할 수 있다는 게 핵심이다.
함수의 기본 문법은 이렇다.
array_filter(array $array, ?callable $callback = null, int $mode = 0): array
세 개의 인자가 있는데, 각각의 역할을 정확히 알아야 실무에서 제대로 쓸 수 있다.
콜백을 생략하면 array_filter()는 단순히 falsy 값들(0, false, null, 빈 문자열, '0')을 제거한다.
$data = [0, 1, false, 2, '', 3, null];
$result = array_filter($data);
print_r($result);
// Array ( [1] => 1 [3] => 2 [5] => 3 )
보다시피 인덱스 1, 3, 5의 요소만 남고 0, false, '', null은 전부 제거됐다. 주목할 점은 배열의 키(index)는 유지된다는 것. 만약 키 재정렬이 필요하면 array_values()를 따로 사용해야 한다.
실무에서는 거의 항상 콜백을 정의해서 쓴다. 간단한 예제부터 보자.
$numbers = [1, 2, 3, 4, 5, 6];
$even = array_filter($numbers, function($num) {
return $num % 2 === 0;
});
print_r($even);
// Array ( [1] => 2 [3] => 4 [5] => 6 )
짝수만 필터링하는 콜백이다. 콜백이 true를 반환하는 요소만 결과 배열에 포함된다.
array_filter()의 세 번째 인자 $mode는 많은 개발자가 놓치는 부분이다. 이 인자로 콜백 함수가 받는 인자를 제어할 수 있다.
| mode 값 | 콜백 인자 | 설명 |
|---|---|---|
| 0 (기본값) | $value | 요소의 값만 전달 |
| ARRAY_FILTER_USE_KEY | $key | 요소의 키만 전달 |
| ARRAY_FILTER_USE_BOTH | $value, $key | 값과 키 모두 전달 |
각 mode의 실제 사용 사례를 보자.
$user = [
'name' => 'Alice',
'age' => 25,
'email' => 'alice@example.com',
'phone' => null,
'address' => ''
];
// mode 0 (기본): 값만 필터링
$filled = array_filter($user);
// Array ( [name] => Alice [age] => 25 [email] => alice@example.com )
// ARRAY_FILTER_USE_KEY: 키로 필터링
$important = array_filter($user, function($key) {
return in_array($key, ['name', 'email']);
}, ARRAY_FILTER_USE_KEY);
// Array ( [name] => Alice [email] => alice@example.com )
// ARRAY_FILTER_USE_BOTH: 값과 키 모두 사용
$valid = array_filter($user, function($value, $key) {
if ($key === 'age') return $value >= 18;
return $value !== null && $value !== '';
}, ARRAY_FILTER_USE_BOTH);
// Array ( [name] => Alice [age] => 25 [email] => alice@example.com )
특히 ARRAY_FILTER_USE_KEY는 키 이름으로 특정 필드만 추출할 때 매우 유용하다.
전자상거래 시스템에서 가격대별로 상품을 필터링하는 경우다.
✗ 잘못된 코드 (반복문으로 일일이 처리)
$products = [
['name' => '노트북', 'price' => 1500000, 'stock' => 5],
['name' => '마우스', 'price' => 30000, 'stock' => 0],
['name' => '키보드', 'price' => 150000, 'stock' => 3],
['name' => '모니터', 'price' => 350000, 'stock' => 2],
];
$filtered = [];
foreach ($products as $product) {
if ($product['price'] >= 100000 && $product['price'] <= 500000 && $product['stock'] > 0) {
$filtered[] = $product;
}
}
// 반복문이 길어지고 가독성이 떨어짐
✓ 올바른 코드 (array_filter 사용)
$filtered = array_filter($products, function($product) {
return $product['price'] >= 100000
&& $product['price'] <= 500000
&& $product['stock'] > 0;
});
print_r($filtered);
// Array (
// [1] => Array ( [name] => 키보드 [price] => 150000 [stock] => 3 )
// [3] => Array ( [name] => 모니터 [price] => 350000 [stock] => 2 )
// )
array_filter()를 쓰면 조건이 명확하고 코드가 간결해진다. 인덱스가 유지되므로 필요하면 array_values()로 재정렬하면 된다.
$filtered = array_values(array_filter($products, function($product) {
return $product['price'] >= 100000
&& $product['price'] <= 500000
&& $product['stock'] > 0;
}));
사용자 목록에서 특정 권한을 가진 사용자만 필터링하는 경우다.
$users = [
['id' => 1, 'name' => 'Alice', 'role' => 'admin', 'active' => true],
['id' => 2, 'name' => 'Bob', 'role' => 'user', 'active' => true],
['id' => 3, 'name' => 'Charlie', 'role' => 'moderator', 'active' => false],
['id' => 4, 'name' => 'Diana', 'role' => 'admin', 'active' => true],
];
// 활성 상태인 관리자만 필터링
$activeAdmins = array_filter($users, function($user) {
return $user['role'] === 'admin' && $user['active'] === true;
});
foreach ($activeAdmins as $admin) {
echo $admin['name'] . ' (' . $admin['id'] . ')' . PHP_EOL;
}
// 출력:
// Alice (1)
// Diana (4)
필터링 후 데이터 변환이 필요한 경우가 있다. 이때 array_map과 조합하면 강력하다.
✗ 잘못된 코드 (두 번의 반복 처리)
$orders = [
['id' => 1, 'total' => 50000, 'status' => 'completed'],
['id' => 2, 'total' => 75000, 'status' => 'pending'],
['id' => 3, 'total' => 30000, 'status' => 'completed'],
];
// 먼저 필터링
$completed = [];
foreach ($orders as $order) {
if ($order['status'] === 'completed') {
$completed[] = $order;
}
}
// 다시 매핑
$ids = [];
foreach ($completed as $order) {
$ids[] = $order['id'];
}
// 비효율적인 이중 반복
✓ 올바른 코드 (array_filter + array_map)
$ids = array_map(
function($order) { return $order['id']; },
array_filter($orders, function($order) {
return $order['status'] === 'completed';
})
);
print_r($ids);
// Array ( [0] => 1 [2] => 3 )
// array_values로 인덱스 정렬
$ids = array_values($ids);
// Array ( [0] => 1 [1] => 3 )
함수형 체인을 사용하면 코드가 선언적이 되고 부작용이 줄어든다.
✗ 잘못된 방법: 콜백에서 배열 수정하기
$data = ['a' => 1, 'b' => 2, 'c' => 3];
// 절대 금지: 콜백 내에서 배열을 수정하려고 시도
array_filter($data, function(&$value, $key) {
if ($key === 'b') {
unset($value); // 이렇게 하면 예상과 다른 결과 발생
}
return true;
}, ARRAY_FILTER_USE_BOTH);
// array_filter는 새로운 배열을 반환하므로 원본 배열은 변하지 않음
✓ 올바른 방법: 필터링 결과를 새 변수에 저장
$data = ['a' => 1, 'b' => 2, 'c' => 3];
// 정확한 방식
$filtered = array_filter($data, function($value, $key) {
return $key !== 'b';
}, ARRAY_FILTER_USE_BOTH);
print_r($filtered);
// Array ( [a] => 1 [c] => 3 )
array_filter()는 새로운 배열을 반환하는 함수이지, 원본 배열을 수정하지 않는다. 이 점을 착각해서 콜백 내에서 원본을 건드리려고 하는 실수가 자주 발생한다.
또 하나 주의할 점은 인덱스가 유지된다는 것이다. 결과를 JSON으로 인코딩하거나 순차적 접근이 필요하면 array_values()를 반드시 붙여야 한다.
$nums = [1, 2, 3, 4, 5];
$even = array_filter($nums, fn($n) => $n % 2 === 0);
echo json_encode($even);
// 출력: {"1":2,"3":4}
echo json_encode(array_values($even));
// 출력: [2,4]
PHP 7.4부터는 단일 표현식 콜백에 화살표 함수를 쓸 수 있다.
$numbers = [1, 2, 3, 4, 5, 6];
// 기존 방식
$even = array_filter($numbers, function($n) {
return $n % 2 === 0;
});
// 화살표 함수 (훨씬 간결)
$even = array_filter($numbers, fn($n) => $n % 2 === 0);
// 키 조건도 간단히
$users = ['admin_1', 'user_2', 'admin_3', 'guest_1'];
$admins = array_filter(
$users,
fn($user) => str_starts_with($user, 'admin')
);
화살표 함수는 단일 값을 반환하는 간단한 콜백에 최적화되어 있다. 복잡한 로직이 필요하면 일반 function을 쓰는 게 낫다.
array_filter()는 내부적으로 최적화된 C 코드로 동작하므로 PHP 반복문보다 빠르다. 다만 매우 큰 배열(수백만 요소)에서는 메모리 사용량을 고려해야 한다. array_filter()는 새로운 배열을 생성하므로, 필터링 결과가 원본 크기의 절반 이상이면 메모리가 일시적으로 1.5배 이상 필요할 수 있다. 이런 경우는 Generator 기반 필터링이 나을 수 있다.
// Generator로 메모리 효율적 필터링
function filterGenerator($array, $callback) {
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
yield $key => $value;
}
}
}
$filtered = filterGenerator($largeArray, fn($item) => $item['active']);
foreach ($filtered as $item) {
// 한 번에 하나씩만 메모리에 로드됨
}
array_filter()는 배열 필터링의 핵심 함수다. 단순해 보이지만 mode 인자와 콜백 활용법을 정확히 알면 복잡한 데이터 처리도 간결하게 할 수 있다. 특히 화살표 함수와 조합하면 선언적이고 읽기 쉬운 코드를 작성할 수 있다. 이 글에서 소개한 패턴들(가격 필터링, 권한 필터링, 값과 키 동시 필터링)을 실무에 적용하면, 반복문 없이도 안전하고 효율적인 배열 처리가 가능해질 것이다.