PHP에서 배열을 다루다 보면 특정 조건에 맞는 요소만 추출하거나, 불필요한 데이터를 걸러내야 할 상황이 자주 생긴다. 대부분의 개발자들은 foreach 루프를 돌면서 if문으로 하나하나 체크하고 새로운 배열에 담는 방식을 써왔을 것이다. 다만 array_filter 함수를 제대로 이해하고 활용하면, 코드를 훨씬 간결하게 만들 수 있을 뿐 아니라 가독성과 유지보수성도 크게 향상된다. 이번에는 array_filter의 정확한 동작 원리부터 실무에서 자주 쓰이는 활용 사례까지 완벽하게 정리해서 소개하겠다.
array_filter는 배열의 각 요소를 콜백 함수에 통과시켜서, 콜백이 true를 반환한 요소만 남기고 나머지는 제거하는 함수다. 기본 문법은 다음과 같다:
array_filter(array $array, ?callable $callback = null, int $mode = 0): array
여기서 중요한 점은 원본 배열을 수정하지 않고 새로운 배열을 반환한다는 것이다. 또한 키(key)는 유지된다. 예를 들어, 배열 인덱스가 0, 1, 2, 3, 4인데 2번과 4번 요소가 제거되면, 결과 배열의 인덱스는 0, 1, 3이 된다.
array_filter를 사용하는 가장 일반적인 방식은 콜백 함수를 정의하고, 각 요소에 대해 true/false를 판단하는 것이다.
$numbers = [1, 2, 3, 4, 5, 6];
// ✗ 잘못된 방식 - 루프 돌면서 수동으로 필터링
$even = [];
foreach ($numbers as $num) {
if ($num % 2 == 0) {
$even[] = $num;
}
}
print_r($even);
// 결과: Array ( [0] => 2 [1] => 4 [2] => 6 )
// ✓ 올바른 방식 - array_filter 사용
$even = array_filter($numbers, function($num) {
return $num % 2 == 0;
});
print_r($even);
// 결과: Array ( [1] => 2 [3] => 4 [5] => 6 )
// 주목: 원본 인덱스 1, 3, 5가 유지됨
✗ 잘못된 코드: foreach로 수동으로 필터링하면 코드가 길어지고, 인덱스를 다시 재정렬해야 하는 부담이 있다. 또한 인덱스 재정렬이 필요하면 추가 로직을 작성해야 한다.
✓ 올바른 코드: array_filter를 쓰면 한 줄로 끝나고, 결과도 명확하다. 다만 원본 인덱스가 유지된다는 점을 주의해야 한다.
$numbers = [1, 2, 3, 4, 5, 6];
// ✓ 간결한 화살표 함수
$even = array_filter($numbers, fn($num) => $num % 2 == 0);
print_r($even);
// 결과: Array ( [1] => 2 [3] => 4 [5] => 6 )
PHP 7.4 이상이라면 화살표 함수를 쓰면 훨씬 간결해진다.
$data = ['apple', '', 'banana', 0, 'cherry', false, null];
// ✓ 콜백 함수 생략 - 거짓값(falsy) 제거
$filtered = array_filter($data);
print_r($filtered);
// 결과: Array ( [0] => apple [2] => banana [4] => cherry )
// 공 문자열, 0, false, null이 모두 제거됨
콜백을 생략하면, PHP가 자동으로 거짓값(empty string, 0, false, null)을 제거한다. 이는 데이터 정제에 매우 유용하다.
$users = [
['id' => 1, 'name' => 'Alice', 'status' => 'active'],
['id' => 2, 'name' => 'Bob', 'status' => 'inactive'],
['id' => 3, 'name' => 'Charlie', 'status' => 'active'],
['id' => 4, 'name' => 'Diana', 'status' => 'inactive'],
];
// ✓ 활성 사용자만 필터링
$activeUsers = array_filter($users, function($user) {
return $user['status'] === 'active';
});
foreach ($activeUsers as $user) {
echo $user['name'] . " (" . $user['id'] . ")n";
}
// 출력:
// Alice (1)
// Charlie (3)
$tags = ['php', 'a', 'javascript', 'b', 'mysql', 'c'];
// ✓ 3글자 이상인 태그만 추출
$validTags = array_filter($tags, fn($tag) => strlen($tag) >= 3);
print_r($validTags);
// 결과: Array ( [0] => php [2] => javascript [4] => mysql )
$numbers = [1, 2, 3, 4, 5, 6];
// ✗ 잘못된 방식 - 인덱스가 재정렬되지 않음
$even = array_filter($numbers, fn($num) => $num % 2 == 0);
print_r($even);
// 결과: Array ( [1] => 2 [3] => 4 [5] => 6 )
// 인덱스가 원본대로 유지됨
// ✓ 올바른 방식 - array_values로 인덱스 재정렬
$even = array_values(array_filter($numbers, fn($num) => $num % 2 == 0));
print_r($even);
// 결과: Array ( [0] => 2 [1] => 4 [2] => 6 )
// 인덱스가 0부터 다시 시작
✗ 잘못된 코드: array_filter만 쓰면 원본 인덱스가 유지되므로, JSON 직렬화나 순차 접근 시 문제가 될 수 있다.
✓ 올바른 코드: array_values()를 체이닝하면 인덱스가 0부터 재정렬되어 안전하다.
$data = [
'user_id' => 123,
'email' => 'test@example.com',
'password' => 'secret',
'token' => 'abc123',
'created_at' => '2024-01-01',
];
// ✓ 'password'와 'token'은 제외하고 나머지만 반환
$safe = array_filter($data, function($key) {
return !in_array($key, ['password', 'token']);
}, ARRAY_FILTER_USE_KEY);
print_r($safe);
// 결과: Array ( [user_id] => 123 [email] => test@example.com [created_at] => 2024-01-01 )
ARRAY_FILTER_USE_KEY 상수를 사용하면, 콜백 함수가 받는 인자가 값(value) 대신 키(key)가 된다. 이는 특정 키를 조건으로 필터링할 때 유용하다.
| 실수 | 설명 | 해결 방법 |
|---|---|---|
| 인덱스 유지 모르기 | array_filter는 원본 인덱스를 유지하므로, 연속적인 인덱스가 필요하면 문제 | array_values()로 재정렬 |
| 콜백에서 side effect 발생 | 콜백 함수 안에서 전역 변수 수정이나 데이터베이스 쓰기 등을 하면 예상치 못한 동작 발생 | 콜백은 순수 함수처럼 - 값만 반환하고 상태 수정 금지 |
| 큰 배열에서 성능 저하 | 매우 큰 배열에서 복잡한 콜백이 있으면 성능 저하 | 가능하면 캐싱하거나, 미리 인덱싱된 데이터 구조 사용 |
| 타입 체크 부족 | 콜백에서 타입을 엄격하게 체크하지 않으면 예상치 못한 데이터가 통과 | 타입 힌트와 === 비교 사용 |
$counter = 0;
$numbers = [1, 2, 3, 4, 5];
// ✗ 잘못된 코드 - 콜백 안에서 전역 상태 수정
$filtered = array_filter($numbers, function($num) use (&$counter) {
$counter++; // 이것은 예상 불가능한 동작을 야기할 수 있음
return $num > 2;
});
echo "카운터: $countern";
// 출력: 카운터: 5 (모든 요소가 체크됨)
// ✓ 올바른 코드 - 순수 함수처럼 작성
$filtered = array_filter($numbers, fn($num) => $num > 2);
$counter = count($numbers); // 필요하면 밖에서 별도로 처리
$data = [0, '0', false, '', null, '100'];
// ✗ 잘못된 코드 - 느슨한 타입 비교
$filtered = array_filter($data, fn($val) => $val > 0);
print_r($filtered);
// 결과: Array ( [5] => 100 )
// '0' 문자열이 0으로 변환되어 체크됨 (예상과 다름)
// ✓ 올바른 코드 - 타입과 값 모두 체크
$filtered = array_filter($data, fn($val) => is_numeric($val) && (int)$val > 0);
print_r($filtered);
// 결과: Array ( [5] => 100 )
array_filter는 배열 필터링의 가장 강력한 도구다. 단순한 반복문 대신 array_filter를 사용하면 코드가 명확해지고, 함수형 프로그래밍의 장점을 누릴 수 있다. 다만 원본 인덱스가 유지된다는 점과, 콜백 함수는 순수 함수처럼 작성해야 한다는 두 가지 원칙을 잊지 말자. 특히 데이터 정제나 조건부 추출이 자주 나오는 실무 코드에서 이 글의 예제들을 참고해 array_filter를 적극 활용하면, 더 간결하고 유지보수하기 쉬운 PHP 코드를 작성할 수 있을 것이다.