PHP에서 try-catch 구문을 통한 예외처리와 확장방법

try-catch문은 프로그램 실행 중 예외가 발생했을 때, 개발자가 이를 직접 처리할 수 있도록 제공하는 구문입니다. C++이나 Java 등의 언어에 등장한지는 제법 시간이 흘렀지만, PHP에서는 5.1.0 버전에 들어와서야 사용할 수 있게 되었습니다.

예외(Exception)특별한 처리가 필요한 이례적인 상황을 이야기합니다. PHP를 기준으로 하면, $_POST 파라메터로 받은 어떤 값이 나눗셈에서 분모로 들어가는 소스코드를 예로 들 수 있겠습니다. 이러한 코드에서 $_POST 값이 0인 상황이 벌어지면, 해당 값을 별도의 값으로 치환하거나 이전 웹페이지로 되돌려보내 사용자의 새로운 입력을 요구한다던가 하는 특별한 처리가 필요할 것입니다.

기본적으로 예외처리는 if-else문 만으로도 가능합니다. 그러나 예외가 발생했을 때의 처리방법에 일관성이 있을 때는 try-catch문을 사용하여 무척 간결한 코드를 작성할 수 있습니다. 금번 포스팅에서는 간단한 예제를 통해서 try-catch문의 기본적인 성질과 사용법, 확장방법에 대하여 설명합니다.

 

PHP try-catch문의 기본형태

$total = intval($_POST['total']);
$people = intval($_POST['people']);

try
{
	if($total < 1000)
	{
		throw new Exception('금액을 1000원 이상으로 입력해주세요.', 1);
 	}

	if($people <= 0)
	{
		throw new Exception('사람 수를 1명 이상으로 입력해주세요.', 2);
 	}

	if($total % $people != 0)
	{
		throw new Exception('나누어 떨어지지 않습니다.', 3);
 	}

 	$s = floor($total / $people) . '원입니다!';
}
catch(Exception $e)
{
	$s = $e->getMessage() . ' (오류코드:' . $e->getCode() . ')';
}

echo $s;

직장인들의 점심시간에 유용한 간단한 더치페이 계산기 소스코드를 보며 try-catch문의 기본형태를 알아보겠습니다. 이 프로그램은 나눠내야 할 밥값(때때로는 커피값)과 사람 수를 입력하면, 1명당 얼마씩을 걷어야 하는지를 알려줍니다. $_POST['people']에는 몇 명인지가, $_POST['total']에는 나눠서 내야하는 금액이 얼마인지를 받게 됩니다.

이 소스코드에는 3가지의 예외처리를 해야하는 상황이 있습니다. 하나는 금액을 1,000원 미만으로 입력한 경우, 또 하나는 사람수를 0명 이하로 입력한 경우, 마지막 하나는 금액이 사람 수로 나누어 떨어지지 않는 경우입니다. 이 프로그램은 세 가지 예외를 만난 경우, 각 예외에 맞는 문구를 출력합니다. 물론 어떤 예외도 만나지 않은 경우라면, 정상적으로 더치페이할 금액을 계산하여 출력해야 할 것입니다.

위 소스코드에서 확인할 수 있는 try-catch 구문의 기본적인 작성법은 아래와 같습니다.

  1. 예외처리가 발생할 수 있는 소스코드를 try 블록으로 묶습니다.
  2. try 블록 안에서 예외가 발생하는 부분에 Exception이라고 하는 객체를 생성하고, 이 객체를 throw문을 사용하여 던집니다.
  3. throw문을 만났을 때 실행할 코드를 catch 블록에 작성합니다. catch 블록에서는 앞서 throw문에서 던진 Exception 객체를 받아다가 사용할 수 있습니다.

이렇게 작성된 코드는 다음과 같이 실행됩니다.

  1. try 블록을 만나면, PHP는 일단 try 블록에 있는 코드들을 한 라인씩 실행합니다.
  2. 이 과정 중에 throw문을 만나게 되면, try 블록에 있는 throw문 이하의 코드는 모두 건너뛰고, 바로 catch 블록으로 진입하게 됩니다.
  3. 반대로 try 블록을 끝까지 실행했음에도 throw문을 만나지 않았다면, catch 블록은 실행하지 않고 뛰어넘게 됩니다.

 

사용자 정의 예외: Exception 객체를 상속받아 확장된 예외처리 만들기

앞서의 예제에서, 예외가 발생하는 부분에서 생성하는 Exception은 객체입니다. 이 객체는 오류 메세지와 오류코드를 비롯해 최소한의 멤버변수와 멤버함수 만을 가지고 있는데, 그 구조는 PHP Manual에 수록되어 있습니다. 대부분의 상황에서는 Exception 객체 만으로도 훌륭하게 활용할 수 있지만, 세상사에 부족함이 느껴질 때는 항상 있는 법입니다. 이 때는 Exception 객체를 상속받아 확장된 예외처리를 만들 수 있습니다.

앞서의 더치페이 프로그램에는 한 가지 아쉬운 점이 있습니다. 바로 주어진 금액이 사람 수로 나누어 떨어지지 않을 때인데요. 일단 적당히 100원 단위 정도까지만 사람 수로 나눠준 다음, 남은 금액을 출력해준다면 어떨까요. 이를테면 17000원을 3명이 나눠 내야할 때에는, 한 사람당 5600원씩 내고 200원이 남는다고 출력해주는 것입니다. 그럼 남은 금액은 가위바위보를 하든 사다리를 타든 누군가에게 몰아줄 수 있을테니까요.

이 아쉬움을 극복하기 위해서, Exeption 객체를 상속받아 아래와 같이 calcRemainException라는 확장된 예외처리 객체를 만들어 보았습니다.

$total = intval($_POST['total']);
$people = intval($_POST['people']);

class calcRemainException extends Exception
{
	protected $res;		// 100원 단위로 나눈 금액
	protected $remain;	// 나누고 남은 금액
	
 	public function __construct($total, $people, $code = 0)
 	{ 
 		$this->res = floor($total / $people / 100) * 100;
 		$this->remain = $total - $this->res * $people;
 
 		$msg = $this->res . '원씩 나눈 후, ';
 		$msg .= $this->remain . '원이 남습니다.';
 
 		parent::__construct($msg, $code);
	}
}
 
try
{
 	if($total < 1000)
 	{
 	 	throw new Exception('금액을 1000원 이상으로 입력해주세요.', 1);
 	}

 	if($people <= 0)
 	{
 	 	throw new Exception('사람 수를 1명 이상으로 입력해주세요.', 2);
 	}

 	if($total % $people != 0)
 	{ 
  		throw new calcRemainException($total, $people, 3);
 	}

 	$s = floor($total / $people) . '원입니다!';
}
catch(Exception $e)
{
	$s = $e->getMessage() . ' (오류코드:' . $e->getCode() . ')';
}
catch(calcRemainException $e)
{
 	$s = $e->getMessage();
}

echo $s;

위 예제코드에는 두 가지의 유심히 살펴보아야 할 부분이 있습니다.

첫 번째는 바로 calcRemainException 객체를 정의하는 부분인데, extends 키워드를 사용하여 Exception 객체를 상속 받았음을 볼 수 있습니다. 저는 간단하게 생성자 함수에서 직접 나눈 결과금액과 나머지 금액을 인자로 받도록 수정하였습니다. 메세지 내용 등을 인자로 받고 있었던 Exception 객체의 생성자 함수를 기초부터 새로 작성한 셈입니다.

두 번째는 다름 아닌 catch 부분입니다. 흥미롭게도 위 예제코드에서는 1개의 try 문에 catch 문이 2개가 붙어 있습니다. 자세한 설명을 굳이 곁들이지 않더라도, 앞에 나온 catch 문은 Exception 객체가 throw 되었을 때 처리하는 부분을, 이어서 나온 catch 문은 제가 새로 정의한 calcRemainException 객체가 throw 되었을 때 처리하는 부분을 담고 있음을 쉽게 알 수 있습니다.

이처럼 Exception 객체를 상속받아 확장된 예외처리 객체를 정의한 경우, try-catch 구문에서는 기존의 Exception 객체를 혼용해서 사용할 수 있습니다. 같은 이치로 2개 이상의 확장된 예외처리 객체를 정의한 경우에는, 이들을 모두 동시에 섞어서 사용할 수 있습니다.

이것이 예외처리가 복잡해질수록 try-catch 문이 if-else 문보다 효과적인 결정적인 이유입니다. 예외처리 방식이 늘어나도 try-catch 문은 단순히 상속받은 객체와 catch문을 하나씩 늘려주면 해결되는 것입니다. 이러한 성질을 잘 활용하면 코드 자체의 간결함은 물론 수월한 유지보수 또한 가능할 것입니다.

2 thoughts on “PHP에서 try-catch 구문을 통한 예외처리와 확장방법

  1. php에도 try-catch 구문이있었군요. php5이상 부터 지원하네요.
    좋은 자료감사합니다. 재밌게 읽었습니다!!

    • 영준님, 덧글 감사드립니다.

      PHP의 try-catch 문은 사실 PHP의 본질에서는 벗어난 문법입니다. PHP의 태생이 절차적 언어임에도 불구하고, (Java 등의 언어와 동일하게) Exception 객체를 상속해서 catch 문을 확장해가는 객체지향적 방식을 선택했기 때문입니다. 저와 함께 일하시는 분들 중에는 PHP가 주력언어이자 생애 첫 언어인 분들이 많은데, try-catch 문의 특성 때문에 이러한 부분을 활용하기 못하는 모습을 자주 보았습니다. 이 글을 쓴 이유 또한 제가 보아온 그 모습들 가운데 있습니다.

      5.5 버전부터는 finally 블록도 사용할 수 있게 되었습니다. 5.5 버전은 상당히 최근 버전이라, 레거시 버전을 이용하는 분들이 실수할까봐 일부러 제 포스팅에서는 설명을 하지 않았습니다. 관심이 있으시다면 php.net에서 한 번 살펴보시면 좋겠네요!

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.

*

다음의 HTML 태그와 속성을 사용할 수 있습니다: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>