배열에서 중복 값을 제거하려고 array_unique()를 썼는데, 예상과 다르게 중복이 남아있거나 필요한 값까지 사라져버린 경험이 있을까? 대부분의 개발자는 array_unique()가 단순히 '같은 값'을 찾아 제거하는 함수라고 생각하지만, 실제로는 타입 비교 방식에 따라 결과가 크게 달라진다. 이번에는 array_unique()가 정확히 뭔지, 왜 예상과 다른 결과가 나오는지, 어떻게 올바르게 사용하는지 완벽하게 정리해서 소개하겠다.

 

1단계. array_unique() 기초 이해하기

array_unique()는 배열에서 중복된 값을 제거하는 기본 함수다. 첫 번째로 나타난 값은 유지하고, 그 이후 중복된 값들을 삭제한다. 다만 여기서 중요한 점은 '어떻게 비교하는가'다.

기본 동작
array_unique()는 기본적으로 느슨한 타입 비교(loose comparison)를 사용한다. 즉, 정수 1과 문자열 "1"을 같은 값으로 본다는 뜻이다. 이는 PHP의 Type Juggling 특성 때문이다.

<?php
$array = [1, "1", 2, "2", 1, "1"];
$result = array_unique($array);
print_r($result);
?>

예상되는 결과: 배열 [1, 2] (정수만 남음)
실제 결과:

Array
(
    [0] => 1
    [2] => 2
    [4] => 1  // 이건 문자열 "1"인데 출력에선 구분 안 됨
)

보이는 값은 같아 보이지만, 키와 순서에서 차이가 난다. 정수 1이 [0]과 [4] 두 위치에 남아있다.

 

2단계. 상세 방법: 비교 모드 선택하기

array_unique()는 네 번째 파라미터로 비교 모드를 지정할 수 있다.

array_unique(array $array, int $flags = SORT_STRING): array

$flags 옵션

플래그 설명 비교 방식
SORT_REGULAR(기본값) 정규 비교 느슨한 타입 비교 (==), 1 == "1"
SORT_NUMERIC 숫자로 비교 숫자 값 기준, 1과 "1" 같음
SORT_STRING 문자열로 비교 문자열 기준, "1"과 1 다름

잘못된 코드 (✗)

<?php
$array = [1, "1", 2, "2"];
$result = array_unique($array); // SORT_REGULAR 기본 적용
print_r($result);
// 결과: [0 => 1, 2 => 2] - "1"과 "2"는 제거됨
?>

이 코드는 개발자 의도를 불명확하게 한다. 숫자와 문자열이 섞여 있을 때 어떤 비교를 원하는지 명시하지 않았기 때문이다.

올바른 코드 (✓) - 문자열 기반 비교

<?php
$array = [1, "1", 2, "2"];
$result = array_unique($array, SORT_STRING);
print_r($result);
// 결과: [0 => 1, 1 => "1", 2 => 2, 3 => "2"] - 아무것도 제거 안 됨
?>

SORT_STRING을 지정하면 정수 1과 문자열 "1"을 다른 값으로 취급한다. 따라서 모든 값이 유지된다.

올바른 코드 (✓) - 숫자 기반 비교

<?php
$array = [1, "1", 2, "2", 1];
$result = array_unique($array, SORT_NUMERIC);
print_r($result);
// 결과: [0 => 1, 2 => 2] - "1"과 "2"는 제거됨
?>

이제 명확하게 숫자 값 기준으로 비교했으므로, 의도가 드러난다.

 

3단계. 실전 예제: 타입 섞임 처리

사례 1: CSV 파일에서 읽은 데이터

<?php
// CSV에서 읽으면 모든 값이 문자열
$imported_ids = ["123", "456", "123", "789"];

// 잘못된 방법: SORT_REGULAR 사용
$wrong = array_unique($imported_ids);
// 결과: [0 => "123", 1 => "456", 3 => "789"] - 중복 제거 잘됨

// 하지만 데이터베이스의 정수 ID와 비교할 때
$db_ids = [123, 456, 789];
$wrong_result = array_diff($db_ids, $wrong);
// 결과: [0 => 123, 1 => 456, 2 => 789] - SORT_REGULAR 비교 때문에 예상 결과와 다를 수 있음

// 올바른 방법: 명시적으로 타입 변환
$clean_ids = array_map('intval', $imported_ids);
$result = array_unique($clean_ids, SORT_NUMERIC);
print_r($result);
// 결과: [0 => 123, 1 => 456, 3 => 789]
?>

사례 2: 연관 배열에서 특정 필드 중복 제거

<?php
$users = [
    ['id' => 1, 'email' => 'john@example.com'],
    ['id' => 2, 'email' => 'jane@example.com'],
    ['id' => 1, 'email' => 'john@example.com'], // 중복
];

// ✗ array_unique()로 연관 배열 전체를 비교하려고 하면 실패
$wrong = array_unique($users);
// 결과: 모든 배열 요소가 유지됨 (배열은 직렬화되어 비교됨)

// ✓ 특정 필드만 추출해서 중복 제거
$seen_ids = [];
$unique_users = [];
foreach ($users as $user) {
    if (!in_array($user['id'], $seen_ids, true)) {
        $seen_ids[] = $user['id'];
        $unique_users[] = $user;
    }
}
print_r($unique_users);
// 결과: 두 개의 사용자만 출력
?>

사례 3: 부동소수점 숫자의 함정

<?php
$prices = [1.5, "1.5", 1.50, "1.50"];

// ✗ SORT_NUMERIC으로 비교
$wrong = array_unique($prices, SORT_NUMERIC);
print_r($wrong);
// 결과: [0 => 1.5, 2 => 1.5] 
// "1.5"와 "1.50"은 문자열로는 다르지만, SORT_NUMERIC으로는 같음

// ✓ 명시적으로 타입 강제 변환
$clean = array_map('floatval', $prices);
$result = array_unique($clean, SORT_NUMERIC);
print_r($result);
// 결과: [0 => 1.5] - 확실히 하나만 남음
?>

 

4단계. 주의사항과 흔한 실수

✗ 잘못된 것: 깊은 배열에 array_unique() 직접 적용

<?php
// 다차원 배열
$data = [
    [1, 2],
    [1, 2],
    [2, 3]
];

$result = array_unique($data);
// 배열은 직렬화되어 비교되므로 예상과 다를 수 있음
print_r($result);
?>

✓ 올바른 것: JSON 직렬화 후 비교

<?php
$data = [
    [1, 2],
    [1, 2],
    [2, 3]
];

// 배열을 JSON 문자열로 변환해서 비교
$result = array_unique(array_map('json_encode', $data));
$result = array_map('json_decode', $result, [true]);
print_r($result);
// 결과: [[1, 2], [2, 3]]
?>

✗ 잘못된 것: 키 유지를 간과한 경우

<?php
$array = ['a' => 1, 'b' => 2, 'c' => 1];
$result = array_unique($array);
print_r($result);
// 결과: ['a' => 1, 'b' => 2] - 원본 키가 유지됨
// 이를 배열로 변환하려면 array_values() 필요
?>

✓ 올바른 것: 키 초기화까지 완료

<?php
$array = ['a' => 1, 'b' => 2, 'c' => 1];
$result = array_values(array_unique($array));
print_r($result);
// 결과: [0 => 1, 1 => 2] - 키가 0부터 시작하도록 초기화됨
?>

 

5단계. 마무리 및 다음 단계

array_unique()는 배열 중복 제거의 기본 도구지만, 타입 비교 방식을 제어하지 못하면 예상과 다른 결과를 얻는다. 정수와 문자열이 섞여 있을 때는 SORT_STRING으로 명시하고, 연관 배열이나 다차원 배열은 상황에 맞춰 다른 접근을 택해야 한다. 작은 타입 체크 습관이 모여서 데이터 손실 없는 안정적인 배열 처리를 만든다는 점을 잊지 말자. 이 글의 플래그 선택 부분을 참고해 현재 프로젝트의 array_unique() 호출마다 의도를 명시하면, 데이터 무결성 문제를 미연에 방지할 수 있을 것이다.