개발자들이 수식 계산을 다루다 보면 언젠가는 마주치는 에러가 있다. 바로 0으로 나누려고 할 때 발생하는 Warning 메시지다. 대수학에서 0으로 나누는 것은 수학적으로 정의되지 않지만, 프로그래밍에서는 어떻게 처리하느냐에 따라 결과가 완전히 달라진다. 이번에는 이 오류가 왜 발생하는지, 어떻게 사전에 방지할 수 있는지, 그리고 실무에서 자주 사용하는 안전한 계산 패턴을 정리해서 소개하겠다.
PHP에서 0으로 나누기를 시도하면 Warning이 발생한다. PHP 8 이상에서는 좀 더 엄격해져서 DivisionByZeroError 예외가 발생하기도 한다. 문제는 이 오류가 조용하게 실행되거나 예측 불가능한 값을 반환하는 경우가 있다는 점이다. 예를 들어 사용자 입력, 데이터베이스 값, API 응답 같은 외부 데이터를 분모로 사용할 때 0이 들어오면 코드 전체가 무너질 수 있다.
<?php
$total = 100;
$count = 0; // API에서 받아온 데이터
$average = $total / $count; // Warning: Division by zero
echo $average; // 아무것도 출력되지 않거나 INF (무한대) 출력
?>
이 코드는 실행되지만 Warning이 발생하고, $average에는 INF(무한대) 또는 NAN(숫자 아님)이 저장된다. 이후 이 값을 다른 계산에 사용하면 예상 밖의 결과가 나온다.
<?php
$total = 100;
$count = 0; // API에서 받아온 데이터
if ($count !== 0) {
$average = $total / $count;
} else {
$average = 0; // 기본값 설정
}
echo $average; // 0 출력
?>
간단하지만 확실한 방법이다. 분모가 0인지 미리 확인하고, 0이면 기본값이나 예외 처리를 한다.
<?php
$total = 100;
$count = 0;
$average = $count !== 0 ? $total / $count : 0;
echo $average; // 0
?>
가장 간단한 방법이다. 한 줄로 0 체크와 계산을 동시에 처리한다. 기본값이 항상 0일 때 유용하다.
<?php
function calculateAverage($total, $count) {
return ($count ?? 0) !== 0 ? $total / $count : 0;
}
echo calculateAverage(100, 0); // 0
echo calculateAverage(100, null); // 0
echo calculateAverage(100, 5); // 20
?>
API 응답이나 함수 인자에 null이 올 수 있을 때 유용하다. ?? 연산자로 null을 0으로 변환한 후에 나누기를 진행한다.
<?php
function safeDivide($dividend, $divisor, $default = 0) {
if (is_numeric($dividend) && is_numeric($divisor) && $divisor != 0) {
return $dividend / $divisor;
}
return $default;
}
echo safeDivide(100, 5); // 20
echo safeDivide(100, 0); // 0
echo safeDivide(100, 'abc'); // 0
echo safeDivide(100, 0, null); // null
?>
프로젝트 전체에서 반복적으로 사용할 함수라면 이렇게 Helper 함수로 만드는 것이 낫다. 타입 체크도 함께 하므로 문자열 같은 잘못된 값도 방어할 수 있다.
<?php
try {
$total = 100;
$count = 0;
// PHP 8에서는 DivisionByZeroError 발생
$average = intdiv($total, $count);
echo $average;
} catch (DivisionByZeroError $e) {
echo "0으로 나눌 수 없습니다: " . $e->getMessage();
} catch (Throwable $e) {
echo "예상 밖의 오류: " . $e->getMessage();
}
?>
PHP 8 이상에서 intdiv() 함수를 쓰면 명시적으로 DivisionByZeroError를 던진다. 예외를 잡아서 로깅하거나 사용자 친화적인 메시지를 보여줄 수 있다. 일반 나누기(/)는 여전히 Warning을 내보내지만, intdiv()는 더 엄격하다.
| 상황 | 권장 방법 | 예제 |
|---|---|---|
| 평균값 계산 | 기본값 0 또는 null 반환 | $avg = $count !== 0 ? $sum / $count : 0; |
| 백분율 계산 | 기본값 0 반환 | $percent = $total !== 0 ? ($part / $total) * 100 : 0; |
| API 요청 후 계산 | Helper 함수 + null 체크 | $result = safeDivide($api_data['value1'], $api_data['value2'], null); |
| 사용자 입력값 계산 | 필터링 후 try-catch 조합 | if ($userInput === '0') throw new Exception('0으로 나눌 수 없습니다'); |
✗ if ($count == 0) 대신 ✓ if ($count !== 0)을 사용하자. == 연산자는 타입 강제 변환을 일으킬 수 있다. 예를 들어 $count = '0'이면 == 0은 true지만 데이터베이스에서 받은 문자열 '0'이 뜻하는 의미는 완전히 다를 수 있다. !== 연산자는 값과 타입을 모두 비교하므로 더 안전하다.
✗ 직접 나누기 대신 ✓ is_numeric()으로 먼저 검증하자. API 응답이나 사용자 입력이 항상 숫자라고 보장할 수 없다. 문자열 'abc' 같은 값이 오면 0으로 강제 변환되어 0으로 나누기 오류가 발생한다.
<?php
// ✗ 잘못된 코드
$value = 'abc';
$result = 100 / $value; // PHP Notice: A non well formed numeric value encountered
// ✓ 올바른 코드
if (is_numeric($value) && $value != 0) {
$result = 100 / $value;
} else {
$result = 0;
}
?>
✗ if ($divisor == 0) 대신 ✓ if (abs($divisor) < 0.0001)을 사용하자. 부동소수점 계산에서는 0이 아닌 매우 작은 값이 나올 수 있다. 예를 들어 0.1 + 0.2가 0.3이 아닌 0.30000000000000004로 나오는 경우가 있다. 이런 경우를 대비해 절댓값으로 비교하는 것이 안전하다.
<?php
function safeDivideFloat($dividend, $divisor, $epsilon = 0.0001) {
if (abs($divisor) < $epsilon) {
return 0;
}
return $dividend / $divisor;
}
echo safeDivideFloat(1, 0.0000001); // 0
echo safeDivideFloat(1, 0.1); // 10
?>
| PHP 버전 | 일반 나누기 (/) | 정수 나누기 (intdiv) |
|---|---|---|
| PHP 7 이하 | Warning 발생 후 INF 또는 NAN | Warning 발생 후 INF 또는 NAN |
| PHP 8+ | Warning 발생 (엄격함) | DivisionByZeroError 예외 발생 |
PHP 버전이 올라갈수록 더 엄격하게 처리하므로, 이전 코드도 점진적으로 개선해야 한다.
0으로 나누기 오류는 매우 흔하지만 사전 방지가 가능한 오류다. 핵심은 분모가 0일 가능성을 항상 고려하고, 계산 전에 반드시 체크하는 습관을 들이는 것이다. 단순해 보이는 이 작은 방어가 모여서 안정적이고 버그 없는 코드를 만든다. 위 글의 Helper 함수 패턴 3을 참고해 프로젝트 전역 함수로 등록해두면, 앞으로 모든 나누기 계산을 안전하게 처리할 수 있을 것이다.