KR_Exception - somaz94/python-study GitHub Wiki

Python ์˜ˆ์™ธ ์ฒ˜๋ฆฌ(Exception Handling) ๊ฐœ๋… ์ •๋ฆฌ


1๏ธโƒฃ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ(Exception Handling)

์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์ค‘ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

๊ธฐ๋ณธ ๊ตฌ์กฐ

try:
    # ์‹คํ–‰ํ•  ์ฝ”๋“œ
    result = 10 / 0
except ZeroDivisionError as e:
    # ์˜ˆ์™ธ ๋ฐœ์ƒ์‹œ ์‹คํ–‰ํ•  ์ฝ”๋“œ
    print(f"์—๋Ÿฌ ๋ฐœ์ƒ: {e}")
else:
    # ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜์„ ๋•Œ ์‹คํ–‰ํ•  ์ฝ”๋“œ
    print("์„ฑ๊ณต!")
finally:
    # ์˜ˆ์™ธ ๋ฐœ์ƒ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด ์‹คํ–‰ํ•  ์ฝ”๋“œ
    print("์ข…๋ฃŒ")

try-except์˜ ๋™์ž‘ ์›๋ฆฌ

# ๊ธฐ๋ณธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
try:
    num = int(input("์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”: "))
    result = 100 / num
    print(f"๊ฒฐ๊ณผ: {result}")
except ValueError:
    print("์˜ฌ๋ฐ”๋ฅธ ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.")
except ZeroDivisionError:
    print("0์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")

์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ๋ฆ„:

  1. try ๋ธ”๋ก์˜ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.
  2. ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ•ด๋‹น ์˜ˆ์™ธ์™€ ์ผ์น˜ํ•˜๋Š” except ๋ธ”๋ก์ด ์‹คํ–‰๋œ๋‹ค.
  3. ์ผ์น˜ํ•˜๋Š” except๊ฐ€ ์—†์œผ๋ฉด ์˜ˆ์™ธ๋Š” ์ƒ์œ„ ํ˜ธ์ถœ์ž๋กœ ์ „ํŒŒ๋œ๋‹ค.
  4. ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉด else ๋ธ”๋ก์ด ์‹คํ–‰๋œ๋‹ค.
  5. ๋งˆ์ง€๋ง‰์œผ๋กœ finally ๋ธ”๋ก์ด ํ•ญ์ƒ ์‹คํ–‰๋œ๋‹ค.

์˜ˆ์™ธ ์ฒ˜๋ฆฌ์˜ ์‘์šฉ

def safe_division(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("0์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
        return None
    except TypeError:
        print("์ˆซ์ž๋งŒ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
        return None
    else:
        print("๋‚˜๋ˆ—์…ˆ ์„ฑ๊ณต!")
        return result
    finally:
        print("๋‚˜๋ˆ—์…ˆ ์—ฐ์‚ฐ ์ข…๋ฃŒ")

# ํ…Œ์ŠคํŠธ
print(safe_division(10, 2))    # ์ •์ƒ ์ผ€์ด์Šค
print(safe_division(10, 0))    # 0์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ
print(safe_division(10, "2"))  # ํƒ€์ž… ์—๋Ÿฌ


2๏ธโƒฃ ์ฃผ์š” ์˜ˆ์™ธ ์œ ํ˜•

Python์€ ๋‹ค์–‘ํ•œ ๋‚ด์žฅ ์˜ˆ์™ธ ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋ชจ๋“  ์˜ˆ์™ธ๋Š” BaseException ํด๋ž˜์Šค์—์„œ ํŒŒ์ƒ๋œ๋‹ค.

# ๊ธฐ๋ณธ์ ์ธ ๋‚ด์žฅ ์˜ˆ์™ธ๋“ค
try:
    # IndexError
    list1 = [1, 2, 3]
    print(list1[4])
except IndexError:
    print("๋ฆฌ์ŠคํŠธ ์ธ๋ฑ์Šค ๋ฒ”์œ„ ์ดˆ๊ณผ")

try:
    # KeyError
    dict1 = {"a": 1}
    print(dict1["b"])
except KeyError:
    print("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ‚ค")

try:
    # ValueError
    int("abc")
except ValueError:
    print("์ž˜๋ชป๋œ ๊ฐ’ ๋ณ€ํ™˜")

์ฃผ์š” ๋‚ด์žฅ ์˜ˆ์™ธ ํด๋ž˜์Šค

  • Exception: ๋Œ€๋ถ€๋ถ„์˜ ๋‚ด์žฅ ์˜ˆ์™ธ์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค
  • ArithmeticError: ๋ชจ๋“  ์‚ฐ์ˆ  ์—ฐ์‚ฐ ๊ด€๋ จ ์˜ค๋ฅ˜์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค
    • ZeroDivisionError: 0์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ ์‹œ๋„
    • OverflowError: ์—ฐ์‚ฐ ๊ฒฐ๊ณผ๊ฐ€ ๋„ˆ๋ฌด ํผ
  • LookupError: ๋งคํ•‘ ๋˜๋Š” ์‹œํ€€์Šค ๊ด€๋ จ ์˜ค๋ฅ˜์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค
    • IndexError: ์‹œํ€€์Šค์—์„œ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ์ธ๋ฑ์Šค
    • KeyError: ๋งคํ•‘(๋”•์…”๋„ˆ๋ฆฌ)์—์„œ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ‚ค
  • ValueError: ๊ฐ’์ด ์ ์ ˆํ•˜์ง€ ์•Š์Œ (์˜ฌ๋ฐ”๋ฅธ ํƒ€์ž…์ด์ง€๋งŒ ๋ถ€์ ์ ˆํ•œ ๊ฐ’)
  • TypeError: ๋ถ€์ ์ ˆํ•œ ํƒ€์ž…์˜ ๊ฐ์ฒด์— ์—ฐ์‚ฐ ์ ์šฉ
  • NameError: ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ณ€์ˆ˜ ์ฐธ์กฐ
  • AttributeError: ๊ฐ์ฒด์— ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์†์„ฑ ์ฐธ์กฐ
  • SyntaxError: ํŒŒ์ด์ฌ ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ (์ปดํŒŒ์ผ ์‹œ๊ฐ„ ์˜ˆ์™ธ)
  • RuntimeError: ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์ค‘ ๋ฐœ์ƒํ•œ ์ผ๋ฐ˜์ ์ธ ์˜ค๋ฅ˜
  • FileNotFoundError: ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Œ
  • PermissionError: ํŒŒ์ผ ์ ‘๊ทผ ๊ถŒํ•œ ๋ถ€์กฑ
  • ImportError/ModuleNotFoundError: ๋ชจ๋“ˆ ๊ฐ€์ ธ์˜ค๊ธฐ ์‹คํŒจ
  • MemoryError: ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ
  • RecursionError: ์ตœ๋Œ€ ์žฌ๊ท€ ๊นŠ์ด ์ดˆ๊ณผ

์˜ˆ์™ธ ๊ณ„์ธต ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ

# ์˜ˆ์™ธ ํƒ€์ž… ๊ณ„์ธต ๊ตฌ์กฐ ํ™•์ธ
def print_exception_hierarchy(exception_class, indent=0):
    print(' ' * indent + exception_class.__name__)
    for subclass in exception_class.__subclasses__():
        print_exception_hierarchy(subclass, indent + 4)

# ์ผ๋ถ€ ์˜ˆ์™ธ ๊ณ„์ธต ๊ตฌ์กฐ ์ถœ๋ ฅ
print_exception_hierarchy(BaseException)

# ์ƒ์† ๊ด€๊ณ„๋ฅผ ์ด์šฉํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
try:
    # ๋‹ค์–‘ํ•œ ์‚ฐ์ˆ  ์˜ˆ์™ธ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ์ฝ”๋“œ
    result = 1 / 0
except ArithmeticError as e:
    print(f"์ˆ˜ํ•™ ์—ฐ์‚ฐ ์˜ค๋ฅ˜: {type(e).__name__} - {e}")


3๏ธโƒฃ ์‚ฌ์šฉ์ž ์ •์˜ ์˜ˆ์™ธ

ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋‚ด์žฅ ์˜ˆ์™ธ๋งŒ์œผ๋กœ ๋ชจ๋“  ์˜ค๋ฅ˜ ์ƒํ™ฉ์„ ํ‘œํ˜„ํ•˜๊ธฐ ์–ด๋ ค์šธ ๋•Œ ์‚ฌ์šฉ์ž ์ •์˜ ์˜ˆ์™ธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

class CustomError(Exception):
    def __init__(self, message):
        self.message = message
    
    def __str__(self):
        return self.message

def validate_age(age):
    if age < 0:
        raise CustomError("๋‚˜์ด๋Š” ์Œ์ˆ˜์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค")
    return age

์‚ฌ์šฉ์ž ์ •์˜ ์˜ˆ์™ธ ํ™•์žฅ

# ๊ธฐ๋ณธ ์˜ˆ์™ธ ํด๋ž˜์Šค
class AppError(Exception):
    """์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋ณธ ์˜ˆ์™ธ"""
    def __init__(self, message="์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค", code=None):
        self.message = message
        self.code = code
        super().__init__(self.message)

# ํŠน์ • ๊ธฐ๋Šฅ๋ณ„ ์˜ˆ์™ธ
class ValidationError(AppError):
    """์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ ์˜ˆ์™ธ"""
    def __init__(self, message="์ž…๋ ฅ๊ฐ’์ด ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค", field=None):
        self.field = field
        message_with_field = f"{message} (ํ•„๋“œ: {field})" if field else message
        super().__init__(message_with_field, code="VALIDATION_ERROR")

class DatabaseError(AppError):
    """๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—… ์˜ˆ์™ธ"""
    def __init__(self, message="๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์˜ค๋ฅ˜", operation=None):
        self.operation = operation
        message_with_op = f"{message} (์ž‘์—…: {operation})" if operation else message
        super().__init__(message_with_op, code="DB_ERROR")

# ์˜ˆ์™ธ ์‚ฌ์šฉ
def register_user(username, age):
    if not username:
        raise ValidationError("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค", "username")
    
    if not isinstance(age, int) or age < 0 or age > 150:
        raise ValidationError("๋‚˜์ด๋Š” 0-150 ์‚ฌ์ด์˜ ์ •์ˆ˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค", "age")
    
    # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    if username == "admin":
        raise DatabaseError("์‚ฌ์šฉ์ž ์ €์žฅ ์‹คํŒจ", "INSERT")
    
    return {"username": username, "age": age}

# ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
try:
    user = register_user("", 25)
except ValidationError as e:
    print(f"๊ฒ€์ฆ ์˜ค๋ฅ˜: {e.message}, ์ฝ”๋“œ: {e.code}, ํ•„๋“œ: {e.field}")
except DatabaseError as e:
    print(f"DB ์˜ค๋ฅ˜: {e.message}, ์ฝ”๋“œ: {e.code}, ์ž‘์—…: {e.operation}")
except AppError as e:
    print(f"์ผ๋ฐ˜ ์˜ค๋ฅ˜: {e.message}, ์ฝ”๋“œ: {e.code}")


4๏ธโƒฃ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํŒจํ„ด

์—ฌ๋Ÿฌ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

# ์—ฌ๋Ÿฌ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
try:
    value = input("๊ฐ’์„ ์ž…๋ ฅํ•˜์„ธ์š”: ")
    num = int(value)
    result = 100 / num
    print(f"๊ฒฐ๊ณผ: {result}")
except (TypeError, ValueError) as e:
    # ์—ฌ๋Ÿฌ ์˜ˆ์™ธ๋ฅผ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌ
    print(f"ํƒ€์ž… ๋˜๋Š” ๊ฐ’ ์—๋Ÿฌ: {e}")
except ZeroDivisionError as e:
    print(f"0์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {e}")
except Exception as e:
    # ๋ชจ๋“  ์˜ˆ์™ธ ์ฒ˜๋ฆฌ (๋งˆ์ง€๋ง‰ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์ž๋กœ๋งŒ ์‚ฌ์šฉ)
    print(f"๊ธฐํƒ€ ์—๋Ÿฌ: {e}")

์˜ˆ์™ธ ํšŒํ”ผ ๋ฐ ๋‹ค์‹œ ๋ฐœ์ƒ

# ์˜ˆ์™ธ ํšŒํ”ผ (์ผ๋ถ€๋Ÿฌ ๋ฌด์‹œ)
def ignore_exception():
    try:
        # ์‹คํŒจํ•ด๋„ ๊ดœ์ฐฎ์€ ์ž‘์—…
        risky_operation()
    except Exception:
        pass  # ์˜ˆ์™ธ ๋ฌด์‹œ

# ์˜ˆ์™ธ ๋‹ค์‹œ ๋ฐœ์ƒ (์˜ˆ์™ธ ์ฒด์ธ)
def process_data(data):
    try:
        return process_value(data)
    except ValueError as e:
        # ์ถ”๊ฐ€ ์ •๋ณด์™€ ํ•จ๊ป˜ ์˜ˆ์™ธ ๋‹ค์‹œ ๋ฐœ์ƒ
        raise ValueError(f"๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์‹คํŒจ: {data}") from e

# ํ˜„์žฌ ์˜ˆ์™ธ ์ •๋ณด ์ ‘๊ทผ
import sys

def log_exception():
    try:
        1 / 0
    except:
        # ํ˜„์žฌ ์˜ˆ์™ธ ์ •๋ณด ํ™•์ธ
        exc_type, exc_value, exc_traceback = sys.exc_info()
        print(f"์˜ˆ์™ธ ์œ ํ˜•: {exc_type}")
        print(f"์˜ˆ์™ธ ๊ฐ’: {exc_value}")
        
        # ์˜ˆ์™ธ ์ถ”์  ์ •๋ณด ์ถœ๋ ฅ
        import traceback
        traceback.print_exc()  # ๊ฐ„๋‹จํ•œ ์Šคํƒ ์ถ”์  ์ •๋ณด ์ถœ๋ ฅ

assert ๋ฌธ

def calculate_average(numbers):
    # ์กฐ๊ฑด ํ™•์ธ ๋ฐ ์‹คํŒจ ์‹œ AssertionError ๋ฐœ์ƒ
    assert len(numbers) > 0, "๋ฆฌ์ŠคํŠธ๊ฐ€ ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค"
    return sum(numbers) / len(numbers)

# assert ์‚ฌ์šฉ ์˜ˆ
try:
    avg = calculate_average([])
except AssertionError as e:
    print(f"์–ด์„ค์…˜ ์˜ค๋ฅ˜: {e}")

EAFP vs LBYL ํŒจํ„ด

Python์—์„œ๋Š” ๋ณดํ†ต ๋‘ ๊ฐ€์ง€ ์ฝ”๋”ฉ ์Šคํƒ€์ผ์„ ์‚ฌ์šฉํ•œ๋‹ค:

  • EAFP (Easier to Ask for Forgiveness than Permission): ํ—ˆ๋ฝ๋ณด๋‹ค ์šฉ์„œ๊ฐ€ ์‰ฝ๋‹ค
  • LBYL (Look Before You Leap): ๋„์•ฝํ•˜๊ธฐ ์ „์— ์‚ดํŽด๋ณธ๋‹ค
# LBYL ๋ฐฉ์‹ (์‚ฌ์ „ ๊ฒ€์‚ฌ)
def lbyl_style(data, key):
    if key in data:  # ๋จผ์ € ํ™•์ธ
        return data[key]
    else:
        return None

# EAFP ๋ฐฉ์‹ (์˜ˆ์™ธ ์ฒ˜๋ฆฌ)
def eafp_style(data, key):
    try:
        return data[key]  # ์ผ๋‹จ ์‹œ๋„
    except KeyError:
        return None

# Python์€ ์ผ๋ฐ˜์ ์œผ๋กœ EAFP ์Šคํƒ€์ผ์„ ์„ ํ˜ธํ•œ๋‹ค


5๏ธโƒฃ ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ €

์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ €๋Š” with ๋ฌธ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋ฉฐ, ์ž์› ๊ด€๋ฆฌ๋ฅผ ์ž๋™ํ™”ํ•œ๋‹ค.

# with ๋ฌธ์„ ์‚ฌ์šฉํ•œ ์ž์› ๊ด€๋ฆฌ
with open('file.txt', 'r') as f:
    content = f.read()
    # ํŒŒ์ผ์ด ์ž๋™์œผ๋กœ ๋‹ซํž˜ (์˜ˆ์™ธ ๋ฐœ์ƒํ•ด๋„ ๋‹ซํž˜)

์ž์ฒด ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ € ์ž‘์„ฑ

# ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ €
class MyContextManager:
    def __init__(self, name):
        self.name = name
    
    def __enter__(self):
        print(f"{self.name} ์ปจํ…์ŠคํŠธ ์‹œ์ž‘")
        return self  # with ๋ฌธ์˜ as ๋ณ€์ˆ˜์— ํ• ๋‹น๋จ
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"{self.name} ์ปจํ…์ŠคํŠธ ์ข…๋ฃŒ")
        # ์˜ˆ์™ธ ์ •๋ณด ์ถœ๋ ฅ
        if exc_type is not None:
            print(f"์˜ˆ์™ธ ๋ฐœ์ƒ: {exc_type.__name__} - {exc_val}")
        # True ๋ฐ˜ํ™˜ ์‹œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ
        # False ๋˜๋Š” None ๋ฐ˜ํ™˜ ์‹œ ์˜ˆ์™ธ๋ฅผ ํ˜ธ์ถœ์ž์—๊ฒŒ ์ „ํŒŒ
        return False  # ์˜ˆ์™ธ ์ „ํŒŒ

# ์‚ฌ์šฉ ์˜ˆ์‹œ
try:
    with MyContextManager("ํ…Œ์ŠคํŠธ") as cm:
        print("์ž‘์—… ์ˆ˜ํ–‰ ์ค‘...")
        raise ValueError("ํ…Œ์ŠคํŠธ ์˜ˆ์™ธ")
    print("์ด ์ค„์€ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์‹คํ–‰๋˜์ง€ ์•Š์Œ")
except ValueError:
    print("์˜ˆ์™ธ๊ฐ€ ์ „ํŒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")

contextlib์„ ์‚ฌ์šฉํ•œ ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ €

from contextlib import contextmanager

@contextmanager
def my_context(name):
    print(f"{name} ์ปจํ…์ŠคํŠธ ์‹œ์ž‘")
    try:
        # yield ์ด์ „: __enter__์— ํ•ด๋‹น
        yield name  # ์ปจํ…์ŠคํŠธ ๋‚ด๋ถ€๋กœ ๊ฐ’ ์ „๋‹ฌ
        # yield ์ดํ›„: ์ •์ƒ ์ข…๋ฃŒ ์‹œ __exit__์— ํ•ด๋‹น
        print(f"{name} ์ปจํ…์ŠคํŠธ ์ •์ƒ ์ข…๋ฃŒ")
    except Exception as e:
        # ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ __exit__์— ํ•ด๋‹น
        print(f"{name} ์ปจํ…์ŠคํŠธ ์˜ˆ์™ธ ์ข…๋ฃŒ: {e}")
        # ๋‹ค์‹œ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ํ˜ธ์ถœ์ž์—๊ฒŒ ์˜ˆ์™ธ ์ „ํŒŒ
        raise

# ์‚ฌ์šฉ ์˜ˆ์‹œ
with my_context("๋ฐ์ฝ”๋ ˆ์ดํ„ฐ") as value:
    print(f"์ปจํ…์ŠคํŠธ ๋‚ด๋ถ€, ๊ฐ’: {value}")
    # ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž๊ฐ€ ์ฒ˜๋ฆฌ
    # raise ValueError("ํ…Œ์ŠคํŠธ ์˜ˆ์™ธ")

์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ € ์‘์šฉ

# ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ €
import tempfile
import shutil
import os

@contextmanager
def temporary_directory():
    temp_dir = tempfile.mkdtemp()
    print(f"์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ: {temp_dir}")
    try:
        yield temp_dir
    finally:
        print(f"์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์‚ญ์ œ: {temp_dir}")
        shutil.rmtree(temp_dir)

# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜ ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ € (๊ฐ€์ƒ)
@contextmanager
def transaction():
    print("ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘")
    try:
        yield
        print("ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹")
    except:
        print("ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ")
        raise

# ์‚ฌ์šฉ ์˜ˆ์‹œ
with temporary_directory() as temp_dir:
    # ์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ
    file_path = os.path.join(temp_dir, "test.txt")
    with open(file_path, "w") as f:
        f.write("ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ")
    print(f"ํŒŒ์ผ ์ƒ์„ฑ๋จ: {file_path}")
    # ์ปจํ…์ŠคํŠธ ์ข…๋ฃŒ ์‹œ ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ์™€ ํŒŒ์ผ ์‚ญ์ œ


6๏ธโƒฃ ํšจ๊ณผ์ ์ธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ „๋žต

์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ˆ˜์ค€ ์„ ํƒ

# ์„ธ๋ถ„ํ™”๋œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
def process_user_input():
    try:
        # ์ž‘์€ try ๋ธ”๋ก์œผ๋กœ ํŠน์ • ์ž‘์—…๋งŒ ๋ณดํ˜ธ
        age = int(input("๋‚˜์ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”: "))
    except ValueError:
        print("๋‚˜์ด๋Š” ์ˆซ์ž๋กœ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
        return None
    
    try:
        # ๋‹ค๋ฅธ ์ž‘์—…์€ ๋ณ„๋„์˜ try ๋ธ”๋ก์œผ๋กœ ๋ณดํ˜ธ
        name = input("์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”: ")
        if not name:
            raise ValueError("์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.")
    except ValueError as e:
        print(f"์ž…๋ ฅ ์˜ค๋ฅ˜: {e}")
        return None
    
    # ์„ฑ๊ณต ์‹œ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
    return {"name": name, "age": age}

# ๋Œ€๋น„ - ๋„ˆ๋ฌด ํฐ try ๋ธ”๋ก (์•ˆํ‹ฐ ํŒจํ„ด)
def process_user_input_bad():
    try:
        # ๋„ˆ๋ฌด ๋งŽ์€ ๋กœ์ง์„ ํ•˜๋‚˜์˜ try์— ํฌํ•จ
        age = int(input("๋‚˜์ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”: "))
        name = input("์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”: ")
        if not name:
            raise ValueError("์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.")
        return {"name": name, "age": age}
    except Exception as e:
        # ๋„ˆ๋ฌด ์ผ๋ฐ˜์ ์ธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
        print(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
        return None

์—๋Ÿฌ ๋กœ๊น…๊ณผ ์ฒ˜๋ฆฌ

import logging

# ๋กœ๊น… ์„ค์ •
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

def divide_numbers(a, b):
    try:
        result = a / b
        logger.info(f"๋‚˜๋ˆ—์…ˆ ์„ฑ๊ณต: {a} / {b} = {result}")
        return result
    except ZeroDivisionError:
        # ์˜ˆ์™ธ ๋กœ๊น… (์ ์ ˆํ•œ ๋ ˆ๋ฒจ ์„ ํƒ)
        logger.error(f"0์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ ์‹œ๋„: {a} / {b}", exc_info=True)
        # ์‚ฌ์šฉ์ž์—๊ฒŒ ์นœ์ˆ™ํ•œ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
        return "0์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"
    except TypeError as e:
        logger.exception(f"ํƒ€์ž… ์˜ค๋ฅ˜: {a} / {b}")  # ์ „์ฒด ์Šคํƒ ์ถ”์  ๋กœ๊น…
        return f"์ˆซ์ž๋งŒ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: {e}"

์˜ˆ์™ธ ์ฒ˜๋ฆฌ์™€ ํด๋ฆฐ์ฝ”๋“œ

# ์ง‘์ค‘ํ™”๋œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
def get_user_by_id(user_id):
    try:
        # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์•ก์„ธ์Šค ์ฝ”๋“œ
        user = database.find_user(user_id)
        return user
    except DatabaseConnectionError:
        log_error("๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์‹คํŒจ")
        raise ServiceUnavailableError("์„œ๋น„์Šค๊ฐ€ ์ผ์‹œ์ ์œผ๋กœ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค")
    except UserNotFoundError:
        return None  # ์‚ฌ์šฉ์ž๊ฐ€ ์—†์œผ๋ฉด None ๋ฐ˜ํ™˜
    except Exception as e:
        log_error(f"์‚ฌ์šฉ์ž ์กฐํšŒ ์ค‘ ์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜: {e}")
        raise InternalServerError("๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค")

# ์กฐ๊ธฐ ๋ฐ˜ํ™˜๊ณผ ๊ฐ€๋“œ ์กฐํ•ญ (Guard Clause)
def process_payment(payment):
    # ์ „์ œ ์กฐ๊ฑด ๊ฒ€์‚ฌ - ๋น ๋ฅธ ์‹คํŒจ(Fail Fast) ์ „๋žต
    if not payment:
        raise ValueError("๊ฒฐ์ œ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค")
    
    if not payment.get('amount'):
        raise ValueError("๊ฒฐ์ œ ๊ธˆ์•ก์ด ์—†์Šต๋‹ˆ๋‹ค")
    
    if payment.get('amount') <= 0:
        raise ValueError("๊ฒฐ์ œ ๊ธˆ์•ก์€ ์–‘์ˆ˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค")
    
    # ์ „์ œ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜๋ฉด ์ฃผ์š” ๋กœ์ง ์ˆ˜ํ–‰
    return process_valid_payment(payment)

์ฃผ์š” ํŒ

โœ… ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ชจ๋ฒ” ์‚ฌ๋ก€:

  • ๊ตฌ์ฒด์ ์ธ ์˜ˆ์™ธ๋ฅผ ๋จผ์ € ์ฒ˜๋ฆฌํ•˜๊ณ , ์ผ๋ฐ˜์ ์ธ ์˜ˆ์™ธ๋ฅผ ๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•œ๋‹ค
  • ์˜ˆ์™ธ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ด€๋ จ ์˜ˆ์™ธ๋ฅผ ๊ทธ๋ฃนํ™”ํ•œ๋‹ค
  • ์ตœ์†Œํ•œ์˜ ์ฝ”๋“œ๋งŒ try ๋ธ”๋ก์— ํฌํ•จ์‹œ์ผœ ์˜ค๋ฅ˜ ์›์ธ์„ ๋ช…ํ™•ํžˆ ํ•œ๋‹ค
  • ๋งค์šฐ ํฐ try-except ๋ธ”๋ก์€ ํ”ผํ•˜๊ณ  ์ ์ ˆํ•œ ํฌ๊ธฐ๋กœ ๋‚˜๋ˆˆ๋‹ค
  • ๋น„์–ด ์žˆ๋Š” except ๋ธ”๋ก์€ ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์—†๋‹ค๋ฉด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค
  • ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜ˆ์™ธ๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์‚ฌ์šฉ์ž ์ •์˜ ์˜ˆ์™ธ ๋Œ€์‹  ์‚ฌ์šฉํ•œ๋‹ค
  • ์˜๋ฏธ ์žˆ๋Š” ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ณตํ•˜๊ณ  ๊ฐ€๋Šฅํ•˜๋ฉด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•๋„ ์ œ์‹œํ•œ๋‹ค
  • ๋””๋ฒ„๊น…์— ๋„์›€๋˜๋Š” ์ •๋ณด๋ฅผ ๋กœ๊ทธ์— ๊ธฐ๋กํ•œ๋‹ค
  • ์‚ฌ์šฉ์ž ์ •์˜ ์˜ˆ์™ธ๋Š” ์˜๋ฏธ ์žˆ๋Š” ๊ณ„์ธต ๊ตฌ์กฐ๋กœ ์„ค๊ณ„ํ•œ๋‹ค
  • ์ž์› ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ํ•ญ์ƒ ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ €(with ๋ฌธ)์„ ์‚ฌ์šฉํ•œ๋‹ค


โš ๏ธ **GitHub.com Fallback** โš ๏ธ