커스텀 예외 계층 구조
Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 91개 중 67번째.
PHP의 내장 예외 클래스는 일반적인 시나리오를 다루지만, 실제 애플리케이션에서는 더 구체적인 오류 유형이 필요한 경우가 많습니다. 사용자 정의 예외 계층 구조를 만들면 도메인에 맞는 예외를 정의하고 다양한 수준의 구체성에서 이를 포착할 수 있습니다.
사용자 정의 예외를 만들려면 간단히 Exception 클래스 또는 그 하위 클래스 중 하나를 확장하면 됩니다:
<?php
class PaymentException extends Exception {}
class InsufficientFundsException extends PaymentException {
public function __construct(
private float $required,
private float $available
) {
parent::__construct("Need $required but only $available available");
}
public function getShortfall(): float {
return $this->required - $this->available;
}
}
InsufficientFundsException이 Exception을 직접 상속하는 것이 아니라 PaymentException을 상속하는 방식에 주목하세요. 이를 통해 하나의 catch 블록으로 모든 결제 관련 예외를 포착하거나, 특정 예외를 개별적으로 처리할 수 있는 계층 구조가 생성됩니다.
사용자 정의 예외에는 오류와 관련된 추가 속성 및 메서드를 포함할 수 있습니다. 여기서 getShortfall()은 일반적인 예외가 제공할 수 없는 유용한 정보를 제공합니다:
<?php
class Wallet {
public function __construct(private float $balance) {}
public function pay(float $amount): void {
if ($amount > $this->balance) {
throw new InsufficientFundsException($amount, $this->balance);
}
$this->balance -= $amount;
}
}
잘 설계된 예외 계층 구조는 코드의 유지보수성을 높여줍니다. 예외를 처리할 때 필요에 따라 포괄적으로 또는 구체적으로 지정할 수 있으며, 예외 객체 자체에는 무엇이 잘못되었는지에 대한 의미 있는 컨텍스트가 포함되어 있습니다.
챌린지
쉬움다양한 유형의 주문 실패를 처리하는 사용자 정의 예외 계층 구조를 갖춘 주문 처리 시스템을 구축해 보겠습니다. 무엇이 잘못되었는지에 대한 의미 있는 컨텍스트를 전달하는 예외를 생성하여 에러 처리를 보다 정밀하고 유익하게 만들 것입니다.
코드는 네 개의 파일로 구성됩니다:
OrderException.php—Exception을 상속받는 기본OrderException클래스를 생성합니다. 이는 모든 주문 관련 예외의 부모 역할을 하며, 필요한 경우 단일 catch 블록으로 모든 주문 문제를 포착할 수 있게 해줍니다.OutOfStockException.php—OrderException을 상속받는OutOfStockException클래스를 생성합니다. 이 예외는 생성자에서 제품 이름과 요청된 수량을 인자로 받아야 합니다. 이러한 private 속성들에 대해 생성자 프로모션(constructor promotion)을 사용하세요. 부모 생성자를"[productName] is out of stock (requested: [quantity])"형식의 메시지와 함께 호출하십시오. 제품 이름을 반환하는getProductName(): string메서드를 추가하세요.InvalidQuantityException.php—OrderException을 상속받는InvalidQuantityException클래스를 생성합니다. 이 예외는 생성자 프로모션을 사용하여 수량 값을 private 속성으로 받아야 합니다. 부모 생성자를"Invalid quantity: [quantity]"메시지와 함께 호출하십시오. 수량 값을 반환하는getQuantity(): int메서드를 추가하세요.main.php— 세 개의 예외 파일을 모두 포함합니다. 제품 이름과 수량(정수로 변환해야 하는 문자열)이라는 두 개의 입력을 받게 됩니다.수량 값에 따라 적절한 예외를 던지십시오:
- 수량이 1보다 작으면
InvalidQuantityException을 던집니다. - 수량이 100보다 크면
OutOfStockException을 던집니다 (재고 부족을 시뮬레이션함). - 그렇지 않으면
"Order placed: [quantity]x [productName]"를 출력합니다.
계층 구조를 사용하여 예외를 catch 하십시오. 먼저
InvalidQuantityException을 catch 하고 예외의 메시지와getQuantity()메서드를 사용하여"Quantity error: [message] (value: [quantity])"를 출력합니다. 그다음OutOfStockException을 catch 하고 예외의 메시지와getProductName()메서드를 사용하여"Stock error: [message] (product: [productName])"를 출력합니다.- 수량이 1보다 작으면
이 계층 구조를 통해 특정 예외를 개별적으로 처리하는 동시에, 기본 OrderException 클래스를 통해 모든 주문 관련 문제를 catch 할 수 있는 옵션을 가질 수 있습니다. 각 예외의 사용자 정의 메서드는 일반적인 예외가 제공할 수 없는 컨텍스트를 제공합니다.
치트 시트
Exception 클래스나 그 하위 클래스를 확장하여 사용자 정의 예외를 생성합니다:
<?php
class PaymentException extends Exception {}
class InsufficientFundsException extends PaymentException {
public function __construct(
private float $required,
private float $available
) {
parent::__construct("Need $required but only $available available");
}
public function getShortfall(): float {
return $this->required - $this->available;
}
}
사용자 정의 예외는 다른 사용자 정의 예외를 확장함으로써 계층 구조(hierarchy)를 형성할 수 있습니다. 이를 통해 다양한 수준의 구체성으로 예외를 포착할 수 있습니다. 예를 들어, PaymentException으로 모든 결제 관련 예외를 포착하거나, InsufficientFundsException과 같은 특정 예외를 개별적으로 처리할 수 있습니다.
사용자 정의 예외에는 오류에 대한 컨텍스트를 제공하기 위해 추가적인 프로퍼티와 메서드를 포함할 수 있습니다:
<?php
class Wallet {
public function __construct(private float $balance) {}
public function pay(float $amount): void {
if ($amount > $this->balance) {
throw new InsufficientFundsException($amount, $this->balance);
}
$this->balance -= $amount;
}
}
생성자 홍보(constructor promotion)를 사용하여 생성자 매개변수에서 직접 프로퍼티를 정의하세요. 예외 메시지를 설정하려면 parent::__construct()를 호출합니다.
직접 해보기
<?php
// 모든 예외 파일을 포함합니다
require_once 'OrderException.php';
require_once 'OutOfStockException.php';
require_once 'InvalidQuantityException.php';
// 입력 읽기
$productName = trim(fgets(STDIN));
$quantity = intval(trim(fgets(STDIN)));
// TODO: try-catch를 사용하여 주문 처리를 처리하세요
// try 블록 내부:
// - 만약 quantity < 1 이면, InvalidQuantityException을 던집니다
// - 만약 quantity > 100 이면, OutOfStockException을 던집니다
// - 그렇지 않으면, "Order placed: [quantity]x [productName]"을 출력합니다
//
// 먼저 InvalidQuantityException을 캐치합니다:
// - getMessage()와 getQuantity()를 사용하여 "Quantity error: [message] (value: [quantity])"를 출력합니다
//
// OutOfStockException을 캐치합니다:
// - getMessage()와 getProductName()을 사용하여 "Stock error: [message] (product: [productName])"를 출력합니다
?>이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8매직 메서드
매직 메서드 소개__toString 및 __debugInfo__get, __set, __isset, __unset__call 및 __callStatic__clone 및 객체 복제__serialize 및 __unserialize요약 - 커스텀 컬렉션11타입 시스템과 에러 핸들링
타입 선언Nullable 타입유니온 및 인터섹션 타입예외 클래스커스텀 예외 계층 구조Try, Catch, Finally요약 - Form Validator