PHP 개발자들이 자주 겪는 문제 중 하나가 바로 형 변환이다. 사용자 입력값이나 API 응답값을 정수/실수로 변환할 때, 많은 개발자들이 단순히 괄호로 감싸거나(int) $value 형태로 변환한다. 다만 이런 방식이 정말 안전한지, 예상치 못한 값이 들어올 때 어떻게 처리되는지 정확히 아는 개발자는 드물다. 이번에는 intval()과 floatval()의 정확한 동작 원리, 왜 타입 캐스팅보다 안전한지, 그리고 실전 예제를 통해 완벽하게 정리해보겠다.

 

1단계: PHP 형 변환의 기초 개념 이해하기
PHP는 동적 타입 언어라 자동으로 형 변환(Type Juggling)을 수행한다. 하지만 이 자동 변환이 항상 예상대로 작동하지 않는다. 예를 들어 문자열 "123abc"를 정수로 변환하면 어떻게 될까?

타입 캐스팅 vs intval() 함수의 차이:

입력값 (int) 캐스팅 intval() 함수 차이
"123" 123 123 같음
"123abc" 123 123 같음
"abc123" 0 0 같음
"12.34" 12 12 같음
true 1 1 같음
null 0 0 같음

표만 보면 둘이 거의 같아 보인다. 하지만 intval()은 base 파라미터를 지원해서 진법 변환도 가능하고, 명시적으로 형 변환 의도를 드러낼 수 있다는 점에서 코드 가독성이 좋다.

 

2단계: intval()과 floatval() 함수 상세히 알기
intval() 함수의 구조
intval()은 변수를 정수로 변환한다. 선택적으로 base(진법)를 지정할 수 있다.
intval(mixed $value, int $base = 10): int

파라미터 설명:

  • $value: 변환할 값 (문자열, 숫자, 불린 등 어떤 타입이든 가능)
  • $base: 진법 (기본값 10진법. 2~36진법 지원)

반환값: 항상 정수(int) 타입. 실패하면 0 반환

floatval() 함수의 구조
floatval()은 변수를 실수로 변환한다. base는 지원하지 않는다.
floatval(mixed $value): float

반환값: 항상 실수(float) 타입. 실패하면 0.0 반환

 

3단계: 실전 예제로 안전한 형 변환 구현하기
예제 1: 사용자 입력값 검증 및 변환

잘못된 코드 - 형 캐스팅만 사용

<?php
// 사용자가 입력한 나이
$input_age = $_POST['age'] ?? '';
$age = (int)$input_age; // 형 캐스팅만 수행

// 문제: 입력값이 "30세" 또는 "30 years"라면?
echo "나이: " . $age; // "나이: 30" (의도치 않게 30으로 변환됨)

// 더 위험한 경우
$suspicious_input = "0x1F"; // 16진수?
$value = (int)$suspicious_input;
echo $value; // 0 (의도치 않게 0으로 처리됨)
?>

올바른 코드 - intval()과 유효성 검사 함께 사용

<?php
// 방법 1: 순수 정수 문자열 검증
$input_age = $_POST['age'] ?? '';

if (!ctype_digit($input_age)) {
    die("나이는 숫자만 입력 가능합니다");
}

$age = intval($input_age);
echo "나이: " . $age; // 안전하게 정수만 처리

// 방법 2: 범위 검증까지 추가
$age = intval($input_age);
if ($age < 0 || $age > 150) {
    die("유효하지 않은 나이 범위입니다");
}

echo "나이: " . $age;
?>

결과/설명: ctype_digit()으로 숫자만 입력되었는지 미리 확인한 후 intval()로 변환하면, 예상치 못한 문자열이 섞여 있어도 안전하게 처리할 수 있다.

예제 2: API 응답값에서 숫자 추출 및 변환

잘못된 코드 - 캐스팅만 하면 위험

<?php
// 외부 API에서 받은 JSON 응답
$json = '{"price": "19.99", "quantity": "5 units"}';
$data = json_decode($json, true);

// 단순 캐스팅
$price = (int)$data['price'];
$quantity = (int)$data['quantity'];

echo "가격: " . $price; // "가격: 19" (소수점이 버려짐)
echo "수량: " . $quantity; // "수량: 5" (뒤의 " units" 무시)

// 계산 오류 발생 가능
$total = $price * $quantity; // 95 (정확한 가격은 99.95여야 함)
?>

올바른 코드 - floatval()과 intval() 구분해서 사용

<?php
$json = '{"price": "19.99", "quantity": "5 units"}';
$data = json_decode($json, true);

// 가격은 floatval()로 실수 처리
$price = floatval($data['price']);
// 수량은 intval()로 정수 처리
$quantity = intval($data['quantity']);

echo "가격: " . $price; // "가격: 19.99"
echo "수량: " . $quantity; // "수량: 5"

// 정확한 계산
$total = $price * $quantity; // 99.95
echo "합계: " . $total; // "합계: 99.95"
?>

결과/설명: 가격처럼 소수점이 중요한 값은 floatval()으로, 수량처럼 정수만 필요한 값은 intval()로 구분해서 처리하면 계산 오류를 방지할 수 있다.

예제 3: intval()의 base 파라미터 활용 (진법 변환)

잘못된 코드 - 16진수를 그냥 정수로 변환

<?php
$hex_color = "FF"; // 16진수 색상 값
$decimal_value = intval($hex_color); // base 지정 없음

echo $decimal_value; // 0 ("FF"는 10진법으로 숫자가 아니므로 0)
?>

올바른 코드 - base 파라미터로 16진수 지정

<?php
$hex_color = "FF"; // 16진수 색상 값
$decimal_value = intval($hex_color, 16); // base=16 (16진법)

echo $decimal_value; // 255 (FF를 10진수로 변환)

// 다양한 진법 예제
echo intval("1010", 2); // 10 (2진수)
echo intval("77", 8); // 63 (8진수)
echo intval("ZZ", 36); // 1295 (36진수, 0-9와 a-z 사용)
?>

 

4단계: 주의사항 및 흔한 실수
실수 1: floatval()과 intval()의 혼동

잘못된 것

<?php
$money = "99.99";
$quantity = 5;

// 실수: 둘 다 intval()로 변환
$money_int = intval($money); // 99 (소수점 버려짐)
$total = $money_int * $quantity; // 495 (정확한 값은 499.95)
?>

올바른 것

<?php
$money = "99.99";
$quantity = 5;

// 금액은 floatval(), 수량은 intval()
$money_float = floatval($money); // 99.99
$total = $money_float * $quantity; // 499.95
?>
실수 2: 변환 실패 시 반환값 미확인

잘못된 것

<?php
$value = "not_a_number";
$result = intval($value);

// 0이 반환되지만, 의도적으로 0을 입력한 것과 구분 불가
if ($result === 0) {
    // 실제 0인지, 변환 실패인지 알 수 없음
}
?>

올바른 것

<?php
$value = "not_a_number";

// is_numeric()으로 변환 가능 여부를 먼저 확인
if (is_numeric($value)) {
    $result = intval($value);
} else {
    die("숫자 형식이 아닙니다");
}
?>
실수 3: base 파라미터 범위 무시

잘못된 것

<?php
// base는 2~36 사이만 가능
$result = intval("10", 50); // Warning: intval() expects base to be between 2 and 36
?>

올바른 것

<?php
// 진법 유효성 검사
$base = 16; // 사용자 입력

if ($base < 2 || $base > 36) {
    die("base는 2~36 사이여야 합니다");
}

$result = intval("FF", $base); // 안전하게 처리
?>

 

5단계: 실무에서 안전한 형 변환 패턴
통합 검증 함수 만들기

프로젝트에서 반복해서 사용할 수 있는 안전한 형 변환 함수:

<?php
/**
 * 안전한 정수 변환 함수
 */
function safe_intval($value, $base = 10, $min = null, $max = null) {
    // base 유효성 검사
    if ($base < 2 || $base > 36) {
        throw new InvalidArgumentException("base는 2~36 사이여야 합니다");
    }
    
    // 숫자 여부 확인
    if (!is_numeric($value)) {
        return 0; // 또는 null, false 반환 가능
    }
    
    $int_value = intval($value, $base);
    
    // 범위 검증
    if ($min !== null && $int_value < $min) {
        return $min;
    }
    if ($max !== null && $int_value > $max) {
        return $max;
    }
    
    return $int_value;
}

/**
 * 안전한 실수 변환 함수
 */
function safe_floatval($value, $min = null, $max = null, $decimals = 2) {
    if (!is_numeric($value)) {
        return 0.0;
    }
    
    $float_value = floatval($value);
    
    // 범위 검증
    if ($min !== null && $float_value < $min) {
        return $min;
    }
    if ($max !== null && $float_value > $max) {
        return $max;
    }
    
    // 소수점 자리 반올림
    return round($float_value, $decimals);
}

// 사용 예제
$age = safe_intval($_POST['age'] ?? '', 10, 0, 150);
$price = safe_floatval($_POST['price'] ?? '', 0, 1000000, 2);
?>

 

마무리: intval()과 floatval()로 안전한 개발하기

intval()과 floatval()은 단순한 형 변환 함수처럼 보이지만, PHP의 동적 타입 시스템 속에서 명시적이고 안전한 데이터 처리를 위한 필수 도구다. 대부분의 개발자들은 (int)나 (float) 캐스팅에 익숙해서 이 함수들의 가치를 간과하지만, base 파라미터 지원, 명확한 의도 표현, 그리고 is_numeric()과의 조합을 통한 사전 검증으로 훨씬 견고한 코드를 작성할 수 있다. 사용자 입력값, API 응답값, 데이터베이스 조회 결과 등 외부에서 들어오는 모든 값은 intval()/floatval() + 사전 검증을 습관화하면, 예상치 못한 타입 오류로 인한 버그를 크게 줄일 수 있을 것이다.