본문 바로가기
Architecture

[Clean Code 정리] 오류 처리

by 테리는당근을좋아해 2022. 5. 29.

Clean Code - 오류 처리

 

1. 오류 코드 대신 예외를 사용하라

# 오류 코드를 사용하는 예
def analysis_wafer(self, wafer):
    if wafer.is_valid:
        self.do_something(wafer)
    else:
        self.logger.error("analysis error")

# 예외 처리를 사용하는 예
def analysis_wafer(self, wafer):
    try:
        self.do_something(wafer)
    except InvalidWaferError as error:
        self.logger.error("%s : %s", wafer.get_id(), error)

 

 

 

 

2. try-catch문 (try-except)문을 먼저 사용하라

def try_except_in_python():
    try:
        # 예외가 발생할 수 있는 코드 블럭
    except ExceptionType1 as error:
        # 예외 타입 1 발생 시 처리 블럭
    except ExceptionType2 as error:
        # 예외 타입 2 발생 시 처리 블럭
    else:
        # 예외가 발생하지 않았을 때 실행되는 블럭
    finally:
        # 예외 여부와 관계 없이 실행되는 코드 블럭

 

 

 

3. 호출자를 고려한 예외 클래스 정의

- Exception class를 만드는데 가장 중요한 것은 "어떤 방식으로 예외를 잡을까?"이다.

- 써드 파티 라이브러리를 사용하는 경우 wrapping을 통해 라이브러리 교체 등의 변경이 있는 경우에 대응하기 쉬워진다.

- 라이브러리의 API 디자인에 종속적이지 않게 설계할 수 있다.

# 오류를 형편없이 분류한 사례
class WaferPredictor():
    def get_wafer_data_from_etch(self):
        EtchEquipment eqp = EtchEquipment("eqp_id")

        try:
            eqp.get_wafer_data()
            ...
        except EquipmentConnectionError as error:
            self.__report_equipment_error(e)
            self.logger.error(error)
        except InvalidWaferError as error:
            self.__report_equipment_error(e)
            self.logger.error(error)
        except InOperationError as error:
            self.__report_equipment_error(e)
            self.logger.error(error)
        
# wrapping 클래스를 사용한 오류 분류
# wrapping 클래스
class Equipment():
    def __init__(self, eqp_id):
        self.__equipment = EtchEquipment(eqp_id)
        
    def get_wafer_data_from_etch(self):
        try:
            self.__equipment.get_wafer_data()
            ...
        except EquipmentConnectionError as error:
            raise EquipmentError
        except InvalidWaferError as error:
            raise EquipmentError
        except InOperationError as error:
            raise EquipmentError

# 클라이언트 코드
class WaferPredictor():
    def get_wafer_data_from_etch(self):
        Equipment eqp = Equipment("eqp_id")

        try:
            eqp.get_wafer_data()
            ...
        except EquipmentError as error:
            self.__report_equipment_error(e)
            self.logger.error(error)

 

 

 

4. 정상적인 상황을 정의하라

- catch 문에서 예외적인 상황을 처리해야하는 경우 코드가 지저분해질 수 있다.

- 마틴 파울러의 Special Case Pattern을 사용한다.

- 클라이언트 코드 입장에서는 예외 케이스에 대해 신경쓰지 않아도 된다.

- 예외 상황을 special case object 내에 캡슐화가 된다.

# 일반적인 예외처리
try:
    loss_reason = LossClassification.classify_loss(wafer)
    reasons.append(loss_reason)
except InvalidWaferError as error:
    loss_reason = LossClassification.classify_loss(
                            self.__tune_wafer_info(wafer))
    reasons.append(loss_reason)

# SPECIAL CASE PATTERN
try:
    loss_reason = LossClassification.classify_loss(wafer)
    reasons.append(loss_reason)

class LossClassification():
    ...
    def classify_loss(self, wafer):
        if wafer.is_invalid():
            return TunedLossClassifaction.classify_loss(wafer)
        return do_general_case_function(wafer)

class TunedLossClassification(LossClassification):
    ...
    def classify_loss(self, wafer):
        wafer = self.__tune_wafer_info(wafer)
        return super().classify_loss(wafer)

 

 

 

5. Null을 리턴하지 마라

- null을 리턴하고 싶다면 위의 Special Case Object를 리턴하라

- 써드 파티 라이브러리에서 null을 리턴할 가능성이 있는 메서드가 있다면 Exception을 던지거나 Special Case Object를 리턴하는 메서드로 래핑한다.

- null을 리턴하는 것도 나쁘지만 null을 메서드로 넘기는 것은 더 안 좋은 코드를 만들어 낸다.

- null을 메서드의 파라미터로 넣어야하는 API를 사용하는 경우가 아니면 null을 메서드로 넘기지 마라.

- 가장 이상적인 해법은 null을 파라미터로 받지 못하게 하는 것이다.

'Architecture' 카테고리의 다른 글

[Clean Code 정리] TDD  (0) 2022.05.30
[Clean Code 정리] 경계  (0) 2022.05.29
[Clean Code 정리] 객체와 자료구조  (0) 2022.05.29
[Clean Code 정리] 형식 맞추기  (0) 2022.05.29
[Clean Code 정리] 주석  (0) 2022.05.28

댓글