배열에서 요소를 삭제한 후 반복문을 돌리면 뭔가 이상하다고 느껴봤을까? 특히 숫자 인덱스를 예상하고 코드를 짰는데 갑자기 중간에 구멍이 뚫려있거나, JSON으로 변환했을 때 배열이 객체처럼 표현되는 문제. 대부분의 개발자들은 이게 PHP의 버그라고 생각하거나, 단순히 array_splice()를 쓰면 된다고 알고 있다. 하지만 array_values()는 훨씬 더 강력하고 실무에서 자주 쓰이는 함수인데 제대로 활용하지 못하는 경우가 많다. 이번에는 array_values()가 정확히 뭔지, 언제 써야 하는지, 어떻게 써야 실수를 피할 수 있는지 완벽하게 정리해서 소개하겠다.
array_values()는 배열의 모든 값을 새로운 배열로 반환하면서 동시에 키를 0부터 순차적으로 리셋한다. 배열의 크기나 내용은 그대로인데, 인덱스(키)만 깔끔하게 정렬된다고 생각하면 된다.
<?php
$fruits = array('apple', 'banana', 'cherry');
$result = array_values($fruits);
var_dump($result);
// 결과:
// array(3) {
// [0]=> string(5) "apple"
// [1]=> string(6) "banana"
// [2]=> string(6) "cherry"
// }
?>
이것만 봐서는 큰 효과가 없어 보인다. 하지만 배열에서 요소를 삭제한 후를 보자.
<?php
$fruits = array('apple', 'banana', 'cherry', 'date');
unset($fruits[1]); // banana 삭제
var_dump($fruits);
// 결과:
// array(3) {
// [0]=> string(5) "apple"
// [2]=> string(6) "cherry"
// [3]=> string(4) "date"
// }
// 이제 array_values() 사용
$fixed = array_values($fruits);
var_dump($fixed);
// 결과:
// array(3) {
// [0]=> string(5) "apple"
// [1]=> string(6) "cherry"
// [2]=> string(4) "date"
// }
?>
보이는가? unset()으로 요소를 삭제하면 배열의 인덱스에 구멍이 생긴다. 근데 array_values()를 거치면 0부터 깔끔하게 다시 정렬된다.
단순히 인덱스 정렬이 뭐 그리 중요하냐고 생각할 수 있지만, 실무에서는 두 가지 큰 문제가 생긴다.
첫 번째. JSON 변환 시 배열이 객체로 표현된다.
<?php
$items = array('apple', 'banana', 'cherry', 'date');
unset($items[1]);
unset($items[3]);
// 문제 상황
echo json_encode($items);
// 결과: {"0":"apple","2":"cherry"}
// 배열이 아니라 객체로 인식됨!
// 올바른 방법
echo json_encode(array_values($items));
// 결과: ["apple","cherry"]
// 진정한 배열로 표현됨
?>
JavaScript에서 배열을 받기를 기대했는데 갑자기 객체가 넘어오면 map()이나 forEach() 같은 배열 메서드를 쓸 수 없다. 이게 얼마나 실수하기 쉬운지는 이미 경험해봤을 것이다.
두 번째. 데이터베이스 조회 후 삭제된 행으로 인한 배열 인덱스 불일치.
<?php
// 조건부로 데이터를 필터링해서 일부 요소를 삭제
$users = [
['id' => 1, 'name' => 'Alice', 'role' => 'admin'],
['id' => 2, 'name' => 'Bob', 'role' => 'user'],
['id' => 3, 'name' => 'Charlie', 'role' => 'user'],
['id' => 4, 'name' => 'David', 'role' => 'admin'],
];
// admin만 필터링
foreach ($users as $key => $user) {
if ($user['role'] !== 'admin') {
unset($users[$key]);
}
}
var_dump($users);
// 배열이 [0=>..., 3=>...] 형태로 인덱스가 뒤죽박죽
// 이를 정렬하려면 array_values() 필수
$users = array_values($users);
var_dump($users);
// 이제 [0=>..., 1=>...] 형태로 깔끔함
?>
방법 1. 연관배열의 값만 추출하기
<?php
$user = [
'name' => 'Alice',
'email' => 'alice@example.com',
'age' => 28,
];
// 연관배열의 값만 뽑아서 숫자 인덱스로 변환
$values = array_values($user);
var_dump($values);
// array(3) {
// [0]=> string(5) "Alice"
// [1]=> string(19) "alice@example.com"
// [2]=> int(28)
// }
?>
방법 2. array_filter()와 조합해서 깔끔한 필터링
<?php
$numbers = [10, 20, 0, 30, 0, 40, 50];
// 0이 아닌 값만 필터링
$filtered = array_filter($numbers);
var_dump($filtered);
// array(5) {
// [0]=> int(10)
// [1]=> int(20)
// [3]=> int(30)
// [5]=> int(40)
// [6]=> int(50)
// }
// 인덱스를 정렬하려면
$clean = array_values(array_filter($numbers));
var_dump($clean);
// array(5) {
// [0]=> int(10)
// [1]=> int(20)
// [2]=> int(30)
// [3]=> int(40)
// [4]=> int(50)
// }
?>
이렇게 array_filter()의 결과를 array_values()로 감싸면, 공백이 생기지 않은 깔끔한 배열을 얻을 수 있다.
방법 3. 문자열 인덱스를 숫자로 변환
<?php
// API 응답이나 데이터 변환 시 인덱스 타입이 혼합되어 있을 수 있다
$mixed = [
'first' => 'apple',
'second' => 'banana',
'third' => 'cherry',
];
// JSON 직렬화 시 문자열 인덱스는 객체로 표현되므로
echo json_encode($mixed);
// 결과: {"first":"apple","second":"banana","third":"cherry"}
// 값만 추출해서 배열로 변환
echo json_encode(array_values($mixed));
// 결과: ["apple","banana","cherry"]
?>
✗ 잘못된 예: array_values()를 까먹고 배열을 직접 사용
<?php
$data = ['apple', 'banana', 'cherry', 'date'];
unset($data[1]);
unset($data[3]);
// 잘못된 방법: 인덱스가 뒤죽박죽인 배열을 바로 JSON으로
echo json_encode($data);
// 결과: {"0":"apple","2":"cherry"}
// 클라이언트에서 배열로 기대했는데 객체가 넘어옴
?>
✓ 올바른 예: array_values()로 인덱스를 정렬 후 사용
<?php
$data = ['apple', 'banana', 'cherry', 'date'];
unset($data[1]);
unset($data[3]);
// 올바른 방법: array_values()로 인덱스 리셋
echo json_encode(array_values($data));
// 결과: ["apple","cherry"]
// 진정한 배열로 클라이언트에 전달됨
?>
✗ 잘못된 예: array_splice() vs array_values() 혼동
<?php
$items = ['a', 'b', 'c', 'd'];
// 이건 다르다. array_splice()는 원본 배열을 수정하고, 삭제된 요소를 반환한다
array_splice($items, 1, 1); // 인덱스 1의 요소('b')를 삭제
var_dump($items);
// array(3) {
// [0]=> string(1) "a"
// [1]=> string(1) "c"
// [2]=> string(1) "d"
// }
// splice()는 자동으로 인덱스를 리셋한다
// 반면 unset()은 원본 배열만 수정하고 인덱스를 리셋하지 않는다
$items = ['a', 'b', 'c', 'd'];
unset($items[1]); // 인덱스 1의 요소('b')를 삭제
var_dump($items);
// array(3) {
// [0]=> string(1) "a"
// [2]=> string(1) "c"
// [3]=> string(1) "d"
// }
// 인덱스가 뒤죽박죽이므로 array_values()가 필요
?>
✓ 올바른 이해: unset()과 array_values()의 조합
<?php
$items = ['a', 'b', 'c', 'd'];
unset($items[1]);
// unset() 후에 항상 array_values()를 생각하자
$items = array_values($items);
var_dump($items);
// array(3) {
// [0]=> string(1) "a"
// [1]=> string(1) "c"
// [2]=> string(1) "d"
// }
?>
array_values()는 새로운 배열을 생성하므로 매우 큰 배열(수백만 개 요소)에서는 성능에 영향을 줄 수 있다. 다만 대부분의 실무 상황에서 이는 무시해도 되는 수준이다. 만약 정말로 큰 배열을 다루고 있다면, array_values()를 마지막에 한 번만 호출하는 방식으로 여러 번의 호출을 피하자.
<?php
// ✗ 비효율적: 반복할 때마다 array_values() 호출
$data = ['a', 'b', 'c', 'd'];
for ($i = 0; $i < 1000; $i++) {
unset($data[$i % count($data)]);
$data = array_values($data); // 매번 리셋
}
// ✓ 효율적: 모든 작업을 끝낸 후 마지막에 한 번만 호출
$data = ['a', 'b', 'c', 'd'];
for ($i = 0; $i < 1000; $i++) {
unset($data[$i % count($data)]);
}
$data = array_values($data); // 마지막에만 리셋
?>
array_values()는 PHP에서 배열의 인덱스를 깔끔하게 정렬하는 가장 간단하고 확실한 방법이다. 특히 JSON 변환을 할 때, 또는 데이터베이스 결과를 필터링한 후에는 반드시 생각해야 한다. 많은 개발자들이 이 함수를 모르거나 무시했다가 나중에 배열이 객체로 변환되는 문제를 겪고 나서야 인지한다. 지금부터는 배열에서 요소를 삭제한 후에는 자동으로 array_values()를 떠올리는 습관을 기르자. 이 작은 습관이 모여서 데이터 직렬화 문제, 클라이언트 측 배열 처리 오류를 완벽하게 방지할 수 있을 것이다.