PHP로 배열을 다루다 보면 특정 키가 존재하는지, 값이 있는지 확인해야 할 순간이 자주 온다. 다만 대부분의 개발자들은 isset(), array_key_exists(), in_array() 세 함수의 차이를 명확히 모른 채 상황에 맞지 않는 함수를 써서 버그를 만든다. 이번에는 이 세 함수가 정확히 뭐가 다르고, 언제 어떤 걸 써야 하는지, 그리고 각각이 어떻게 동작하는지 완벽하게 정리해서 소개하겠다.

 

1단계: isset(), array_key_exists(), in_array() 기초 이해하기

먼저 세 함수의 역할을 명확히 구분해보자.

isset()은 변수나 배열 키가 "존재하면서 동시에 NULL이 아닌지"를 확인한다. 키가 없거나, 키는 있지만 값이 NULL이면 false를 반환한다.

array_key_exists()는 배열에 특정 키가 "존재하는지만" 확인한다. 값이 NULL이든 빈 문자열이든 상관없이, 키 자체의 존재 여부만 본다.

in_array()는 배열에 특정 "값"이 존재하는지 확인한다. 키는 무시하고 값의 존재만 본다.

이 세 함수는 확인 대상(키 vs 값)과 NULL 값 처리 방식에서 근본적으로 다르다. 잘못된 함수를 쓰면 예상과 다른 결과가 나온다.

 

2단계: 세 함수의 동작 방식 자세히 살펴보기

실제 코드로 각 함수의 차이를 비교해보자.

isset() 사용 사례: 키의 존재 여부 + NULL 값 체크

<?php
$user = [
    'name' => 'John',
    'email' => NULL,  // 키는 있지만 값이 NULL
    'phone' => ''     // 빈 문자열
];

// isset() 테스트
var_dump(isset($user['name']));      // true - 키 있음, 값 있음
var_dump(isset($user['email']));     // false - 키는 있지만 값이 NULL
var_dump(isset($user['phone']));     // true - 키 있음, 빈 문자열은 NULL 아님
var_dump(isset($user['address']));   // false - 키 없음
?>

✓ isset()은 NULL 값을 "없는 것"으로 취급한다. 빈 문자열이나 0은 통과하지만 NULL은 막는다.

array_key_exists() 사용 사례: 키의 존재 여부만 순수하게 확인

<?php
$user = [
    'name' => 'John',
    'email' => NULL,  // 키는 있지만 값이 NULL
    'phone' => ''     // 빈 문자열
];

// array_key_exists() 테스트
var_dump(array_key_exists('name', $user));      // true
var_dump(array_key_exists('email', $user));     // true - NULL이어도 키가 있으면 true!
var_dump(array_key_exists('phone', $user));     // true
var_dump(array_key_exists('address', $user));   // false
?>

✓ array_key_exists()는 값이 NULL이든 뭐든 키가 존재하면 true. 데이터베이스에서 NULL 값으로 조회된 경우에도 "정보가 있다"고 판단할 수 있다.

in_array() 사용 사례: 배열 내 특정 값의 존재 여부 확인

<?php
$fruits = ['apple', 'banana', 'orange', ''];

// in_array() 테스트
var_dump(in_array('apple', $fruits));       // true
var_dump(in_array('grape', $fruits));       // false
var_dump(in_array('', $fruits));            // true - 빈 값도 배열에 있으면 true
var_dump(in_array(0, $fruits));             // true (주의!) - 느슨한 비교 시 'apple'이 0과 같다고 봄
?>

✗ in_array()는 기본적으로 느슨한 비교(loose comparison)를 한다. 'apple' == 0은 false지만, 엄격한 비교 모드가 아니면 예상치 못한 결과가 나올 수 있다.

 

3단계: 상황별 올바른 함수 선택 가이드
상황 사용할 함수 이유
배열 키의 존재 여부 확인
(NULL 값 제외)
isset() 키가 있으면서 값이 "실제로 있는" 경우만 true
배열 키의 존재 여부 확인
(NULL 값 포함)
array_key_exists() NULL 포함 모든 값을 유효한 데이터로 봄
배열에 특정 값이 있는지 확인 in_array(, , true) 값 자체를 찾되, 엄격한 비교(===)로 타입까지 확인
폼 데이터 필터링
(빈 값 제외)
isset() 사용자가 입력한 값 중 "실제 내용"이 있는지 확인
데이터베이스 결과 검증
(NULL도 유효한 값)
array_key_exists() DB의 NULL 칼럼도 조회된 정보로 취급

 

4단계: 실전 예제 - 실수하기 쉬운 상황들

상황 1) 폼 데이터 검증: 사용자 입력값이 정말 있는지 확인

<?php
// 사용자가 POST로 보낸 데이터
$_POST = [
    'username' => 'john_doe',
    'bio' => '',          // 빈 문자열
    'newsletter' => NULL  // 체크 안 함
];

// ✗ 잘못된 방법: in_array로 키 존재 확인?
if (in_array('username', $_POST)) {
    // 이 코드는 $_POST 배열의 "값"에 'username'이 있는지 확인 (완전히 다른 의미)
    echo "username이 값으로 존재함"; // 이 코드는 실행 안 됨
}

// ✓ 올바른 방법: isset()으로 키와 값 모두 확인
if (isset($_POST['username']) && !empty($_POST['username'])) {
    echo "username에 실제 값이 있음";
    // 실행됨
}

// ✓ 또는 array_key_exists()로 키만 확인하고 따로 값 검증
if (array_key_exists('bio', $_POST)) {
    echo "bio 필드가 전송됨";  // 빈 문자열이어도 실행됨
    if (!empty($_POST['bio'])) {
        echo "bio에 내용이 있음";  // 이건 실행 안 됨
    }
}
?>

✓ 폼 데이터 검증할 땐 isset()을 쓰되, 필드의 존재 여부만 확인하고 싶으면 array_key_exists()를 따로 쓴다.

상황 2) 데이터베이스에서 조회한 결과 처리

<?php
// DB에서 조회한 사용자 정보
$user = [
    'id' => 1,
    'name' => 'John',
    'middle_name' => NULL,  // DB에 NULL로 저장됨
    'phone' => ''            // DB에 빈 문자열로 저장됨
];

// ✗ 잘못된 방법: isset()으로 middle_name 확인
if (isset($user['middle_name'])) {
    echo "중간 이름: " . $user['middle_name']; // 실행 안 됨 (NULL이므로)
} else {
    echo "중간 이름이 없음";  // 실행됨 (하지만 DB에는 칼럼이 있음)
}

// ✓ 올바른 방법: array_key_exists()로 칼럼 존재 여부 확인
if (array_key_exists('middle_name', $user)) {
    echo "DB에 middle_name 칼럼이 있음"; // 실행됨
    if ($user['middle_name'] !== NULL) {
        echo "중간 이름: " . $user['middle_name'];
    } else {
        echo "중간 이름이 없음 (NULL)";
    }
}
?>

✓ DB 결과는 array_key_exists()로 칼럼의 존재를 확인하고, 값이 NULL인지는 별도로 체크한다.

상황 3) 배열에서 특정 값 찾기

<?php
$allowed_roles = ['admin', 'moderator', 'user', '0'];
$user_role = 'admin';
$suspicious_role = 0;  // 정수 0

// ✗ 잘못된 방법: 느슨한 비교
if (in_array($suspicious_role, $allowed_roles)) {
    echo "허용된 역할"; // 실행됨! 왜냐하면 0 == '0' 이기 때문
}

// ✓ 올바른 방법: 엄격한 비교(세 번째 인자 true)
if (in_array($user_role, $allowed_roles, true)) {
    echo "허용된 역할 (admin 맞음)";  // 실행됨
}

if (in_array($suspicious_role, $allowed_roles, true)) {
    echo "허용된 역할";  // 실행 안 됨 (0과 '0'은 타입이 다름)
}
?>

✓ in_array()를 쓸 땐 항상 세 번째 인자로 true를 넘겨서 엄격한 비교(===)를 하자.

 

5단계: 흔한 실수와 성능 차이

실수 1) isset()과 array_key_exists() 혼동

<?php
$config = ['debug' => false, 'timeout' => NULL];

// ✗ false 값을 "없는 것"으로 착각
if (isset($config['debug'])) {
    echo "디버그 설정이 있음"; // false도 값이므로 실행됨
}

// ✗ NULL을 "있는 것"으로 착각
if (isset($config['timeout'])) {
    echo "타임아웃 설정이 있음"; // 실행 안 됨 (NULL이므로)
}

// ✓ 정확한 비교
var_dump(isset($config['debug']));     // true (false 값도 "있다")
var_dump(isset($config['timeout']));   // false (NULL은 "없다")
?>

✓ isset()은 false, 0, '', '0' 같은 "falsy" 값도 true로 반환한다. NULL만 false다.

실수 2) 느슨한 in_array() 비교의 위험성

<?php
// 보안 검증에서 느슨한 비교 사용
$user_id = '1';  // 문자열
$allowed_ids = [1, 2, 3];  // 정수

// ✗ 위험: 느슨한 비교 (in_array default)
if (in_array($user_id, $allowed_ids)) {
    echo "허용된 사용자"; // '1' == 1이므로 실행됨 (우연히 맞음)
}

// 더 위험한 경우
$magic_value = 0;  // 특별한 의미의 값
if (in_array($magic_value, ['apple', 'banana'])) {
    echo "과일임"; // 실행됨! 0 == 'apple'이 true로 평가
}

// ✓ 안전: 엄격한 비교
if (in_array($user_id, $allowed_ids, true)) {
    echo "허용된 사용자"; // '1'과 1은 타입이 다르므로 false
}
?>

✓ 보안 관련 코드에선 반드시 in_array(, , true)를 쓴다. 타입까지 맞아야 true다.

 

6단계: 마무리 및 실전 팁

배열 요소 확인은 PHP에서 가장 기본적이면서도 가장 자주 실수하는 작업이다. 세 함수를 정확히 구분하는 것만으로도 버그의 상당 부분을 예방할 수 있다.

빠른 참고표:

  • isset() → 키가 있고 NULL이 아닌가? (사용자 입력 검증)
  • array_key_exists() → 키가 있는가? (DB 결과 검증, NULL 포함)
  • in_array(, , true) → 배열에 이 값이 있는가? (역할/권한 검증 등)

이 글의 상황별 가이드를 참고해 상황에 맞는 함수를 선택하면, 데이터 검증에서 발생하는 대부분의 논리 버그를 막을 수 있을 것이다. 특히 in_array()를 쓸 땐 무조건 세 번째 인자로 true를 넘기는 습관만 들여도, 타입 강제 형변환으로 인한 보안 문제를 상당히 줄일 수 있다.